diff --git a/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/__test__/fixtures/endpoint-recursive-types/definition/api.yml b/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/__test__/fixtures/endpoint-recursive-types/definition/api.yml new file mode 100644 index 00000000000..64e6d567fc5 --- /dev/null +++ b/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/__test__/fixtures/endpoint-recursive-types/definition/api.yml @@ -0,0 +1,3 @@ +name: endpoint-recursive +auth: bearer +default-environment: Production diff --git a/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/__test__/fixtures/endpoint-recursive-types/definition/recursive.yml b/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/__test__/fixtures/endpoint-recursive-types/definition/recursive.yml new file mode 100644 index 00000000000..848832162c3 --- /dev/null +++ b/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/__test__/fixtures/endpoint-recursive-types/definition/recursive.yml @@ -0,0 +1,39 @@ +types: + RecursiveResponse: + type: list + Node: + discriminated: false + union: + - type: Node + - type: Node + - type: Node + - type: Node + - type: Node + - type: Terminate + Terminate: + properties: + end: string + + +service: + auth: false + base-path: "" + endpoints: + recursiveGet: + method: GET + path: /recursive + request: + name: RecursiveRequest + body: + properties: + query: string + response: + type: RecursiveResponse + examples: + - name: "Example Recursion" + request: + query: "foo" + response: + body: + - end: "bar" + diff --git a/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/__test__/fixtures/endpoint-recursive-types/generators.yml b/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/__test__/fixtures/endpoint-recursive-types/generators.yml new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/__test__/fixtures/endpoint-recursive-types/generators.yml @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/__test__/valid-example-endpoint-call.test.ts b/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/__test__/valid-example-endpoint-call.test.ts index f2fb179cd70..5cf93893ed0 100644 --- a/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/__test__/valid-example-endpoint-call.test.ts +++ b/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/__test__/valid-example-endpoint-call.test.ts @@ -510,4 +510,19 @@ describe("valid-example-endpoint-call", () => { expect(violations).toEqual(expectedViolations); }); + + it("endpoint-recursive-types", async () => { + const violations = await getViolationsForRule({ + rule: ValidExampleEndpointCallRule, + absolutePathToWorkspace: join( + AbsoluteFilePath.of(__dirname), + RelativeFilePath.of("fixtures"), + RelativeFilePath.of("endpoint-recursive-types") + ) + }); + + const expectedViolations: ValidationViolation[] = []; + + expect(violations).toEqual(expectedViolations); + }); }); diff --git a/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/validateExampleEndpointCallParameters.ts b/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/validateExampleEndpointCallParameters.ts index 9fec3892e53..56f1329e74f 100644 --- a/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/validateExampleEndpointCallParameters.ts +++ b/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/validateExampleEndpointCallParameters.ts @@ -80,7 +80,8 @@ export function validateExampleEndpointCallParameters({ workspace, typeResolver, exampleResolver, - breadcrumbs + breadcrumbs, + depth: 0 }).map((val): RuleViolation => { return { severity: "error", message: val.message }; }) diff --git a/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/validateRequest.ts b/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/validateRequest.ts index 837e0930ce4..9d86096e83b 100644 --- a/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/validateRequest.ts +++ b/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/validateRequest.ts @@ -43,7 +43,8 @@ export function validateRequest({ exampleResolver, workspace, example, - breadcrumbs: ["request"] + breadcrumbs: ["request"], + depth: 0 }).map((val): RuleViolation => { return { severity: "error", message: val.message }; }) @@ -57,7 +58,8 @@ export function validateRequest({ workspace, typeResolver, exampleResolver, - breadcrumbs: ["response", "body"] + breadcrumbs: ["response", "body"], + depth: 0 }).map((val): RuleViolation => { return { severity: "error", message: val.message }; }) diff --git a/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/validateResponse.ts b/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/validateResponse.ts index 2010dfe654f..0b83891455d 100644 --- a/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/validateResponse.ts +++ b/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/validateResponse.ts @@ -76,7 +76,8 @@ function validateBodyResponse({ exampleResolver, file, workspace, - breadcrumbs: ["response", "body"] + breadcrumbs: ["response", "body"], + depth: 0 }).map((val): RuleViolation => { return { severity: "error", @@ -122,7 +123,8 @@ function validateBodyResponse({ exampleResolver, file: errorDeclaration.file, workspace, - breadcrumbs: ["response", "body"] + breadcrumbs: ["response", "body"], + depth: 0 }).map((val): RuleViolation => { return { severity: "error", message: val.message }; }) @@ -177,7 +179,8 @@ function validateStreamResponse({ exampleResolver, file, workspace, - breadcrumbs: ["response", "body"] + breadcrumbs: ["response", "body"], + depth: 0 }).map((val): RuleViolation => { return { severity: "error", message: val.message }; }) @@ -228,7 +231,8 @@ function validateSseResponse({ exampleResolver, file, workspace, - breadcrumbs: ["response", "body"] + breadcrumbs: ["response", "body"], + depth: 0 }).map((val): RuleViolation => { return { severity: "error", message: val.message }; }) diff --git a/packages/cli/fern-definition/validator/src/rules/valid-example-error/valid-example-error.ts b/packages/cli/fern-definition/validator/src/rules/valid-example-error/valid-example-error.ts index 16e827a14fa..75b10960b8c 100644 --- a/packages/cli/fern-definition/validator/src/rules/valid-example-error/valid-example-error.ts +++ b/packages/cli/fern-definition/validator/src/rules/valid-example-error/valid-example-error.ts @@ -31,7 +31,8 @@ export const ValidExampleErrorRule: Rule = { workspace, typeResolver, exampleResolver, - breadcrumbs: ["response", "body"] + breadcrumbs: ["response", "body"], + depth: 0 }); return violations.map((violation) => { return { diff --git a/packages/cli/fern-definition/validator/src/rules/valid-example-type/valid-example-type.ts b/packages/cli/fern-definition/validator/src/rules/valid-example-type/valid-example-type.ts index dd3848f7cd5..8c18d22ca4b 100644 --- a/packages/cli/fern-definition/validator/src/rules/valid-example-type/valid-example-type.ts +++ b/packages/cli/fern-definition/validator/src/rules/valid-example-type/valid-example-type.ts @@ -32,7 +32,8 @@ export const ValidExampleTypeRule: Rule = { typeResolver, exampleResolver, workspace, - breadcrumbs: [] + breadcrumbs: [], + depth: 0 }); return violations.map((violation) => { return { diff --git a/packages/cli/generation/ir-generator/src/converters/type-declarations/convertExampleType.ts b/packages/cli/generation/ir-generator/src/converters/type-declarations/convertExampleType.ts index f194b15b044..e809bc466a1 100644 --- a/packages/cli/generation/ir-generator/src/converters/type-declarations/convertExampleType.ts +++ b/packages/cli/generation/ir-generator/src/converters/type-declarations/convertExampleType.ts @@ -141,7 +141,8 @@ export function convertTypeExample({ exampleResolver, file: fileContainingType, workspace, - breadcrumbs: [] + breadcrumbs: [], + depth: 0 }); if (violationsForMember.length === 0) { return ExampleTypeShape.undiscriminatedUnion({ diff --git a/packages/cli/generation/ir-generator/src/examples/validateAliasExample.ts b/packages/cli/generation/ir-generator/src/examples/validateAliasExample.ts index 3ff829a342e..6598b29ae34 100644 --- a/packages/cli/generation/ir-generator/src/examples/validateAliasExample.ts +++ b/packages/cli/generation/ir-generator/src/examples/validateAliasExample.ts @@ -13,7 +13,8 @@ export function validateAliasExample({ typeResolver, exampleResolver, workspace, - breadcrumbs + breadcrumbs, + depth }: { rawAlias: string | RawSchemas.AliasSchema; example: RawSchemas.ExampleTypeValueSchema; @@ -22,6 +23,7 @@ export function validateAliasExample({ exampleResolver: ExampleResolver; workspace: FernWorkspace; breadcrumbs: string[]; + depth: number; }): ExampleViolation[] { return validateTypeReferenceExample({ rawTypeReference: typeof rawAlias === "string" ? rawAlias : rawAlias.type, @@ -30,6 +32,7 @@ export function validateAliasExample({ typeResolver, exampleResolver, workspace, - breadcrumbs + breadcrumbs, + depth: depth + 1 }); } diff --git a/packages/cli/generation/ir-generator/src/examples/validateObjectExample.ts b/packages/cli/generation/ir-generator/src/examples/validateObjectExample.ts index b488c05aa2f..a774ae3f7ef 100644 --- a/packages/cli/generation/ir-generator/src/examples/validateObjectExample.ts +++ b/packages/cli/generation/ir-generator/src/examples/validateObjectExample.ts @@ -19,7 +19,8 @@ export function validateObjectExample({ exampleResolver, workspace, example, - breadcrumbs + breadcrumbs, + depth }: { // undefined for inline requests typeName: string | undefined; @@ -31,6 +32,7 @@ export function validateObjectExample({ exampleResolver: ExampleResolver; workspace: FernWorkspace; breadcrumbs: string[]; + depth: number; }): ExampleViolation[] { if (!isPlainObject(example)) { return getViolationsForMisshapenExample(example, "an object"); @@ -100,7 +102,8 @@ export function validateObjectExample({ workspace, typeResolver, exampleResolver, - breadcrumbs: [...breadcrumbs, `${exampleKey}`] + breadcrumbs: [...breadcrumbs, `${exampleKey}`], + depth: depth + 1 }) ); } diff --git a/packages/cli/generation/ir-generator/src/examples/validateTypeExample.ts b/packages/cli/generation/ir-generator/src/examples/validateTypeExample.ts index d4c3099ab6e..7dd78398db4 100644 --- a/packages/cli/generation/ir-generator/src/examples/validateTypeExample.ts +++ b/packages/cli/generation/ir-generator/src/examples/validateTypeExample.ts @@ -18,7 +18,8 @@ export function validateTypeExample({ exampleResolver, example, workspace, - breadcrumbs + breadcrumbs, + depth }: { typeName: string; typeDeclaration: RawSchemas.TypeDeclarationSchema; @@ -28,6 +29,7 @@ export function validateTypeExample({ example: RawSchemas.ExampleTypeValueSchema; workspace: FernWorkspace; breadcrumbs: string[]; + depth: number; }): ExampleViolation[] { return visitRawTypeDeclaration(typeDeclaration, { alias: (rawAlias) => { @@ -38,7 +40,8 @@ export function validateTypeExample({ exampleResolver, example, workspace, - breadcrumbs + breadcrumbs, + depth }); }, enum: (rawEnum) => { @@ -58,7 +61,8 @@ export function validateTypeExample({ typeResolver, exampleResolver, workspace, - breadcrumbs + breadcrumbs, + depth }); }, discriminatedUnion: (rawUnion) => { @@ -70,7 +74,8 @@ export function validateTypeExample({ typeResolver, exampleResolver, workspace, - breadcrumbs + breadcrumbs, + depth }); }, undiscriminatedUnion: (rawUnion) => { @@ -81,7 +86,8 @@ export function validateTypeExample({ typeResolver, exampleResolver, workspace, - breadcrumbs + breadcrumbs, + depth }); } }); diff --git a/packages/cli/generation/ir-generator/src/examples/validateTypeReferenceExample.ts b/packages/cli/generation/ir-generator/src/examples/validateTypeReferenceExample.ts index 0cf5845bcff..1c164a78e41 100644 --- a/packages/cli/generation/ir-generator/src/examples/validateTypeReferenceExample.ts +++ b/packages/cli/generation/ir-generator/src/examples/validateTypeReferenceExample.ts @@ -27,6 +27,8 @@ const UUID_REGEX = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA const RFC_3339_DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/; +const MAX_RECURSION_DEPTH = 16; + export function validateTypeReferenceExample({ rawTypeReference, example, @@ -34,7 +36,8 @@ export function validateTypeReferenceExample({ exampleResolver, file, workspace, - breadcrumbs + breadcrumbs, + depth }: { rawTypeReference: string; example: RawSchemas.ExampleTypeReferenceSchema; @@ -43,7 +46,17 @@ export function validateTypeReferenceExample({ file: FernFileContext; workspace: FernWorkspace; breadcrumbs: string[]; + depth: number; }): ExampleViolation[] { + if (depth > MAX_RECURSION_DEPTH) { + // This comment never reaches the user and serves as a termination condition for the recursion. + return [ + { + message: "Example is too deeply nested. This may indicate a circular reference." + } + ]; + } + if (typeof example === "string" && example.startsWith(EXAMPLE_REFERENCE_PREFIX)) { // if it's a reference to another example, we just need to compare the // expected type with the referenced type @@ -107,7 +120,8 @@ export function validateTypeReferenceExample({ typeResolver, exampleResolver, workspace, - breadcrumbs + breadcrumbs, + depth: depth + 1 }); }, map: ({ keyType, valueType }) => { @@ -122,7 +136,8 @@ export function validateTypeReferenceExample({ exampleResolver, file, workspace, - breadcrumbs: [...breadcrumbs, exampleKey] + breadcrumbs: [...breadcrumbs, exampleKey], + depth: depth + 1 }), ...validateTypeReferenceExample({ rawTypeReference: valueType, @@ -131,7 +146,8 @@ export function validateTypeReferenceExample({ exampleResolver, file, workspace, - breadcrumbs: [...breadcrumbs, exampleKey] + breadcrumbs: [...breadcrumbs, exampleKey], + depth: depth + 1 }) ]); }, @@ -147,7 +163,8 @@ export function validateTypeReferenceExample({ exampleResolver, file, workspace, - breadcrumbs: [...breadcrumbs, `${idx}`] + breadcrumbs: [...breadcrumbs, `${idx}`], + depth: depth + 1 }) ); }, @@ -176,7 +193,8 @@ export function validateTypeReferenceExample({ exampleResolver, file, workspace, - breadcrumbs: [...breadcrumbs, `${idx}`] + breadcrumbs: [...breadcrumbs, `${idx}`], + depth: depth + 1 }) ); }, @@ -191,7 +209,8 @@ export function validateTypeReferenceExample({ exampleResolver, file, workspace, - breadcrumbs + breadcrumbs, + depth: depth + 1 }); }, unknown: () => { diff --git a/packages/cli/generation/ir-generator/src/examples/validateUndiscriminatedUnionExample.ts b/packages/cli/generation/ir-generator/src/examples/validateUndiscriminatedUnionExample.ts index 256e4b7491a..0fd37dce739 100644 --- a/packages/cli/generation/ir-generator/src/examples/validateUndiscriminatedUnionExample.ts +++ b/packages/cli/generation/ir-generator/src/examples/validateUndiscriminatedUnionExample.ts @@ -13,7 +13,8 @@ export function validateUndiscriminatedUnionExample({ exampleResolver, file, workspace, - breadcrumbs + breadcrumbs, + depth }: { rawUnion: RawSchemas.UndiscriminatedUnionSchema; example: RawSchemas.ExampleTypeValueSchema; @@ -22,6 +23,7 @@ export function validateUndiscriminatedUnionExample({ file: FernFileContext; workspace: FernWorkspace; breadcrumbs: string[]; + depth: number; }): ExampleViolation[] { const violations: ExampleViolation[] = []; for (const member of rawUnion.union) { @@ -32,7 +34,8 @@ export function validateUndiscriminatedUnionExample({ exampleResolver, file, workspace, - breadcrumbs + breadcrumbs, + depth: depth + 1 }); if (violationsForMember.length === 0) { return []; diff --git a/packages/cli/generation/ir-generator/src/examples/validateUnionExample.ts b/packages/cli/generation/ir-generator/src/examples/validateUnionExample.ts index 5fac447e2d8..279fbbae2e7 100644 --- a/packages/cli/generation/ir-generator/src/examples/validateUnionExample.ts +++ b/packages/cli/generation/ir-generator/src/examples/validateUnionExample.ts @@ -18,7 +18,8 @@ export function validateUnionExample({ exampleResolver, file, workspace, - breadcrumbs + breadcrumbs, + depth }: { typeName: string; rawUnion: RawSchemas.DiscriminatedUnionSchema; @@ -28,6 +29,7 @@ export function validateUnionExample({ file: FernFileContext; workspace: FernWorkspace; breadcrumbs: string[]; + depth: number; }): ExampleViolation[] { if (!isPlainObject(example)) { return getViolationsForMisshapenExample(example, "an object"); @@ -78,7 +80,8 @@ export function validateUnionExample({ typeResolver, exampleResolver, workspace, - breadcrumbs + breadcrumbs, + depth: depth + 1 }); if (basePropertyViolations.length > 0) { return basePropertyViolations; @@ -117,7 +120,8 @@ export function validateUnionExample({ typeResolver, exampleResolver, workspace, - breadcrumbs + breadcrumbs, + depth: depth + 1 }); } @@ -147,7 +151,8 @@ export function validateUnionExample({ exampleResolver, file, workspace, - breadcrumbs + breadcrumbs, + depth: depth + 1 }) ); } diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/.mock/generators.yml b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/.mock/generators.yml index c23323621f2..972ed6d7b73 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/.mock/generators.yml +++ b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/.mock/generators.yml @@ -1,7 +1,6 @@ api: - - path: openapi/openapi.yml - - proto: - root: proto - target: proto/data/v1/data.proto - overrides: overrides.yml - local-generation: true + - proto: + root: proto + target: proto/data/v1/data.proto + overrides: overrides.yml + local-generation: true \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/.mock/openapi/openapi.yml b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/.mock/openapi/openapi.yml deleted file mode 100644 index ebc23143df3..00000000000 --- a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/.mock/openapi/openapi.yml +++ /dev/null @@ -1,33 +0,0 @@ -openapi: 3.0.3 -info: - title: Test API - version: 1.0.0 -servers: - - url: https://localhost -tags: - - name: dataservice -paths: - /foo: - post: - tag: dataservice - x-fern-sdk-group-name: - - dataservice - x-fern-sdk-method-name: foo - security: - - ApiKeyAuth: [] - operationId: foo - responses: - "200": - content: - application/json: - schema: - type: object - -security: - - ApiKeyAuth: [] -components: - securitySchemes: - ApiKeyAuth: - type: apiKey - in: header - name: X-API-Key diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/README.md b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/README.md index fb7a6d7b883..a605ad810d0 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/README.md +++ b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/README.md @@ -18,8 +18,20 @@ Instantiate and use the client with the following: ```csharp using SeedApi; -var client = new SeedApiClient("API_KEY"); -await client.Dataservice.FooAsync(); +var client = new SeedApiClient(); +await client.Dataservice.UploadAsync( + new UploadRequest + { + Columns = new List() + { + new Column + { + Id = "id", + Values = new List() { 1.1f }, + }, + }, + } +); ``` ## Exception Handling @@ -31,7 +43,7 @@ will be thrown. using SeedApi; try { - var response = await client.Dataservice.FooAsync(...); + var response = await client.Dataservice.UploadAsync(...); } catch (SeedApiApiException e) { System.Console.WriteLine(e.Body); System.Console.WriteLine(e.StatusCode); @@ -55,7 +67,7 @@ A request is deemed retriable when any of the following HTTP status codes is ret Use the `MaxRetries` request option to configure this behavior. ```csharp -var response = await client.Dataservice.FooAsync( +var response = await client.Dataservice.UploadAsync( ..., new RequestOptions { MaxRetries: 0 // Override MaxRetries at the request level @@ -68,7 +80,7 @@ var response = await client.Dataservice.FooAsync( The SDK defaults to a 30 second timeout. Use the `Timeout` option to configure this behavior. ```csharp -var response = await client.Dataservice.FooAsync( +var response = await client.Dataservice.UploadAsync( ..., new RequestOptions { Timeout: TimeSpan.FromSeconds(3) // Override timeout to 3s diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/reference.md b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/reference.md index b5e56eaf804..830bacdc726 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/reference.md +++ b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/reference.md @@ -1,30 +1,5 @@ # Reference ## DataService -
client.Dataservice.FooAsync() -> object -
-
- -#### 🔌 Usage - -
-
- -
-
- -```csharp -await client.Dataservice.FooAsync(); -``` -
-
-
-
- - -
-
-
-
client.Dataservice.UploadAsync(UploadRequest { ... }) -> UploadResponse
diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/snippet.json b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/snippet.json index 9a76a063ec4..546b49b57ee 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/snippet.json +++ b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/snippet.json @@ -1,18 +1,6 @@ { "types": {}, "endpoints": [ - { - "example_identifier": null, - "id": { - "path": "/foo", - "method": "POST", - "identifier_override": "endpoint_dataservice.foo" - }, - "snippet": { - "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.FooAsync();\n" - } - }, { "example_identifier": null, "id": { @@ -22,7 +10,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.UploadAsync(\n new UploadRequest\n {\n Columns = new List()\n {\n new Column\n {\n Id = \"id\",\n Values = new List() { 1.1f },\n },\n },\n }\n);\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.UploadAsync(\n new UploadRequest\n {\n Columns = new List()\n {\n new Column\n {\n Id = \"id\",\n Values = new List() { 1.1f },\n },\n },\n }\n);\n" } }, { @@ -34,7 +22,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.DeleteAsync(new DeleteRequest());\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.DeleteAsync(new DeleteRequest());\n" } }, { @@ -46,7 +34,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.DescribeAsync(new DescribeRequest());\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.DescribeAsync(new DescribeRequest());\n" } }, { @@ -58,7 +46,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.FetchAsync(new FetchRequest());\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.FetchAsync(new FetchRequest());\n" } }, { @@ -70,7 +58,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.ListAsync(new ListRequest());\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.ListAsync(new ListRequest());\n" } }, { @@ -82,7 +70,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.QueryAsync(new QueryRequest { TopK = 1 });\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.QueryAsync(new QueryRequest { TopK = 1 });\n" } }, { @@ -94,7 +82,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.UpdateAsync(new UpdateRequest { Id = \"id\" });\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.UpdateAsync(new UpdateRequest { Id = \"id\" });\n" } } ] diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi.Test/SeedApi.Test.Custom.props b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi.Test/SeedApi.Test.Custom.props new file mode 100644 index 00000000000..55e683b0772 --- /dev/null +++ b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi.Test/SeedApi.Test.Custom.props @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi.Test/SeedApi.Test.csproj b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi.Test/SeedApi.Test.csproj index c5be29f92d9..fd7b07f82e5 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi.Test/SeedApi.Test.csproj +++ b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi.Test/SeedApi.Test.csproj @@ -20,7 +20,8 @@ - + + \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/IRequestOptions.cs b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/IRequestOptions.cs new file mode 100644 index 00000000000..e365cd162f0 --- /dev/null +++ b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/IRequestOptions.cs @@ -0,0 +1,34 @@ +using System; +using System.Net.Http; + +#nullable enable + +namespace SeedApi.Core; + +internal interface IRequestOptions +{ + /// + /// The Base URL for the API. + /// + public string? BaseUrl { get; init; } + + /// + /// The http client used to make requests. + /// + public HttpClient? HttpClient { get; init; } + + /// + /// The http headers sent with the request. + /// + internal Headers Headers { get; init; } + + /// + /// The http client used to make requests. + /// + public int? MaxRetries { get; init; } + + /// + /// The timeout for the request. + /// + public TimeSpan? Timeout { get; init; } +} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/Public/ClientOptions.cs b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/Public/ClientOptions.cs index b3264cb28bb..5c12019c489 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/Public/ClientOptions.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/Public/ClientOptions.cs @@ -12,7 +12,7 @@ public partial class ClientOptions /// /// The Base URL for the API. /// - public string BaseUrl { get; init; } = SeedApiEnvironment.Default; + public string BaseUrl { get; init; } = ""; /// /// The http client used to make requests. @@ -52,6 +52,5 @@ internal ClientOptions Clone() Timeout = Timeout, Headers = new Headers(new Dictionary(Headers)), }; - ; } } diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/Public/RequestOptions.cs b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/Public/RequestOptions.cs index 6e0923d6f60..4d560f8337a 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/Public/RequestOptions.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/Public/RequestOptions.cs @@ -6,7 +6,7 @@ namespace SeedApi; -public partial class RequestOptions +public partial class RequestOptions : IRequestOptions { /// /// The Base URL for the API. @@ -31,5 +31,5 @@ public partial class RequestOptions /// /// The http headers sent with the request. /// - internal Headers Headers { get; init; } = new(); + Headers IRequestOptions.Headers { get; init; } = new(); } diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/Public/SeedApiEnvironment.cs b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/Public/SeedApiEnvironment.cs deleted file mode 100644 index 14ffe5ab6c8..00000000000 --- a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/Public/SeedApiEnvironment.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace SeedApi; - -public class SeedApiEnvironment -{ - public static string Default = "https://localhost"; -} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/RawClient.cs b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/RawClient.cs index 0cc389e831a..8cff8b09a01 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/RawClient.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Core/RawClient.cs @@ -1,4 +1,5 @@ using System.Net.Http; +using System.Net.Http.Headers; using System.Text; using System.Threading; @@ -54,7 +55,7 @@ public record BaseApiRequest public Headers Headers { get; init; } = new(); - public RequestOptions? Options { get; init; } + public IRequestOptions? Options { get; init; } } /// @@ -135,11 +136,14 @@ private HttpRequestMessage BuildHttpRequest(BaseApiRequest request) } if (request.ContentType != null) { - request.Headers.Add("Content-Type", request.ContentType); + httpRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse( + request.ContentType + ); } SetHeaders(httpRequest, Options.Headers); SetHeaders(httpRequest, request.Headers); SetHeaders(httpRequest, request.Options?.Headers ?? new Headers()); + return httpRequest; } diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Dataservice/DataserviceClient.cs b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Dataservice/DataserviceClient.cs index 9bd2a610bf3..8da06ffed93 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Dataservice/DataserviceClient.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/Dataservice/DataserviceClient.cs @@ -1,5 +1,3 @@ -using System.Net.Http; -using System.Text.Json; using System.Threading; using Data.V1.Grpc; using Grpc.Core; @@ -24,46 +22,6 @@ internal DataserviceClient(RawClient client) _dataService = new DataService.DataServiceClient(_grpc.Channel); } - /// - /// - /// await client.Dataservice.FooAsync(); - /// - /// - public async Task FooAsync( - RequestOptions? options = null, - CancellationToken cancellationToken = default - ) - { - var response = await _client.MakeRequestAsync( - new RawClient.JsonApiRequest - { - BaseUrl = _client.Options.BaseUrl, - Method = HttpMethod.Post, - Path = "foo", - Options = options, - }, - cancellationToken - ); - var responseBody = await response.Raw.Content.ReadAsStringAsync(); - if (response.StatusCode is >= 200 and < 400) - { - try - { - return JsonUtils.Deserialize(responseBody)!; - } - catch (JsonException e) - { - throw new SeedApiException("Failed to deserialize response", e); - } - } - - throw new SeedApiApiException( - $"Error with status code {response.StatusCode}", - response.StatusCode, - responseBody - ); - } - /// /// /// await client.Dataservice.UploadAsync( diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/SeedApi.Custom.props b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/SeedApi.Custom.props new file mode 100644 index 00000000000..70df2849401 --- /dev/null +++ b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/SeedApi.Custom.props @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/SeedApi.csproj b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/SeedApi.csproj index e875ec8f800..9b70c3847bf 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/SeedApi.csproj +++ b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/SeedApi.csproj @@ -8,6 +8,8 @@ 12 enable 0.0.1 + $(Version) + $(Version) README.md https://github.com/grpc-proto-exhaustive/fern @@ -34,7 +36,7 @@ - + @@ -68,4 +70,5 @@ + diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/SeedApiClient.cs b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/SeedApiClient.cs index a079df6c315..017ab8726ca 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/SeedApiClient.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/no-custom-config/src/SeedApi/SeedApiClient.cs @@ -8,7 +8,7 @@ public partial class SeedApiClient { private RawClient _client; - public SeedApiClient(string apiKey, ClientOptions? clientOptions = null) + public SeedApiClient(ClientOptions? clientOptions = null) { var defaultHeaders = new Headers( new Dictionary() diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/.mock/generators.yml b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/.mock/generators.yml index c23323621f2..972ed6d7b73 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/.mock/generators.yml +++ b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/.mock/generators.yml @@ -1,7 +1,6 @@ api: - - path: openapi/openapi.yml - - proto: - root: proto - target: proto/data/v1/data.proto - overrides: overrides.yml - local-generation: true + - proto: + root: proto + target: proto/data/v1/data.proto + overrides: overrides.yml + local-generation: true \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/.mock/openapi/openapi.yml b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/.mock/openapi/openapi.yml deleted file mode 100644 index ebc23143df3..00000000000 --- a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/.mock/openapi/openapi.yml +++ /dev/null @@ -1,33 +0,0 @@ -openapi: 3.0.3 -info: - title: Test API - version: 1.0.0 -servers: - - url: https://localhost -tags: - - name: dataservice -paths: - /foo: - post: - tag: dataservice - x-fern-sdk-group-name: - - dataservice - x-fern-sdk-method-name: foo - security: - - ApiKeyAuth: [] - operationId: foo - responses: - "200": - content: - application/json: - schema: - type: object - -security: - - ApiKeyAuth: [] -components: - securitySchemes: - ApiKeyAuth: - type: apiKey - in: header - name: X-API-Key diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/README.md b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/README.md index a9db3110199..605573c3e5f 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/README.md +++ b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/README.md @@ -18,8 +18,20 @@ Instantiate and use the client with the following: ```csharp using SeedApi; -var client = new SeedApiClient("API_KEY"); -await client.Dataservice.FooAsync(); +var client = new SeedApiClient(); +await client.Dataservice.UploadAsync( + new UploadRequest + { + Columns = new List() + { + new Column + { + Id = "id", + Values = new List() { 1.1f }, + }, + }, + } +); ``` ## Exception Handling @@ -31,7 +43,7 @@ will be thrown. using SeedApi; try { - var response = await client.Dataservice.FooAsync(...); + var response = await client.Dataservice.UploadAsync(...); } catch (SeedApiApiException e) { System.Console.WriteLine(e.Body); System.Console.WriteLine(e.StatusCode); @@ -55,7 +67,7 @@ A request is deemed retriable when any of the following HTTP status codes is ret Use the `MaxRetries` request option to configure this behavior. ```csharp -var response = await client.Dataservice.FooAsync( +var response = await client.Dataservice.UploadAsync( ..., new RequestOptions { MaxRetries: 0 // Override MaxRetries at the request level @@ -68,7 +80,7 @@ var response = await client.Dataservice.FooAsync( The SDK defaults to a 30 second timeout. Use the `Timeout` option to configure this behavior. ```csharp -var response = await client.Dataservice.FooAsync( +var response = await client.Dataservice.UploadAsync( ..., new RequestOptions { Timeout: TimeSpan.FromSeconds(3) // Override timeout to 3s diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/reference.md b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/reference.md index b5e56eaf804..830bacdc726 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/reference.md +++ b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/reference.md @@ -1,30 +1,5 @@ # Reference ## DataService -
client.Dataservice.FooAsync() -> object -
-
- -#### 🔌 Usage - -
-
- -
-
- -```csharp -await client.Dataservice.FooAsync(); -``` -
-
-
-
- - -
-
-
-
client.Dataservice.UploadAsync(UploadRequest { ... }) -> UploadResponse
diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/snippet.json b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/snippet.json index 9a76a063ec4..546b49b57ee 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/snippet.json +++ b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/snippet.json @@ -1,18 +1,6 @@ { "types": {}, "endpoints": [ - { - "example_identifier": null, - "id": { - "path": "/foo", - "method": "POST", - "identifier_override": "endpoint_dataservice.foo" - }, - "snippet": { - "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.FooAsync();\n" - } - }, { "example_identifier": null, "id": { @@ -22,7 +10,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.UploadAsync(\n new UploadRequest\n {\n Columns = new List()\n {\n new Column\n {\n Id = \"id\",\n Values = new List() { 1.1f },\n },\n },\n }\n);\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.UploadAsync(\n new UploadRequest\n {\n Columns = new List()\n {\n new Column\n {\n Id = \"id\",\n Values = new List() { 1.1f },\n },\n },\n }\n);\n" } }, { @@ -34,7 +22,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.DeleteAsync(new DeleteRequest());\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.DeleteAsync(new DeleteRequest());\n" } }, { @@ -46,7 +34,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.DescribeAsync(new DescribeRequest());\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.DescribeAsync(new DescribeRequest());\n" } }, { @@ -58,7 +46,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.FetchAsync(new FetchRequest());\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.FetchAsync(new FetchRequest());\n" } }, { @@ -70,7 +58,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.ListAsync(new ListRequest());\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.ListAsync(new ListRequest());\n" } }, { @@ -82,7 +70,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.QueryAsync(new QueryRequest { TopK = 1 });\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.QueryAsync(new QueryRequest { TopK = 1 });\n" } }, { @@ -94,7 +82,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.UpdateAsync(new UpdateRequest { Id = \"id\" });\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.UpdateAsync(new UpdateRequest { Id = \"id\" });\n" } } ] diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi.Test/SeedApi.Test.Custom.props b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi.Test/SeedApi.Test.Custom.props new file mode 100644 index 00000000000..55e683b0772 --- /dev/null +++ b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi.Test/SeedApi.Test.Custom.props @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi.Test/SeedApi.Test.csproj b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi.Test/SeedApi.Test.csproj index c5be29f92d9..fd7b07f82e5 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi.Test/SeedApi.Test.csproj +++ b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi.Test/SeedApi.Test.csproj @@ -20,7 +20,8 @@ - + + \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/IRequestOptions.cs b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/IRequestOptions.cs new file mode 100644 index 00000000000..e365cd162f0 --- /dev/null +++ b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/IRequestOptions.cs @@ -0,0 +1,34 @@ +using System; +using System.Net.Http; + +#nullable enable + +namespace SeedApi.Core; + +internal interface IRequestOptions +{ + /// + /// The Base URL for the API. + /// + public string? BaseUrl { get; init; } + + /// + /// The http client used to make requests. + /// + public HttpClient? HttpClient { get; init; } + + /// + /// The http headers sent with the request. + /// + internal Headers Headers { get; init; } + + /// + /// The http client used to make requests. + /// + public int? MaxRetries { get; init; } + + /// + /// The timeout for the request. + /// + public TimeSpan? Timeout { get; init; } +} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/Public/ClientOptions.cs b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/Public/ClientOptions.cs index b3264cb28bb..5c12019c489 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/Public/ClientOptions.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/Public/ClientOptions.cs @@ -12,7 +12,7 @@ public partial class ClientOptions /// /// The Base URL for the API. /// - public string BaseUrl { get; init; } = SeedApiEnvironment.Default; + public string BaseUrl { get; init; } = ""; /// /// The http client used to make requests. @@ -52,6 +52,5 @@ internal ClientOptions Clone() Timeout = Timeout, Headers = new Headers(new Dictionary(Headers)), }; - ; } } diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/Public/RequestOptions.cs b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/Public/RequestOptions.cs index 6e0923d6f60..4d560f8337a 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/Public/RequestOptions.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/Public/RequestOptions.cs @@ -6,7 +6,7 @@ namespace SeedApi; -public partial class RequestOptions +public partial class RequestOptions : IRequestOptions { /// /// The Base URL for the API. @@ -31,5 +31,5 @@ public partial class RequestOptions /// /// The http headers sent with the request. /// - internal Headers Headers { get; init; } = new(); + Headers IRequestOptions.Headers { get; init; } = new(); } diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/Public/SeedApiEnvironment.cs b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/Public/SeedApiEnvironment.cs deleted file mode 100644 index 14ffe5ab6c8..00000000000 --- a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/Public/SeedApiEnvironment.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace SeedApi; - -public class SeedApiEnvironment -{ - public static string Default = "https://localhost"; -} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/RawClient.cs b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/RawClient.cs index 0cc389e831a..8cff8b09a01 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/RawClient.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Core/RawClient.cs @@ -1,4 +1,5 @@ using System.Net.Http; +using System.Net.Http.Headers; using System.Text; using System.Threading; @@ -54,7 +55,7 @@ public record BaseApiRequest public Headers Headers { get; init; } = new(); - public RequestOptions? Options { get; init; } + public IRequestOptions? Options { get; init; } } /// @@ -135,11 +136,14 @@ private HttpRequestMessage BuildHttpRequest(BaseApiRequest request) } if (request.ContentType != null) { - request.Headers.Add("Content-Type", request.ContentType); + httpRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse( + request.ContentType + ); } SetHeaders(httpRequest, Options.Headers); SetHeaders(httpRequest, request.Headers); SetHeaders(httpRequest, request.Options?.Headers ?? new Headers()); + return httpRequest; } diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Dataservice/DataserviceClient.cs b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Dataservice/DataserviceClient.cs index 9bd2a610bf3..8da06ffed93 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Dataservice/DataserviceClient.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/Dataservice/DataserviceClient.cs @@ -1,5 +1,3 @@ -using System.Net.Http; -using System.Text.Json; using System.Threading; using Data.V1.Grpc; using Grpc.Core; @@ -24,46 +22,6 @@ internal DataserviceClient(RawClient client) _dataService = new DataService.DataServiceClient(_grpc.Channel); } - /// - /// - /// await client.Dataservice.FooAsync(); - /// - /// - public async Task FooAsync( - RequestOptions? options = null, - CancellationToken cancellationToken = default - ) - { - var response = await _client.MakeRequestAsync( - new RawClient.JsonApiRequest - { - BaseUrl = _client.Options.BaseUrl, - Method = HttpMethod.Post, - Path = "foo", - Options = options, - }, - cancellationToken - ); - var responseBody = await response.Raw.Content.ReadAsStringAsync(); - if (response.StatusCode is >= 200 and < 400) - { - try - { - return JsonUtils.Deserialize(responseBody)!; - } - catch (JsonException e) - { - throw new SeedApiException("Failed to deserialize response", e); - } - } - - throw new SeedApiApiException( - $"Error with status code {response.StatusCode}", - response.StatusCode, - responseBody - ); - } - /// /// /// await client.Dataservice.UploadAsync( diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/SeedApi.Custom.props b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/SeedApi.Custom.props new file mode 100644 index 00000000000..70df2849401 --- /dev/null +++ b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/SeedApi.Custom.props @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/SeedApi.csproj b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/SeedApi.csproj index e4a170039e8..1ed464ff911 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/SeedApi.csproj +++ b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/SeedApi.csproj @@ -9,6 +9,8 @@ 12 enable 0.0.1 + $(Version) + $(Version) README.md https://github.com/grpc-proto-exhaustive/fern @@ -35,7 +37,7 @@ - + @@ -69,4 +71,5 @@ + diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/SeedApiClient.cs b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/SeedApiClient.cs index a079df6c315..017ab8726ca 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/SeedApiClient.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/package-id/src/SeedApi/SeedApiClient.cs @@ -8,7 +8,7 @@ public partial class SeedApiClient { private RawClient _client; - public SeedApiClient(string apiKey, ClientOptions? clientOptions = null) + public SeedApiClient(ClientOptions? clientOptions = null) { var defaultHeaders = new Headers( new Dictionary() diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/.mock/generators.yml b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/.mock/generators.yml index c23323621f2..972ed6d7b73 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/.mock/generators.yml +++ b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/.mock/generators.yml @@ -1,7 +1,6 @@ api: - - path: openapi/openapi.yml - - proto: - root: proto - target: proto/data/v1/data.proto - overrides: overrides.yml - local-generation: true + - proto: + root: proto + target: proto/data/v1/data.proto + overrides: overrides.yml + local-generation: true \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/.mock/openapi/openapi.yml b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/.mock/openapi/openapi.yml deleted file mode 100644 index ebc23143df3..00000000000 --- a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/.mock/openapi/openapi.yml +++ /dev/null @@ -1,33 +0,0 @@ -openapi: 3.0.3 -info: - title: Test API - version: 1.0.0 -servers: - - url: https://localhost -tags: - - name: dataservice -paths: - /foo: - post: - tag: dataservice - x-fern-sdk-group-name: - - dataservice - x-fern-sdk-method-name: foo - security: - - ApiKeyAuth: [] - operationId: foo - responses: - "200": - content: - application/json: - schema: - type: object - -security: - - ApiKeyAuth: [] -components: - securitySchemes: - ApiKeyAuth: - type: apiKey - in: header - name: X-API-Key diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/README.md b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/README.md index fb7a6d7b883..d7cbceedb96 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/README.md +++ b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/README.md @@ -18,8 +18,16 @@ Instantiate and use the client with the following: ```csharp using SeedApi; -var client = new SeedApiClient("API_KEY"); -await client.Dataservice.FooAsync(); +var client = new SeedApiClient(); +await client.Dataservice.UploadAsync( + new UploadRequest + { + Columns = new List() + { + new Column { Id = "id", Values = new[] { 1.1f } }, + }, + } +); ``` ## Exception Handling @@ -31,7 +39,7 @@ will be thrown. using SeedApi; try { - var response = await client.Dataservice.FooAsync(...); + var response = await client.Dataservice.UploadAsync(...); } catch (SeedApiApiException e) { System.Console.WriteLine(e.Body); System.Console.WriteLine(e.StatusCode); @@ -55,7 +63,7 @@ A request is deemed retriable when any of the following HTTP status codes is ret Use the `MaxRetries` request option to configure this behavior. ```csharp -var response = await client.Dataservice.FooAsync( +var response = await client.Dataservice.UploadAsync( ..., new RequestOptions { MaxRetries: 0 // Override MaxRetries at the request level @@ -68,7 +76,7 @@ var response = await client.Dataservice.FooAsync( The SDK defaults to a 30 second timeout. Use the `Timeout` option to configure this behavior. ```csharp -var response = await client.Dataservice.FooAsync( +var response = await client.Dataservice.UploadAsync( ..., new RequestOptions { Timeout: TimeSpan.FromSeconds(3) // Override timeout to 3s diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/reference.md b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/reference.md index e862902bd0f..b5cdd6e8580 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/reference.md +++ b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/reference.md @@ -1,30 +1,5 @@ # Reference ## DataService -
client.Dataservice.FooAsync() -> object -
-
- -#### 🔌 Usage - -
-
- -
-
- -```csharp -await client.Dataservice.FooAsync(); -``` -
-
-
-
- - -
-
-
-
client.Dataservice.UploadAsync(UploadRequest { ... }) -> UploadResponse
diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/snippet.json b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/snippet.json index 062bf9a5e73..66926a40e09 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/snippet.json +++ b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/snippet.json @@ -1,18 +1,6 @@ { "types": {}, "endpoints": [ - { - "example_identifier": null, - "id": { - "path": "/foo", - "method": "POST", - "identifier_override": "endpoint_dataservice.foo" - }, - "snippet": { - "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.FooAsync();\n" - } - }, { "example_identifier": null, "id": { @@ -22,7 +10,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.UploadAsync(\n new UploadRequest\n {\n Columns = new List()\n {\n new Column { Id = \"id\", Values = new[] { 1.1f } },\n },\n }\n);\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.UploadAsync(\n new UploadRequest\n {\n Columns = new List()\n {\n new Column { Id = \"id\", Values = new[] { 1.1f } },\n },\n }\n);\n" } }, { @@ -34,7 +22,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.DeleteAsync(new DeleteRequest());\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.DeleteAsync(new DeleteRequest());\n" } }, { @@ -46,7 +34,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.DescribeAsync(new DescribeRequest());\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.DescribeAsync(new DescribeRequest());\n" } }, { @@ -58,7 +46,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.FetchAsync(new FetchRequest());\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.FetchAsync(new FetchRequest());\n" } }, { @@ -70,7 +58,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.ListAsync(new ListRequest());\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.ListAsync(new ListRequest());\n" } }, { @@ -82,7 +70,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.QueryAsync(new QueryRequest { TopK = 1 });\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.QueryAsync(new QueryRequest { TopK = 1 });\n" } }, { @@ -94,7 +82,7 @@ }, "snippet": { "type": "typescript", - "client": "using SeedApi;\n\nvar client = new SeedApiClient(\"API_KEY\");\nawait client.Dataservice.UpdateAsync(new UpdateRequest { Id = \"id\" });\n" + "client": "using SeedApi;\n\nvar client = new SeedApiClient();\nawait client.Dataservice.UpdateAsync(new UpdateRequest { Id = \"id\" });\n" } } ] diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi.Test/SeedApi.Test.Custom.props b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi.Test/SeedApi.Test.Custom.props new file mode 100644 index 00000000000..55e683b0772 --- /dev/null +++ b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi.Test/SeedApi.Test.Custom.props @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi.Test/SeedApi.Test.csproj b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi.Test/SeedApi.Test.csproj index c5be29f92d9..fd7b07f82e5 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi.Test/SeedApi.Test.csproj +++ b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi.Test/SeedApi.Test.csproj @@ -20,7 +20,8 @@ - + + \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/IRequestOptions.cs b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/IRequestOptions.cs new file mode 100644 index 00000000000..e365cd162f0 --- /dev/null +++ b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/IRequestOptions.cs @@ -0,0 +1,34 @@ +using System; +using System.Net.Http; + +#nullable enable + +namespace SeedApi.Core; + +internal interface IRequestOptions +{ + /// + /// The Base URL for the API. + /// + public string? BaseUrl { get; init; } + + /// + /// The http client used to make requests. + /// + public HttpClient? HttpClient { get; init; } + + /// + /// The http headers sent with the request. + /// + internal Headers Headers { get; init; } + + /// + /// The http client used to make requests. + /// + public int? MaxRetries { get; init; } + + /// + /// The timeout for the request. + /// + public TimeSpan? Timeout { get; init; } +} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/Public/ClientOptions.cs b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/Public/ClientOptions.cs index b3264cb28bb..5c12019c489 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/Public/ClientOptions.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/Public/ClientOptions.cs @@ -12,7 +12,7 @@ public partial class ClientOptions /// /// The Base URL for the API. /// - public string BaseUrl { get; init; } = SeedApiEnvironment.Default; + public string BaseUrl { get; init; } = ""; /// /// The http client used to make requests. @@ -52,6 +52,5 @@ internal ClientOptions Clone() Timeout = Timeout, Headers = new Headers(new Dictionary(Headers)), }; - ; } } diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/Public/RequestOptions.cs b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/Public/RequestOptions.cs index 6e0923d6f60..4d560f8337a 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/Public/RequestOptions.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/Public/RequestOptions.cs @@ -6,7 +6,7 @@ namespace SeedApi; -public partial class RequestOptions +public partial class RequestOptions : IRequestOptions { /// /// The Base URL for the API. @@ -31,5 +31,5 @@ public partial class RequestOptions /// /// The http headers sent with the request. /// - internal Headers Headers { get; init; } = new(); + Headers IRequestOptions.Headers { get; init; } = new(); } diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/Public/SeedApiEnvironment.cs b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/Public/SeedApiEnvironment.cs deleted file mode 100644 index 14ffe5ab6c8..00000000000 --- a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/Public/SeedApiEnvironment.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace SeedApi; - -public class SeedApiEnvironment -{ - public static string Default = "https://localhost"; -} diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/RawClient.cs b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/RawClient.cs index 0cc389e831a..8cff8b09a01 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/RawClient.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Core/RawClient.cs @@ -1,4 +1,5 @@ using System.Net.Http; +using System.Net.Http.Headers; using System.Text; using System.Threading; @@ -54,7 +55,7 @@ public record BaseApiRequest public Headers Headers { get; init; } = new(); - public RequestOptions? Options { get; init; } + public IRequestOptions? Options { get; init; } } /// @@ -135,11 +136,14 @@ private HttpRequestMessage BuildHttpRequest(BaseApiRequest request) } if (request.ContentType != null) { - request.Headers.Add("Content-Type", request.ContentType); + httpRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse( + request.ContentType + ); } SetHeaders(httpRequest, Options.Headers); SetHeaders(httpRequest, request.Headers); SetHeaders(httpRequest, request.Options?.Headers ?? new Headers()); + return httpRequest; } diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Dataservice/DataserviceClient.cs b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Dataservice/DataserviceClient.cs index 9aa28678958..9c85dcc2d0e 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Dataservice/DataserviceClient.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/Dataservice/DataserviceClient.cs @@ -1,5 +1,3 @@ -using System.Net.Http; -using System.Text.Json; using System.Threading; using Data.V1.Grpc; using Grpc.Core; @@ -24,46 +22,6 @@ internal DataserviceClient(RawClient client) _dataService = new DataService.DataServiceClient(_grpc.Channel); } - /// - /// - /// await client.Dataservice.FooAsync(); - /// - /// - public async Task FooAsync( - RequestOptions? options = null, - CancellationToken cancellationToken = default - ) - { - var response = await _client.MakeRequestAsync( - new RawClient.JsonApiRequest - { - BaseUrl = _client.Options.BaseUrl, - Method = HttpMethod.Post, - Path = "foo", - Options = options, - }, - cancellationToken - ); - var responseBody = await response.Raw.Content.ReadAsStringAsync(); - if (response.StatusCode is >= 200 and < 400) - { - try - { - return JsonUtils.Deserialize(responseBody)!; - } - catch (JsonException e) - { - throw new SeedApiException("Failed to deserialize response", e); - } - } - - throw new SeedApiApiException( - $"Error with status code {response.StatusCode}", - response.StatusCode, - responseBody - ); - } - /// /// /// await client.Dataservice.UploadAsync( diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/SeedApi.Custom.props b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/SeedApi.Custom.props new file mode 100644 index 00000000000..70df2849401 --- /dev/null +++ b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/SeedApi.Custom.props @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/SeedApi.csproj b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/SeedApi.csproj index e875ec8f800..9b70c3847bf 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/SeedApi.csproj +++ b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/SeedApi.csproj @@ -8,6 +8,8 @@ 12 enable 0.0.1 + $(Version) + $(Version) README.md https://github.com/grpc-proto-exhaustive/fern @@ -34,7 +36,7 @@ - + @@ -68,4 +70,5 @@ + diff --git a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/SeedApiClient.cs b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/SeedApiClient.cs index a079df6c315..017ab8726ca 100644 --- a/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/SeedApiClient.cs +++ b/seed/csharp-sdk/grpc-proto-exhaustive/read-only-memory/src/SeedApi/SeedApiClient.cs @@ -8,7 +8,7 @@ public partial class SeedApiClient { private RawClient _client; - public SeedApiClient(string apiKey, ClientOptions? clientOptions = null) + public SeedApiClient(ClientOptions? clientOptions = null) { var defaultHeaders = new Headers( new Dictionary() diff --git a/seed/csharp-sdk/grpc-proto/src/SeedApi.Test/SeedApi.Test.Custom.props b/seed/csharp-sdk/grpc-proto/src/SeedApi.Test/SeedApi.Test.Custom.props new file mode 100644 index 00000000000..55e683b0772 --- /dev/null +++ b/seed/csharp-sdk/grpc-proto/src/SeedApi.Test/SeedApi.Test.Custom.props @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto/src/SeedApi.Test/SeedApi.Test.csproj b/seed/csharp-sdk/grpc-proto/src/SeedApi.Test/SeedApi.Test.csproj index c5be29f92d9..fd7b07f82e5 100644 --- a/seed/csharp-sdk/grpc-proto/src/SeedApi.Test/SeedApi.Test.csproj +++ b/seed/csharp-sdk/grpc-proto/src/SeedApi.Test/SeedApi.Test.csproj @@ -20,7 +20,8 @@ - + + \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto/src/SeedApi/Core/IRequestOptions.cs b/seed/csharp-sdk/grpc-proto/src/SeedApi/Core/IRequestOptions.cs new file mode 100644 index 00000000000..e365cd162f0 --- /dev/null +++ b/seed/csharp-sdk/grpc-proto/src/SeedApi/Core/IRequestOptions.cs @@ -0,0 +1,34 @@ +using System; +using System.Net.Http; + +#nullable enable + +namespace SeedApi.Core; + +internal interface IRequestOptions +{ + /// + /// The Base URL for the API. + /// + public string? BaseUrl { get; init; } + + /// + /// The http client used to make requests. + /// + public HttpClient? HttpClient { get; init; } + + /// + /// The http headers sent with the request. + /// + internal Headers Headers { get; init; } + + /// + /// The http client used to make requests. + /// + public int? MaxRetries { get; init; } + + /// + /// The timeout for the request. + /// + public TimeSpan? Timeout { get; init; } +} diff --git a/seed/csharp-sdk/grpc-proto/src/SeedApi/Core/Public/ClientOptions.cs b/seed/csharp-sdk/grpc-proto/src/SeedApi/Core/Public/ClientOptions.cs index e4f22c22965..5c12019c489 100644 --- a/seed/csharp-sdk/grpc-proto/src/SeedApi/Core/Public/ClientOptions.cs +++ b/seed/csharp-sdk/grpc-proto/src/SeedApi/Core/Public/ClientOptions.cs @@ -52,6 +52,5 @@ internal ClientOptions Clone() Timeout = Timeout, Headers = new Headers(new Dictionary(Headers)), }; - ; } } diff --git a/seed/csharp-sdk/grpc-proto/src/SeedApi/Core/Public/RequestOptions.cs b/seed/csharp-sdk/grpc-proto/src/SeedApi/Core/Public/RequestOptions.cs index 6e0923d6f60..4d560f8337a 100644 --- a/seed/csharp-sdk/grpc-proto/src/SeedApi/Core/Public/RequestOptions.cs +++ b/seed/csharp-sdk/grpc-proto/src/SeedApi/Core/Public/RequestOptions.cs @@ -6,7 +6,7 @@ namespace SeedApi; -public partial class RequestOptions +public partial class RequestOptions : IRequestOptions { /// /// The Base URL for the API. @@ -31,5 +31,5 @@ public partial class RequestOptions /// /// The http headers sent with the request. /// - internal Headers Headers { get; init; } = new(); + Headers IRequestOptions.Headers { get; init; } = new(); } diff --git a/seed/csharp-sdk/grpc-proto/src/SeedApi/Core/RawClient.cs b/seed/csharp-sdk/grpc-proto/src/SeedApi/Core/RawClient.cs index 0cc389e831a..8cff8b09a01 100644 --- a/seed/csharp-sdk/grpc-proto/src/SeedApi/Core/RawClient.cs +++ b/seed/csharp-sdk/grpc-proto/src/SeedApi/Core/RawClient.cs @@ -1,4 +1,5 @@ using System.Net.Http; +using System.Net.Http.Headers; using System.Text; using System.Threading; @@ -54,7 +55,7 @@ public record BaseApiRequest public Headers Headers { get; init; } = new(); - public RequestOptions? Options { get; init; } + public IRequestOptions? Options { get; init; } } /// @@ -135,11 +136,14 @@ private HttpRequestMessage BuildHttpRequest(BaseApiRequest request) } if (request.ContentType != null) { - request.Headers.Add("Content-Type", request.ContentType); + httpRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse( + request.ContentType + ); } SetHeaders(httpRequest, Options.Headers); SetHeaders(httpRequest, request.Headers); SetHeaders(httpRequest, request.Options?.Headers ?? new Headers()); + return httpRequest; } diff --git a/seed/csharp-sdk/grpc-proto/src/SeedApi/SeedApi.Custom.props b/seed/csharp-sdk/grpc-proto/src/SeedApi/SeedApi.Custom.props new file mode 100644 index 00000000000..70df2849401 --- /dev/null +++ b/seed/csharp-sdk/grpc-proto/src/SeedApi/SeedApi.Custom.props @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/seed/csharp-sdk/grpc-proto/src/SeedApi/SeedApi.csproj b/seed/csharp-sdk/grpc-proto/src/SeedApi/SeedApi.csproj index 1cafc3fca56..4cd5aff38e8 100644 --- a/seed/csharp-sdk/grpc-proto/src/SeedApi/SeedApi.csproj +++ b/seed/csharp-sdk/grpc-proto/src/SeedApi/SeedApi.csproj @@ -8,6 +8,8 @@ 12 enable 0.0.1 + $(Version) + $(Version) README.md https://github.com/grpc-proto/fern @@ -34,7 +36,7 @@ - + @@ -66,4 +68,5 @@ + diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/.mock/definition/endpoints/content-type.yml b/seed/java-sdk/exhaustive/custom-client-class-name/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/java-sdk/exhaustive/custom-client-class-name/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java index 144281483d2..b0a786b1335 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java @@ -6,6 +6,7 @@ import com.seed.exhaustive.core.ClientOptions; import com.seed.exhaustive.core.Suppliers; import com.seed.exhaustive.resources.endpoints.container.ContainerClient; +import com.seed.exhaustive.resources.endpoints.contenttype.ContentTypeClient; import com.seed.exhaustive.resources.endpoints.enum_.EnumClient; import com.seed.exhaustive.resources.endpoints.httpmethods.HttpMethodsClient; import com.seed.exhaustive.resources.endpoints.object.ObjectClient; @@ -19,6 +20,8 @@ public class EndpointsClient { protected final Supplier containerClient; + protected final Supplier contentTypeClient; + protected final Supplier enumClient; protected final Supplier httpMethodsClient; @@ -34,6 +37,7 @@ public class EndpointsClient { public EndpointsClient(ClientOptions clientOptions) { this.clientOptions = clientOptions; this.containerClient = Suppliers.memoize(() -> new ContainerClient(clientOptions)); + this.contentTypeClient = Suppliers.memoize(() -> new ContentTypeClient(clientOptions)); this.enumClient = Suppliers.memoize(() -> new EnumClient(clientOptions)); this.httpMethodsClient = Suppliers.memoize(() -> new HttpMethodsClient(clientOptions)); this.objectClient = Suppliers.memoize(() -> new ObjectClient(clientOptions)); @@ -46,6 +50,10 @@ public ContainerClient container() { return this.containerClient.get(); } + public ContentTypeClient contentType() { + return this.contentTypeClient.get(); + } + public EnumClient enum_() { return this.enumClient.get(); } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java new file mode 100644 index 00000000000..e7db2103102 --- /dev/null +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java @@ -0,0 +1,121 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.resources.endpoints.contenttype; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.seed.exhaustive.core.BestApiException; +import com.seed.exhaustive.core.BestException; +import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.MediaTypes; +import com.seed.exhaustive.core.ObjectMappers; +import com.seed.exhaustive.core.RequestOptions; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.io.IOException; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class ContentTypeClient { + protected final ClientOptions clientOptions; + + public ContentTypeClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + public void postJsonPatchContentType() { + postJsonPatchContentType(ObjectWithOptionalField.builder().build()); + } + + public void postJsonPatchContentType(ObjectWithOptionalField request) { + postJsonPatchContentType(request, null); + } + + public void postJsonPatchContentType(ObjectWithOptionalField request, RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("foo") + .addPathSegments("bar") + .build(); + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new BestException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new BestApiException( + "Error with status code " + response.code(), + response.code(), + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } catch (IOException e) { + throw new BestException("Network error executing HTTP request", e); + } + } + + public void postJsonPatchContentWithCharsetType() { + postJsonPatchContentWithCharsetType(ObjectWithOptionalField.builder().build()); + } + + public void postJsonPatchContentWithCharsetType(ObjectWithOptionalField request) { + postJsonPatchContentWithCharsetType(request, null); + } + + public void postJsonPatchContentWithCharsetType(ObjectWithOptionalField request, RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("foo") + .addPathSegments("baz") + .build(); + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new BestException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new BestApiException( + "Error with status code " + response.code(), + response.code(), + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } catch (IOException e) { + throw new BestException("Network error executing HTTP request", e); + } + } +} diff --git a/seed/java-sdk/exhaustive/custom-dependency/.mock/definition/endpoints/content-type.yml b/seed/java-sdk/exhaustive/custom-dependency/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/java-sdk/exhaustive/custom-dependency/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java index 144281483d2..b0a786b1335 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java @@ -6,6 +6,7 @@ import com.seed.exhaustive.core.ClientOptions; import com.seed.exhaustive.core.Suppliers; import com.seed.exhaustive.resources.endpoints.container.ContainerClient; +import com.seed.exhaustive.resources.endpoints.contenttype.ContentTypeClient; import com.seed.exhaustive.resources.endpoints.enum_.EnumClient; import com.seed.exhaustive.resources.endpoints.httpmethods.HttpMethodsClient; import com.seed.exhaustive.resources.endpoints.object.ObjectClient; @@ -19,6 +20,8 @@ public class EndpointsClient { protected final Supplier containerClient; + protected final Supplier contentTypeClient; + protected final Supplier enumClient; protected final Supplier httpMethodsClient; @@ -34,6 +37,7 @@ public class EndpointsClient { public EndpointsClient(ClientOptions clientOptions) { this.clientOptions = clientOptions; this.containerClient = Suppliers.memoize(() -> new ContainerClient(clientOptions)); + this.contentTypeClient = Suppliers.memoize(() -> new ContentTypeClient(clientOptions)); this.enumClient = Suppliers.memoize(() -> new EnumClient(clientOptions)); this.httpMethodsClient = Suppliers.memoize(() -> new HttpMethodsClient(clientOptions)); this.objectClient = Suppliers.memoize(() -> new ObjectClient(clientOptions)); @@ -46,6 +50,10 @@ public ContainerClient container() { return this.containerClient.get(); } + public ContentTypeClient contentType() { + return this.contentTypeClient.get(); + } + public EnumClient enum_() { return this.enumClient.get(); } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java new file mode 100644 index 00000000000..4113789731d --- /dev/null +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java @@ -0,0 +1,121 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.resources.endpoints.contenttype; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.MediaTypes; +import com.seed.exhaustive.core.ObjectMappers; +import com.seed.exhaustive.core.RequestOptions; +import com.seed.exhaustive.core.SeedExhaustiveApiException; +import com.seed.exhaustive.core.SeedExhaustiveException; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.io.IOException; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class ContentTypeClient { + protected final ClientOptions clientOptions; + + public ContentTypeClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + public void postJsonPatchContentType() { + postJsonPatchContentType(ObjectWithOptionalField.builder().build()); + } + + public void postJsonPatchContentType(ObjectWithOptionalField request) { + postJsonPatchContentType(request, null); + } + + public void postJsonPatchContentType(ObjectWithOptionalField request, RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("foo") + .addPathSegments("bar") + .build(); + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new SeedExhaustiveException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), + response.code(), + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } + + public void postJsonPatchContentWithCharsetType() { + postJsonPatchContentWithCharsetType(ObjectWithOptionalField.builder().build()); + } + + public void postJsonPatchContentWithCharsetType(ObjectWithOptionalField request) { + postJsonPatchContentWithCharsetType(request, null); + } + + public void postJsonPatchContentWithCharsetType(ObjectWithOptionalField request, RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("foo") + .addPathSegments("baz") + .build(); + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new SeedExhaustiveException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), + response.code(), + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } +} diff --git a/seed/java-sdk/exhaustive/custom-error-names/.mock/definition/endpoints/content-type.yml b/seed/java-sdk/exhaustive/custom-error-names/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/java-sdk/exhaustive/custom-error-names/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java index 144281483d2..b0a786b1335 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java @@ -6,6 +6,7 @@ import com.seed.exhaustive.core.ClientOptions; import com.seed.exhaustive.core.Suppliers; import com.seed.exhaustive.resources.endpoints.container.ContainerClient; +import com.seed.exhaustive.resources.endpoints.contenttype.ContentTypeClient; import com.seed.exhaustive.resources.endpoints.enum_.EnumClient; import com.seed.exhaustive.resources.endpoints.httpmethods.HttpMethodsClient; import com.seed.exhaustive.resources.endpoints.object.ObjectClient; @@ -19,6 +20,8 @@ public class EndpointsClient { protected final Supplier containerClient; + protected final Supplier contentTypeClient; + protected final Supplier enumClient; protected final Supplier httpMethodsClient; @@ -34,6 +37,7 @@ public class EndpointsClient { public EndpointsClient(ClientOptions clientOptions) { this.clientOptions = clientOptions; this.containerClient = Suppliers.memoize(() -> new ContainerClient(clientOptions)); + this.contentTypeClient = Suppliers.memoize(() -> new ContentTypeClient(clientOptions)); this.enumClient = Suppliers.memoize(() -> new EnumClient(clientOptions)); this.httpMethodsClient = Suppliers.memoize(() -> new HttpMethodsClient(clientOptions)); this.objectClient = Suppliers.memoize(() -> new ObjectClient(clientOptions)); @@ -46,6 +50,10 @@ public ContainerClient container() { return this.containerClient.get(); } + public ContentTypeClient contentType() { + return this.contentTypeClient.get(); + } + public EnumClient enum_() { return this.enumClient.get(); } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java new file mode 100644 index 00000000000..9ccd41fcf75 --- /dev/null +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java @@ -0,0 +1,121 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.resources.endpoints.contenttype; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.CustomApiException; +import com.seed.exhaustive.core.CustomException; +import com.seed.exhaustive.core.MediaTypes; +import com.seed.exhaustive.core.ObjectMappers; +import com.seed.exhaustive.core.RequestOptions; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.io.IOException; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class ContentTypeClient { + protected final ClientOptions clientOptions; + + public ContentTypeClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + public void postJsonPatchContentType() { + postJsonPatchContentType(ObjectWithOptionalField.builder().build()); + } + + public void postJsonPatchContentType(ObjectWithOptionalField request) { + postJsonPatchContentType(request, null); + } + + public void postJsonPatchContentType(ObjectWithOptionalField request, RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("foo") + .addPathSegments("bar") + .build(); + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new CustomException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new CustomApiException( + "Error with status code " + response.code(), + response.code(), + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } catch (IOException e) { + throw new CustomException("Network error executing HTTP request", e); + } + } + + public void postJsonPatchContentWithCharsetType() { + postJsonPatchContentWithCharsetType(ObjectWithOptionalField.builder().build()); + } + + public void postJsonPatchContentWithCharsetType(ObjectWithOptionalField request) { + postJsonPatchContentWithCharsetType(request, null); + } + + public void postJsonPatchContentWithCharsetType(ObjectWithOptionalField request, RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("foo") + .addPathSegments("baz") + .build(); + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new CustomException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new CustomApiException( + "Error with status code " + response.code(), + response.code(), + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } catch (IOException e) { + throw new CustomException("Network error executing HTTP request", e); + } + } +} diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/.mock/definition/endpoints/content-type.yml b/seed/java-sdk/exhaustive/enable-public-constructors/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/java-sdk/exhaustive/enable-public-constructors/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java index 144281483d2..b0a786b1335 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java @@ -6,6 +6,7 @@ import com.seed.exhaustive.core.ClientOptions; import com.seed.exhaustive.core.Suppliers; import com.seed.exhaustive.resources.endpoints.container.ContainerClient; +import com.seed.exhaustive.resources.endpoints.contenttype.ContentTypeClient; import com.seed.exhaustive.resources.endpoints.enum_.EnumClient; import com.seed.exhaustive.resources.endpoints.httpmethods.HttpMethodsClient; import com.seed.exhaustive.resources.endpoints.object.ObjectClient; @@ -19,6 +20,8 @@ public class EndpointsClient { protected final Supplier containerClient; + protected final Supplier contentTypeClient; + protected final Supplier enumClient; protected final Supplier httpMethodsClient; @@ -34,6 +37,7 @@ public class EndpointsClient { public EndpointsClient(ClientOptions clientOptions) { this.clientOptions = clientOptions; this.containerClient = Suppliers.memoize(() -> new ContainerClient(clientOptions)); + this.contentTypeClient = Suppliers.memoize(() -> new ContentTypeClient(clientOptions)); this.enumClient = Suppliers.memoize(() -> new EnumClient(clientOptions)); this.httpMethodsClient = Suppliers.memoize(() -> new HttpMethodsClient(clientOptions)); this.objectClient = Suppliers.memoize(() -> new ObjectClient(clientOptions)); @@ -46,6 +50,10 @@ public ContainerClient container() { return this.containerClient.get(); } + public ContentTypeClient contentType() { + return this.contentTypeClient.get(); + } + public EnumClient enum_() { return this.enumClient.get(); } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java new file mode 100644 index 00000000000..4113789731d --- /dev/null +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java @@ -0,0 +1,121 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.resources.endpoints.contenttype; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.MediaTypes; +import com.seed.exhaustive.core.ObjectMappers; +import com.seed.exhaustive.core.RequestOptions; +import com.seed.exhaustive.core.SeedExhaustiveApiException; +import com.seed.exhaustive.core.SeedExhaustiveException; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.io.IOException; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class ContentTypeClient { + protected final ClientOptions clientOptions; + + public ContentTypeClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + public void postJsonPatchContentType() { + postJsonPatchContentType(ObjectWithOptionalField.builder().build()); + } + + public void postJsonPatchContentType(ObjectWithOptionalField request) { + postJsonPatchContentType(request, null); + } + + public void postJsonPatchContentType(ObjectWithOptionalField request, RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("foo") + .addPathSegments("bar") + .build(); + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new SeedExhaustiveException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), + response.code(), + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } + + public void postJsonPatchContentWithCharsetType() { + postJsonPatchContentWithCharsetType(ObjectWithOptionalField.builder().build()); + } + + public void postJsonPatchContentWithCharsetType(ObjectWithOptionalField request) { + postJsonPatchContentWithCharsetType(request, null); + } + + public void postJsonPatchContentWithCharsetType(ObjectWithOptionalField request, RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("foo") + .addPathSegments("baz") + .build(); + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new SeedExhaustiveException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), + response.code(), + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } +} diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/.mock/definition/endpoints/content-type.yml b/seed/java-sdk/exhaustive/forward-compatible-enums/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java index 144281483d2..b0a786b1335 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java @@ -6,6 +6,7 @@ import com.seed.exhaustive.core.ClientOptions; import com.seed.exhaustive.core.Suppliers; import com.seed.exhaustive.resources.endpoints.container.ContainerClient; +import com.seed.exhaustive.resources.endpoints.contenttype.ContentTypeClient; import com.seed.exhaustive.resources.endpoints.enum_.EnumClient; import com.seed.exhaustive.resources.endpoints.httpmethods.HttpMethodsClient; import com.seed.exhaustive.resources.endpoints.object.ObjectClient; @@ -19,6 +20,8 @@ public class EndpointsClient { protected final Supplier containerClient; + protected final Supplier contentTypeClient; + protected final Supplier enumClient; protected final Supplier httpMethodsClient; @@ -34,6 +37,7 @@ public class EndpointsClient { public EndpointsClient(ClientOptions clientOptions) { this.clientOptions = clientOptions; this.containerClient = Suppliers.memoize(() -> new ContainerClient(clientOptions)); + this.contentTypeClient = Suppliers.memoize(() -> new ContentTypeClient(clientOptions)); this.enumClient = Suppliers.memoize(() -> new EnumClient(clientOptions)); this.httpMethodsClient = Suppliers.memoize(() -> new HttpMethodsClient(clientOptions)); this.objectClient = Suppliers.memoize(() -> new ObjectClient(clientOptions)); @@ -46,6 +50,10 @@ public ContainerClient container() { return this.containerClient.get(); } + public ContentTypeClient contentType() { + return this.contentTypeClient.get(); + } + public EnumClient enum_() { return this.enumClient.get(); } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java new file mode 100644 index 00000000000..4113789731d --- /dev/null +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java @@ -0,0 +1,121 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.resources.endpoints.contenttype; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.MediaTypes; +import com.seed.exhaustive.core.ObjectMappers; +import com.seed.exhaustive.core.RequestOptions; +import com.seed.exhaustive.core.SeedExhaustiveApiException; +import com.seed.exhaustive.core.SeedExhaustiveException; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.io.IOException; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class ContentTypeClient { + protected final ClientOptions clientOptions; + + public ContentTypeClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + public void postJsonPatchContentType() { + postJsonPatchContentType(ObjectWithOptionalField.builder().build()); + } + + public void postJsonPatchContentType(ObjectWithOptionalField request) { + postJsonPatchContentType(request, null); + } + + public void postJsonPatchContentType(ObjectWithOptionalField request, RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("foo") + .addPathSegments("bar") + .build(); + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new SeedExhaustiveException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), + response.code(), + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } + + public void postJsonPatchContentWithCharsetType() { + postJsonPatchContentWithCharsetType(ObjectWithOptionalField.builder().build()); + } + + public void postJsonPatchContentWithCharsetType(ObjectWithOptionalField request) { + postJsonPatchContentWithCharsetType(request, null); + } + + public void postJsonPatchContentWithCharsetType(ObjectWithOptionalField request, RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("foo") + .addPathSegments("baz") + .build(); + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new SeedExhaustiveException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), + response.code(), + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } +} diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/.mock/definition/endpoints/content-type.yml b/seed/java-sdk/exhaustive/json-include-non-empty/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/java-sdk/exhaustive/json-include-non-empty/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java index 144281483d2..b0a786b1335 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java @@ -6,6 +6,7 @@ import com.seed.exhaustive.core.ClientOptions; import com.seed.exhaustive.core.Suppliers; import com.seed.exhaustive.resources.endpoints.container.ContainerClient; +import com.seed.exhaustive.resources.endpoints.contenttype.ContentTypeClient; import com.seed.exhaustive.resources.endpoints.enum_.EnumClient; import com.seed.exhaustive.resources.endpoints.httpmethods.HttpMethodsClient; import com.seed.exhaustive.resources.endpoints.object.ObjectClient; @@ -19,6 +20,8 @@ public class EndpointsClient { protected final Supplier containerClient; + protected final Supplier contentTypeClient; + protected final Supplier enumClient; protected final Supplier httpMethodsClient; @@ -34,6 +37,7 @@ public class EndpointsClient { public EndpointsClient(ClientOptions clientOptions) { this.clientOptions = clientOptions; this.containerClient = Suppliers.memoize(() -> new ContainerClient(clientOptions)); + this.contentTypeClient = Suppliers.memoize(() -> new ContentTypeClient(clientOptions)); this.enumClient = Suppliers.memoize(() -> new EnumClient(clientOptions)); this.httpMethodsClient = Suppliers.memoize(() -> new HttpMethodsClient(clientOptions)); this.objectClient = Suppliers.memoize(() -> new ObjectClient(clientOptions)); @@ -46,6 +50,10 @@ public ContainerClient container() { return this.containerClient.get(); } + public ContentTypeClient contentType() { + return this.contentTypeClient.get(); + } + public EnumClient enum_() { return this.enumClient.get(); } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java new file mode 100644 index 00000000000..4113789731d --- /dev/null +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java @@ -0,0 +1,121 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.resources.endpoints.contenttype; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.MediaTypes; +import com.seed.exhaustive.core.ObjectMappers; +import com.seed.exhaustive.core.RequestOptions; +import com.seed.exhaustive.core.SeedExhaustiveApiException; +import com.seed.exhaustive.core.SeedExhaustiveException; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.io.IOException; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class ContentTypeClient { + protected final ClientOptions clientOptions; + + public ContentTypeClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + public void postJsonPatchContentType() { + postJsonPatchContentType(ObjectWithOptionalField.builder().build()); + } + + public void postJsonPatchContentType(ObjectWithOptionalField request) { + postJsonPatchContentType(request, null); + } + + public void postJsonPatchContentType(ObjectWithOptionalField request, RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("foo") + .addPathSegments("bar") + .build(); + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new SeedExhaustiveException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), + response.code(), + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } + + public void postJsonPatchContentWithCharsetType() { + postJsonPatchContentWithCharsetType(ObjectWithOptionalField.builder().build()); + } + + public void postJsonPatchContentWithCharsetType(ObjectWithOptionalField request) { + postJsonPatchContentWithCharsetType(request, null); + } + + public void postJsonPatchContentWithCharsetType(ObjectWithOptionalField request, RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("foo") + .addPathSegments("baz") + .build(); + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new SeedExhaustiveException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), + response.code(), + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } +} diff --git a/seed/java-sdk/exhaustive/local-files/.mock/definition/endpoints/content-type.yml b/seed/java-sdk/exhaustive/local-files/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/java-sdk/exhaustive/local-files/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/java-sdk/exhaustive/local-files/resources/endpoints/EndpointsClient.java b/seed/java-sdk/exhaustive/local-files/resources/endpoints/EndpointsClient.java index 65094bf3031..32835fdb87a 100644 --- a/seed/java-sdk/exhaustive/local-files/resources/endpoints/EndpointsClient.java +++ b/seed/java-sdk/exhaustive/local-files/resources/endpoints/EndpointsClient.java @@ -7,6 +7,7 @@ import com.fern.sdk.core.ClientOptions; import com.fern.sdk.core.Suppliers; import com.fern.sdk.resources.endpoints.container.ContainerClient; +import com.fern.sdk.resources.endpoints.contenttype.ContentTypeClient; import com.fern.sdk.resources.endpoints.enum_.EnumClient; import com.fern.sdk.resources.endpoints.httpmethods.HttpMethodsClient; import com.fern.sdk.resources.endpoints.object.ObjectClient; @@ -20,6 +21,8 @@ public class EndpointsClient { protected final Supplier containerClient; + protected final Supplier contentTypeClient; + protected final Supplier enumClient; protected final Supplier httpMethodsClient; @@ -35,6 +38,7 @@ public class EndpointsClient { public EndpointsClient(ClientOptions clientOptions) { this.clientOptions = clientOptions; this.containerClient = Suppliers.memoize(() -> new ContainerClient(clientOptions)); + this.contentTypeClient = Suppliers.memoize(() -> new ContentTypeClient(clientOptions)); this.enumClient = Suppliers.memoize(() -> new EnumClient(clientOptions)); this.httpMethodsClient = Suppliers.memoize(() -> new HttpMethodsClient(clientOptions)); this.objectClient = Suppliers.memoize(() -> new ObjectClient(clientOptions)); @@ -47,6 +51,10 @@ public ContainerClient container() { return this.containerClient.get(); } + public ContentTypeClient contentType() { + return this.contentTypeClient.get(); + } + public EnumClient enum_() { return this.enumClient.get(); } diff --git a/seed/java-sdk/exhaustive/local-files/resources/endpoints/contenttype/ContentTypeClient.java b/seed/java-sdk/exhaustive/local-files/resources/endpoints/contenttype/ContentTypeClient.java new file mode 100644 index 00000000000..1b606cf6b8d --- /dev/null +++ b/seed/java-sdk/exhaustive/local-files/resources/endpoints/contenttype/ContentTypeClient.java @@ -0,0 +1,120 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package com.fern.sdk.resources.endpoints.contenttype; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fern.sdk.core.ClientOptions; +import com.fern.sdk.core.MediaTypes; +import com.fern.sdk.core.ObjectMappers; +import com.fern.sdk.core.RequestOptions; +import com.fern.sdk.core.SeedExhaustiveApiException; +import com.fern.sdk.core.SeedExhaustiveException; +import com.fern.sdk.resources.types.object.types.ObjectWithOptionalField; +import java.io.IOException; +import java.lang.Object; +import java.lang.String; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class ContentTypeClient { + protected final ClientOptions clientOptions; + + public ContentTypeClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + public void postJsonPatchContentType() { + postJsonPatchContentType(ObjectWithOptionalField.builder().build()); + } + + public void postJsonPatchContentType(ObjectWithOptionalField request) { + postJsonPatchContentType(request,null); + } + + public void postJsonPatchContentType(ObjectWithOptionalField request, + RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()).newBuilder() + .addPathSegments("foo") + .addPathSegments("bar") + .build(); + RequestBody body; + try { + body = RequestBody.create(ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } + catch(JsonProcessingException e) { + throw new SeedExhaustiveException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new SeedExhaustiveApiException("Error with status code " + response.code(), response.code(), ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } + catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } + + public void postJsonPatchContentWithCharsetType() { + postJsonPatchContentWithCharsetType(ObjectWithOptionalField.builder().build()); + } + + public void postJsonPatchContentWithCharsetType(ObjectWithOptionalField request) { + postJsonPatchContentWithCharsetType(request,null); + } + + public void postJsonPatchContentWithCharsetType(ObjectWithOptionalField request, + RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()).newBuilder() + .addPathSegments("foo") + .addPathSegments("baz") + .build(); + RequestBody body; + try { + body = RequestBody.create(ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } + catch(JsonProcessingException e) { + throw new SeedExhaustiveException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new SeedExhaustiveApiException("Error with status code " + response.code(), response.code(), ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } + catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } +} diff --git a/seed/java-sdk/exhaustive/no-custom-config/.mock/definition/endpoints/content-type.yml b/seed/java-sdk/exhaustive/no-custom-config/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/java-sdk/exhaustive/no-custom-config/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java index 144281483d2..b0a786b1335 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/EndpointsClient.java @@ -6,6 +6,7 @@ import com.seed.exhaustive.core.ClientOptions; import com.seed.exhaustive.core.Suppliers; import com.seed.exhaustive.resources.endpoints.container.ContainerClient; +import com.seed.exhaustive.resources.endpoints.contenttype.ContentTypeClient; import com.seed.exhaustive.resources.endpoints.enum_.EnumClient; import com.seed.exhaustive.resources.endpoints.httpmethods.HttpMethodsClient; import com.seed.exhaustive.resources.endpoints.object.ObjectClient; @@ -19,6 +20,8 @@ public class EndpointsClient { protected final Supplier containerClient; + protected final Supplier contentTypeClient; + protected final Supplier enumClient; protected final Supplier httpMethodsClient; @@ -34,6 +37,7 @@ public class EndpointsClient { public EndpointsClient(ClientOptions clientOptions) { this.clientOptions = clientOptions; this.containerClient = Suppliers.memoize(() -> new ContainerClient(clientOptions)); + this.contentTypeClient = Suppliers.memoize(() -> new ContentTypeClient(clientOptions)); this.enumClient = Suppliers.memoize(() -> new EnumClient(clientOptions)); this.httpMethodsClient = Suppliers.memoize(() -> new HttpMethodsClient(clientOptions)); this.objectClient = Suppliers.memoize(() -> new ObjectClient(clientOptions)); @@ -46,6 +50,10 @@ public ContainerClient container() { return this.containerClient.get(); } + public ContentTypeClient contentType() { + return this.contentTypeClient.get(); + } + public EnumClient enum_() { return this.enumClient.get(); } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java new file mode 100644 index 00000000000..4113789731d --- /dev/null +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/contenttype/ContentTypeClient.java @@ -0,0 +1,121 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.exhaustive.resources.endpoints.contenttype; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.MediaTypes; +import com.seed.exhaustive.core.ObjectMappers; +import com.seed.exhaustive.core.RequestOptions; +import com.seed.exhaustive.core.SeedExhaustiveApiException; +import com.seed.exhaustive.core.SeedExhaustiveException; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.io.IOException; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class ContentTypeClient { + protected final ClientOptions clientOptions; + + public ContentTypeClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + public void postJsonPatchContentType() { + postJsonPatchContentType(ObjectWithOptionalField.builder().build()); + } + + public void postJsonPatchContentType(ObjectWithOptionalField request) { + postJsonPatchContentType(request, null); + } + + public void postJsonPatchContentType(ObjectWithOptionalField request, RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("foo") + .addPathSegments("bar") + .build(); + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new SeedExhaustiveException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), + response.code(), + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } + + public void postJsonPatchContentWithCharsetType() { + postJsonPatchContentWithCharsetType(ObjectWithOptionalField.builder().build()); + } + + public void postJsonPatchContentWithCharsetType(ObjectWithOptionalField request) { + postJsonPatchContentWithCharsetType(request, null); + } + + public void postJsonPatchContentWithCharsetType(ObjectWithOptionalField request, RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("foo") + .addPathSegments("baz") + .build(); + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new SeedExhaustiveException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), + response.code(), + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } +} diff --git a/seed/java-sdk/license/.github/workflows/ci.yml b/seed/java-sdk/license/.github/workflows/ci.yml new file mode 100644 index 00000000000..8598a73092a --- /dev/null +++ b/seed/java-sdk/license/.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@v3 + + - 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@v3 + + - 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@v3 + + - 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-sdk/license/.gitignore b/seed/java-sdk/license/.gitignore new file mode 100644 index 00000000000..d4199abc2cd --- /dev/null +++ b/seed/java-sdk/license/.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-sdk/license/.mock/definition/__package__.yml b/seed/java-sdk/license/.mock/definition/__package__.yml new file mode 100644 index 00000000000..b1e4d4a878f --- /dev/null +++ b/seed/java-sdk/license/.mock/definition/__package__.yml @@ -0,0 +1,13 @@ +types: + Type: + docs: A simple type with just a name. + properties: + name: string + +service: + auth: false + base-path: / + endpoints: + get: + path: "/" + method: GET diff --git a/seed/java-sdk/license/.mock/definition/api.yml b/seed/java-sdk/license/.mock/definition/api.yml new file mode 100644 index 00000000000..5523ff1f181 --- /dev/null +++ b/seed/java-sdk/license/.mock/definition/api.yml @@ -0,0 +1 @@ +name: license diff --git a/seed/java-sdk/license/.mock/fern.config.json b/seed/java-sdk/license/.mock/fern.config.json new file mode 100644 index 00000000000..4c8e54ac313 --- /dev/null +++ b/seed/java-sdk/license/.mock/fern.config.json @@ -0,0 +1 @@ +{"organization": "fern-test", "version": "*"} \ No newline at end of file diff --git a/seed/java-sdk/license/.mock/generators.yml b/seed/java-sdk/license/.mock/generators.yml new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/seed/java-sdk/license/.mock/generators.yml @@ -0,0 +1 @@ +{} diff --git a/seed/java-sdk/license/build.gradle b/seed/java-sdk/license/build.gradle new file mode 100644 index 00000000000..4e9fe420429 --- /dev/null +++ b/seed/java-sdk/license/build.gradle @@ -0,0 +1,70 @@ +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.squareup.okhttp3:okhttp:4.12.0' + api 'com.fasterxml.jackson.core:jackson-databind:2.17.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.17.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.2' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.2' +} + + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +spotless { + java { + palantirJavaFormat() + } +} + +java { + withSourcesJar() + withJavadocJar() +} + +test { + useJUnitPlatform() + testLogging { + showStandardStreams = true + } +} +publishing { + publications { + maven(MavenPublication) { + groupId = 'com.fern' + artifactId = 'license' + version = '0.0.1' + from components.java + pom { + scm { + connection = 'scm:git:git://github.com/license/fern.git' + developerConnection = 'scm:git:git://github.com/license/fern.git' + url = 'https://github.com/license/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-sdk/license/sample-app/build.gradle b/seed/java-sdk/license/sample-app/build.gradle new file mode 100644 index 00000000000..4ee8f227b7a --- /dev/null +++ b/seed/java-sdk/license/sample-app/build.gradle @@ -0,0 +1,19 @@ +plugins { + id 'java-library' +} + +repositories { + mavenCentral() + maven { + url 'https://s01.oss.sonatype.org/content/repositories/releases/' + } +} + +dependencies { + implementation rootProject +} + + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + diff --git a/seed/java-sdk/license/sample-app/src/main/java/sample/App.java b/seed/java-sdk/license/sample-app/src/main/java/sample/App.java new file mode 100644 index 00000000000..67e3f0a349b --- /dev/null +++ b/seed/java-sdk/license/sample-app/src/main/java/sample/App.java @@ -0,0 +1,13 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package sample; + +import java.lang.String; + +public final class App { + public static void main(String[] args) { + // import com.seed.license.SeedLicenseClient + } +} diff --git a/seed/java-sdk/license/settings.gradle b/seed/java-sdk/license/settings.gradle new file mode 100644 index 00000000000..aed36fec10b --- /dev/null +++ b/seed/java-sdk/license/settings.gradle @@ -0,0 +1 @@ +include 'sample-app' \ No newline at end of file diff --git a/seed/java-sdk/license/snippet-templates.json b/seed/java-sdk/license/snippet-templates.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/java-sdk/license/snippet.json b/seed/java-sdk/license/snippet.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/SeedLicenseClient.java b/seed/java-sdk/license/src/main/java/com/seed/license/SeedLicenseClient.java new file mode 100644 index 00000000000..77c244b7297 --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/SeedLicenseClient.java @@ -0,0 +1,61 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license; + +import com.seed.license.core.ClientOptions; +import com.seed.license.core.ObjectMappers; +import com.seed.license.core.RequestOptions; +import com.seed.license.core.SeedLicenseApiException; +import com.seed.license.core.SeedLicenseException; +import java.io.IOException; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class SeedLicenseClient { + protected final ClientOptions clientOptions; + + public SeedLicenseClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + public void get() { + get(null); + } + + public void get(RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .build(); + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("GET", null) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .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(); + if (response.isSuccessful()) { + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new SeedLicenseApiException( + "Error with status code " + response.code(), + response.code(), + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } catch (IOException e) { + throw new SeedLicenseException("Network error executing HTTP request", e); + } + } + + public static SeedLicenseClientBuilder builder() { + return new SeedLicenseClientBuilder(); + } +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/SeedLicenseClientBuilder.java b/seed/java-sdk/license/src/main/java/com/seed/license/SeedLicenseClientBuilder.java new file mode 100644 index 00000000000..caa45a24585 --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/SeedLicenseClientBuilder.java @@ -0,0 +1,23 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license; + +import com.seed.license.core.ClientOptions; +import com.seed.license.core.Environment; + +public final class SeedLicenseClientBuilder { + private ClientOptions.Builder clientOptionsBuilder = ClientOptions.builder(); + + private Environment environment; + + public SeedLicenseClientBuilder url(String url) { + this.environment = Environment.custom(url); + return this; + } + + public SeedLicenseClient build() { + clientOptionsBuilder.environment(this.environment); + return new SeedLicenseClient(clientOptionsBuilder.build()); + } +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/core/ClientOptions.java b/seed/java-sdk/license/src/main/java/com/seed/license/core/ClientOptions.java new file mode 100644 index 00000000000..6f5488c115c --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/core/ClientOptions.java @@ -0,0 +1,103 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license.core; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import okhttp3.OkHttpClient; + +public final class ClientOptions { + private final Environment environment; + + private final Map headers; + + private final Map> headerSuppliers; + + private final OkHttpClient httpClient; + + private ClientOptions( + Environment environment, + Map headers, + Map> headerSuppliers, + OkHttpClient httpClient) { + this.environment = environment; + this.headers = new HashMap<>(); + this.headers.putAll(headers); + this.headers.putAll(new HashMap() { + { + put("X-Fern-Language", "JAVA"); + } + }); + this.headerSuppliers = headerSuppliers; + this.httpClient = httpClient; + } + + public Environment environment() { + return this.environment; + } + + public Map headers(RequestOptions requestOptions) { + Map values = new HashMap<>(this.headers); + headerSuppliers.forEach((key, supplier) -> { + values.put(key, supplier.get()); + }); + if (requestOptions != null) { + values.putAll(requestOptions.getHeaders()); + } + return values; + } + + public OkHttpClient httpClient() { + return this.httpClient; + } + + public OkHttpClient httpClientWithTimeout(RequestOptions requestOptions) { + if (requestOptions == null) { + return this.httpClient; + } + return this.httpClient + .newBuilder() + .callTimeout(requestOptions.getTimeout().get(), requestOptions.getTimeoutTimeUnit()) + .connectTimeout(0, TimeUnit.SECONDS) + .writeTimeout(0, TimeUnit.SECONDS) + .readTimeout(0, TimeUnit.SECONDS) + .build(); + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private Environment environment; + + private final Map headers = new HashMap<>(); + + private final Map> headerSuppliers = new HashMap<>(); + + public Builder environment(Environment environment) { + this.environment = environment; + return this; + } + + public Builder addHeader(String key, String value) { + this.headers.put(key, value); + return this; + } + + public Builder addHeader(String key, Supplier value) { + this.headerSuppliers.put(key, value); + return this; + } + + public ClientOptions build() { + OkHttpClient okhttpClient = new OkHttpClient.Builder() + .addInterceptor(new RetryInterceptor(3)) + .build(); + return new ClientOptions(environment, headers, headerSuppliers, okhttpClient); + } + } +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/core/DateTimeDeserializer.java b/seed/java-sdk/license/src/main/java/com/seed/license/core/DateTimeDeserializer.java new file mode 100644 index 00000000000..d40c7d4dcfe --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/core/DateTimeDeserializer.java @@ -0,0 +1,55 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license.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); + } + } + } +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/core/Environment.java b/seed/java-sdk/license/src/main/java/com/seed/license/core/Environment.java new file mode 100644 index 00000000000..337ecef672f --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/core/Environment.java @@ -0,0 +1,20 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license.core; + +public final class Environment { + private final String url; + + private Environment(String url) { + this.url = url; + } + + public String getUrl() { + return this.url; + } + + public static Environment custom(String url) { + return new Environment(url); + } +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/core/FileStream.java b/seed/java-sdk/license/src/main/java/com/seed/license/core/FileStream.java new file mode 100644 index 00000000000..d1e1a46c71b --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/core/InputStreamRequestBody.java b/seed/java-sdk/license/src/main/java/com/seed/license/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..a7b1dc0f301 --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/core/MediaTypes.java b/seed/java-sdk/license/src/main/java/com/seed/license/core/MediaTypes.java new file mode 100644 index 00000000000..b3cb20f01e3 --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/core/MediaTypes.java @@ -0,0 +1,13 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license.core; + +import okhttp3.MediaType; + +public final class MediaTypes { + + public static final MediaType APPLICATION_JSON = MediaType.parse("application/json"); + + private MediaTypes() {} +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/core/ObjectMappers.java b/seed/java-sdk/license/src/main/java/com/seed/license/core/ObjectMappers.java new file mode 100644 index 00000000000..8b4851fb4b6 --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/core/ObjectMappers.java @@ -0,0 +1,36 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license.core; + +import com.fasterxml.jackson.annotation.JsonInclude; +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; + +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()); + } + } +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/core/RequestOptions.java b/seed/java-sdk/license/src/main/java/com/seed/license/core/RequestOptions.java new file mode 100644 index 00000000000..fa1bb399c79 --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/core/RequestOptions.java @@ -0,0 +1,58 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license.core; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +public final class RequestOptions { + private final Optional timeout; + + private final TimeUnit timeoutTimeUnit; + + private RequestOptions(Optional timeout, TimeUnit timeoutTimeUnit) { + this.timeout = timeout; + this.timeoutTimeUnit = timeoutTimeUnit; + } + + public Optional getTimeout() { + return timeout; + } + + public TimeUnit getTimeoutTimeUnit() { + return timeoutTimeUnit; + } + + public Map getHeaders() { + Map headers = new HashMap<>(); + return headers; + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private Optional timeout = Optional.empty(); + + private TimeUnit timeoutTimeUnit = TimeUnit.SECONDS; + + public Builder timeout(Integer timeout) { + this.timeout = Optional.of(timeout); + return this; + } + + public Builder timeout(Integer timeout, TimeUnit timeoutTimeUnit) { + this.timeout = Optional.of(timeout); + this.timeoutTimeUnit = timeoutTimeUnit; + return this; + } + + public RequestOptions build() { + return new RequestOptions(timeout, timeoutTimeUnit); + } + } +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/core/ResponseBodyInputStream.java b/seed/java-sdk/license/src/main/java/com/seed/license/core/ResponseBodyInputStream.java new file mode 100644 index 00000000000..be43f130ecd --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/core/ResponseBodyInputStream.java @@ -0,0 +1,45 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license.core; + +import java.io.FilterInputStream; +import java.io.IOException; +import okhttp3.Response; + +/** + * A custom InputStream that wraps the InputStream from the OkHttp Response and ensures that the + * OkHttp Response object is properly closed when the stream is closed. + * + * This class extends FilterInputStream and takes an OkHttp Response object as a parameter. + * It retrieves the InputStream from the Response and overrides the close method to close + * both the InputStream and the Response object, ensuring proper resource management and preventing + * premature closure of the underlying HTTP connection. + */ +public class ResponseBodyInputStream extends FilterInputStream { + private final Response response; + + /** + * Constructs a ResponseBodyInputStream that wraps the InputStream from the given OkHttp + * Response object. + * + * @param response the OkHttp Response object from which the InputStream is retrieved + * @throws IOException if an I/O error occurs while retrieving the InputStream + */ + public ResponseBodyInputStream(Response response) throws IOException { + super(response.body().byteStream()); + this.response = response; + } + + /** + * Closes the InputStream and the associated OkHttp Response object. This ensures that the + * underlying HTTP connection is properly closed after the stream is no longer needed. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void close() throws IOException { + super.close(); + response.close(); // Ensure the response is closed when the stream is closed + } +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/core/ResponseBodyReader.java b/seed/java-sdk/license/src/main/java/com/seed/license/core/ResponseBodyReader.java new file mode 100644 index 00000000000..795239360bd --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/core/ResponseBodyReader.java @@ -0,0 +1,44 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license.core; + +import java.io.FilterReader; +import java.io.IOException; +import okhttp3.Response; + +/** + * A custom Reader that wraps the Reader from the OkHttp Response and ensures that the + * OkHttp Response object is properly closed when the reader is closed. + * + * This class extends FilterReader and takes an OkHttp Response object as a parameter. + * It retrieves the Reader from the Response and overrides the close method to close + * both the Reader and the Response object, ensuring proper resource management and preventing + * premature closure of the underlying HTTP connection. + */ +public class ResponseBodyReader extends FilterReader { + private final Response response; + + /** + * Constructs a ResponseBodyReader that wraps the Reader from the given OkHttp Response object. + * + * @param response the OkHttp Response object from which the Reader is retrieved + * @throws IOException if an I/O error occurs while retrieving the Reader + */ + public ResponseBodyReader(Response response) throws IOException { + super(response.body().charStream()); + this.response = response; + } + + /** + * Closes the Reader and the associated OkHttp Response object. This ensures that the + * underlying HTTP connection is properly closed after the reader is no longer needed. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void close() throws IOException { + super.close(); + response.close(); // Ensure the response is closed when the reader is closed + } +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/core/RetryInterceptor.java b/seed/java-sdk/license/src/main/java/com/seed/license/core/RetryInterceptor.java new file mode 100644 index 00000000000..81394fb3cb3 --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/core/RetryInterceptor.java @@ -0,0 +1,78 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license.core; + +import java.io.IOException; +import java.time.Duration; +import java.util.Optional; +import java.util.Random; +import okhttp3.Interceptor; +import okhttp3.Response; + +public class RetryInterceptor implements Interceptor { + + private static final Duration ONE_SECOND = Duration.ofSeconds(1); + private final ExponentialBackoff backoff; + private final Random random = new Random(); + + public RetryInterceptor(int maxRetries) { + this.backoff = new ExponentialBackoff(maxRetries); + } + + @Override + public Response intercept(Chain chain) throws IOException { + Response response = chain.proceed(chain.request()); + + if (shouldRetry(response.code())) { + return retryChain(response, chain); + } + + return response; + } + + private Response retryChain(Response response, Chain chain) throws IOException { + Optional nextBackoff = this.backoff.nextBackoff(); + while (nextBackoff.isPresent()) { + try { + Thread.sleep(nextBackoff.get().toMillis()); + } catch (InterruptedException e) { + throw new IOException("Interrupted while trying request", e); + } + response.close(); + response = chain.proceed(chain.request()); + if (shouldRetry(response.code())) { + nextBackoff = this.backoff.nextBackoff(); + } else { + return response; + } + } + + return response; + } + + private static boolean shouldRetry(int statusCode) { + return statusCode == 408 || statusCode == 409 || statusCode == 429 || statusCode >= 500; + } + + private final class ExponentialBackoff { + + private final int maxNumRetries; + + private int retryNumber = 0; + + ExponentialBackoff(int maxNumRetries) { + this.maxNumRetries = maxNumRetries; + } + + public Optional nextBackoff() { + retryNumber += 1; + if (retryNumber > maxNumRetries) { + return Optional.empty(); + } + + int upperBound = (int) Math.pow(2, retryNumber); + return Optional.of(ONE_SECOND.multipliedBy(random.nextInt(upperBound))); + } + } +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/core/SeedLicenseApiException.java b/seed/java-sdk/license/src/main/java/com/seed/license/core/SeedLicenseApiException.java new file mode 100644 index 00000000000..68367cf6e92 --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/core/SeedLicenseApiException.java @@ -0,0 +1,45 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license.core; + +/** + * This exception type will be thrown for any non-2XX API responses. + */ +public class SeedLicenseApiException extends SeedLicenseException { + /** + * The error code of the response that triggered the exception. + */ + private final int statusCode; + + /** + * The body of the response that triggered the exception. + */ + private final Object body; + + public SeedLicenseApiException(String message, int statusCode, Object body) { + super(message); + this.statusCode = statusCode; + this.body = body; + } + + /** + * @return the statusCode + */ + public int statusCode() { + return this.statusCode; + } + + /** + * @return the body + */ + public Object body() { + return this.body; + } + + @java.lang.Override + public String toString() { + return "SeedLicenseApiException{" + "message: " + getMessage() + ", statusCode: " + statusCode + ", body: " + + body + "}"; + } +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/core/SeedLicenseException.java b/seed/java-sdk/license/src/main/java/com/seed/license/core/SeedLicenseException.java new file mode 100644 index 00000000000..e303ecb745e --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/core/SeedLicenseException.java @@ -0,0 +1,17 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license.core; + +/** + * This class serves as the base exception for all errors in the SDK. + */ +public class SeedLicenseException extends RuntimeException { + public SeedLicenseException(String message) { + super(message); + } + + public SeedLicenseException(String message, Exception e) { + super(message, e); + } +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/core/Stream.java b/seed/java-sdk/license/src/main/java/com/seed/license/core/Stream.java new file mode 100644 index 00000000000..0b49285d559 --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/core/Stream.java @@ -0,0 +1,97 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license.core; + +import java.io.Reader; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Scanner; + +/** + * The {@code Stream} class implmenets {@link Iterable} to provide a simple mechanism for reading and parsing + * objects of a given type from data streamed via a {@link Reader} using a specified delimiter. + *

+ * {@code Stream} assumes that data is being pushed to the provided {@link Reader} asynchronously and utilizes a + * {@code Scanner} to block during iteration if the next object is not available. + * + * @param The type of objects in the stream. + */ +public final class Stream implements Iterable { + /** + * The {@link Class} of the objects in the stream. + */ + private final Class valueType; + /** + * The {@link Scanner} used for reading from the input stream and blocking when neede during iteration. + */ + private final Scanner scanner; + + /** + * Constructs a new {@code Stream} with the specified value type, reader, and delimiter. + * + * @param valueType The class of the objects in the stream. + * @param reader The reader that provides the streamed data. + * @param delimiter The delimiter used to separate elements in the stream. + */ + public Stream(Class valueType, Reader reader, String delimiter) { + this.scanner = new Scanner(reader).useDelimiter(delimiter); + this.valueType = valueType; + } + + /** + * Returns an iterator over the elements in this stream that blocks during iteration when the next object is + * not yet available. + * + * @return An iterator that can be used to traverse the elements in the stream. + */ + @Override + public Iterator iterator() { + return new Iterator() { + /** + * Returns {@code true} if there are more elements in the stream. + *

+ * Will block and wait for input if the stream has not ended and the next object is not yet available. + * + * @return {@code true} if there are more elements, {@code false} otherwise. + */ + @Override + public boolean hasNext() { + return scanner.hasNext(); + } + + /** + * Returns the next element in the stream. + *

+ * Will block and wait for input if the stream has not ended and the next object is not yet available. + * + * @return The next element in the stream. + * @throws NoSuchElementException If there are no more elements in the stream. + */ + @Override + public T next() { + if (!scanner.hasNext()) { + throw new NoSuchElementException(); + } else { + try { + T parsedResponse = ObjectMappers.JSON_MAPPER.readValue( + scanner.next().trim(), valueType); + return parsedResponse; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + /** + * Removing elements from {@code Stream} is not supported. + * + * @throws UnsupportedOperationException Always, as removal is not supported. + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/core/Suppliers.java b/seed/java-sdk/license/src/main/java/com/seed/license/core/Suppliers.java new file mode 100644 index 00000000000..dcd064c6c0c --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/core/Suppliers.java @@ -0,0 +1,23 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license.core; + +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + +public final class Suppliers { + private Suppliers() {} + + public static Supplier memoize(Supplier delegate) { + AtomicReference value = new AtomicReference<>(); + return () -> { + T val = value.get(); + if (val == null) { + val = value.updateAndGet(cur -> cur == null ? Objects.requireNonNull(delegate.get()) : cur); + } + return val; + }; + } +} diff --git a/seed/java-sdk/license/src/main/java/com/seed/license/types/Type.java b/seed/java-sdk/license/src/main/java/com/seed/license/types/Type.java new file mode 100644 index 00000000000..0be0d65f777 --- /dev/null +++ b/seed/java-sdk/license/src/main/java/com/seed/license/types/Type.java @@ -0,0 +1,102 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license.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.databind.annotation.JsonDeserialize; +import com.seed.license.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = Type.Builder.class) +public final class Type { + private final String name; + + private final Map additionalProperties; + + private Type(String name, Map additionalProperties) { + this.name = name; + this.additionalProperties = additionalProperties; + } + + @JsonProperty("name") + public String getName() { + return name; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof Type && equalTo((Type) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(Type other) { + return name.equals(other.name); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.name); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static NameStage builder() { + return new Builder(); + } + + public interface NameStage { + _FinalStage name(@NotNull String name); + + Builder from(Type other); + } + + public interface _FinalStage { + Type build(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements NameStage, _FinalStage { + private String name; + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(Type other) { + name(other.getName()); + 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 Type build() { + return new Type(name, additionalProperties); + } + } +} diff --git a/seed/java-sdk/license/src/test/java/com/seed/license/TestClient.java b/seed/java-sdk/license/src/test/java/com/seed/license/TestClient.java new file mode 100644 index 00000000000..dedbffa502a --- /dev/null +++ b/seed/java-sdk/license/src/test/java/com/seed/license/TestClient.java @@ -0,0 +1,11 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.license; + +public final class TestClient { + public void test() { + // Add tests here and mark this file in .fernignore + assert true; + } +} diff --git a/seed/java-sdk/server-sent-event-examples/.github/workflows/ci.yml b/seed/java-sdk/server-sent-event-examples/.github/workflows/ci.yml new file mode 100644 index 00000000000..8598a73092a --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/.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@v3 + + - 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@v3 + + - 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@v3 + + - 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-sdk/server-sent-event-examples/.gitignore b/seed/java-sdk/server-sent-event-examples/.gitignore new file mode 100644 index 00000000000..d4199abc2cd --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/.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-sdk/server-sent-event-examples/.mock/definition/api.yml b/seed/java-sdk/server-sent-event-examples/.mock/definition/api.yml new file mode 100644 index 00000000000..80e84c41785 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/.mock/definition/api.yml @@ -0,0 +1 @@ +name: server-sent-events diff --git a/seed/java-sdk/server-sent-event-examples/.mock/definition/completions.yml b/seed/java-sdk/server-sent-event-examples/.mock/definition/completions.yml new file mode 100644 index 00000000000..09a88253331 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/.mock/definition/completions.yml @@ -0,0 +1,36 @@ +types: + StreamedCompletion: + properties: + delta: string + tokens: optional + +service: + auth: false + base-path: "" + endpoints: + stream: + method: POST + path: /stream + request: + name: StreamCompletionRequest + body: + properties: + query: string + response-stream: + type: StreamedCompletion + format: sse + terminator: "[[DONE]]" + examples: + - name: "Stream completions" + request: + query: "foo" + response: + stream: + - event: discriminant-1 + data: + delta: "foo" + tokens: 1 + - event: discriminant-2 + data: + delta: "bar" + tokens: 2 diff --git a/seed/java-sdk/server-sent-event-examples/.mock/fern.config.json b/seed/java-sdk/server-sent-event-examples/.mock/fern.config.json new file mode 100644 index 00000000000..4c8e54ac313 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/.mock/fern.config.json @@ -0,0 +1 @@ +{"organization": "fern-test", "version": "*"} \ No newline at end of file diff --git a/seed/java-sdk/server-sent-event-examples/.mock/generators.yml b/seed/java-sdk/server-sent-event-examples/.mock/generators.yml new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/.mock/generators.yml @@ -0,0 +1 @@ +{} diff --git a/seed/java-sdk/server-sent-event-examples/build.gradle b/seed/java-sdk/server-sent-event-examples/build.gradle new file mode 100644 index 00000000000..032d2fb79ae --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/build.gradle @@ -0,0 +1,70 @@ +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.squareup.okhttp3:okhttp:4.12.0' + api 'com.fasterxml.jackson.core:jackson-databind:2.17.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.17.2' + api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.2' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.2' +} + + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +spotless { + java { + palantirJavaFormat() + } +} + +java { + withSourcesJar() + withJavadocJar() +} + +test { + useJUnitPlatform() + testLogging { + showStandardStreams = true + } +} +publishing { + publications { + maven(MavenPublication) { + groupId = 'com.fern' + artifactId = 'server-sent-event-examples' + version = '0.0.1' + from components.java + pom { + scm { + connection = 'scm:git:git://github.com/server-sent-event-examples/fern.git' + developerConnection = 'scm:git:git://github.com/server-sent-event-examples/fern.git' + url = 'https://github.com/server-sent-event-examples/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-sdk/server-sent-event-examples/sample-app/build.gradle b/seed/java-sdk/server-sent-event-examples/sample-app/build.gradle new file mode 100644 index 00000000000..4ee8f227b7a --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/sample-app/build.gradle @@ -0,0 +1,19 @@ +plugins { + id 'java-library' +} + +repositories { + mavenCentral() + maven { + url 'https://s01.oss.sonatype.org/content/repositories/releases/' + } +} + +dependencies { + implementation rootProject +} + + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + diff --git a/seed/java-sdk/server-sent-event-examples/sample-app/src/main/java/sample/App.java b/seed/java-sdk/server-sent-event-examples/sample-app/src/main/java/sample/App.java new file mode 100644 index 00000000000..713a3d82b0a --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/sample-app/src/main/java/sample/App.java @@ -0,0 +1,13 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +package sample; + +import java.lang.String; + +public final class App { + public static void main(String[] args) { + // import com.seed.serverSentEvents.SeedServerSentEventsClient + } +} diff --git a/seed/java-sdk/server-sent-event-examples/settings.gradle b/seed/java-sdk/server-sent-event-examples/settings.gradle new file mode 100644 index 00000000000..aed36fec10b --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/settings.gradle @@ -0,0 +1 @@ +include 'sample-app' \ No newline at end of file diff --git a/seed/java-sdk/server-sent-event-examples/snippet-templates.json b/seed/java-sdk/server-sent-event-examples/snippet-templates.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/java-sdk/server-sent-event-examples/snippet.json b/seed/java-sdk/server-sent-event-examples/snippet.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/SeedServerSentEventsClient.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/SeedServerSentEventsClient.java new file mode 100644 index 00000000000..8b823cea78a --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/SeedServerSentEventsClient.java @@ -0,0 +1,28 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents; + +import com.seed.serverSentEvents.core.ClientOptions; +import com.seed.serverSentEvents.core.Suppliers; +import com.seed.serverSentEvents.resources.completions.CompletionsClient; +import java.util.function.Supplier; + +public class SeedServerSentEventsClient { + protected final ClientOptions clientOptions; + + protected final Supplier completionsClient; + + public SeedServerSentEventsClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.completionsClient = Suppliers.memoize(() -> new CompletionsClient(clientOptions)); + } + + public CompletionsClient completions() { + return this.completionsClient.get(); + } + + public static SeedServerSentEventsClientBuilder builder() { + return new SeedServerSentEventsClientBuilder(); + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/SeedServerSentEventsClientBuilder.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/SeedServerSentEventsClientBuilder.java new file mode 100644 index 00000000000..2c17fb7af10 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/SeedServerSentEventsClientBuilder.java @@ -0,0 +1,23 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents; + +import com.seed.serverSentEvents.core.ClientOptions; +import com.seed.serverSentEvents.core.Environment; + +public final class SeedServerSentEventsClientBuilder { + private ClientOptions.Builder clientOptionsBuilder = ClientOptions.builder(); + + private Environment environment; + + public SeedServerSentEventsClientBuilder url(String url) { + this.environment = Environment.custom(url); + return this; + } + + public SeedServerSentEventsClient build() { + clientOptionsBuilder.environment(this.environment); + return new SeedServerSentEventsClient(clientOptionsBuilder.build()); + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/ClientOptions.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/ClientOptions.java new file mode 100644 index 00000000000..1a280b7b281 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/ClientOptions.java @@ -0,0 +1,103 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.core; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import okhttp3.OkHttpClient; + +public final class ClientOptions { + private final Environment environment; + + private final Map headers; + + private final Map> headerSuppliers; + + private final OkHttpClient httpClient; + + private ClientOptions( + Environment environment, + Map headers, + Map> headerSuppliers, + OkHttpClient httpClient) { + this.environment = environment; + this.headers = new HashMap<>(); + this.headers.putAll(headers); + this.headers.putAll(new HashMap() { + { + put("X-Fern-Language", "JAVA"); + } + }); + this.headerSuppliers = headerSuppliers; + this.httpClient = httpClient; + } + + public Environment environment() { + return this.environment; + } + + public Map headers(RequestOptions requestOptions) { + Map values = new HashMap<>(this.headers); + headerSuppliers.forEach((key, supplier) -> { + values.put(key, supplier.get()); + }); + if (requestOptions != null) { + values.putAll(requestOptions.getHeaders()); + } + return values; + } + + public OkHttpClient httpClient() { + return this.httpClient; + } + + public OkHttpClient httpClientWithTimeout(RequestOptions requestOptions) { + if (requestOptions == null) { + return this.httpClient; + } + return this.httpClient + .newBuilder() + .callTimeout(requestOptions.getTimeout().get(), requestOptions.getTimeoutTimeUnit()) + .connectTimeout(0, TimeUnit.SECONDS) + .writeTimeout(0, TimeUnit.SECONDS) + .readTimeout(0, TimeUnit.SECONDS) + .build(); + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private Environment environment; + + private final Map headers = new HashMap<>(); + + private final Map> headerSuppliers = new HashMap<>(); + + public Builder environment(Environment environment) { + this.environment = environment; + return this; + } + + public Builder addHeader(String key, String value) { + this.headers.put(key, value); + return this; + } + + public Builder addHeader(String key, Supplier value) { + this.headerSuppliers.put(key, value); + return this; + } + + public ClientOptions build() { + OkHttpClient okhttpClient = new OkHttpClient.Builder() + .addInterceptor(new RetryInterceptor(3)) + .build(); + return new ClientOptions(environment, headers, headerSuppliers, okhttpClient); + } + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/DateTimeDeserializer.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/DateTimeDeserializer.java new file mode 100644 index 00000000000..180511c8a53 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/DateTimeDeserializer.java @@ -0,0 +1,55 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.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); + } + } + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/Environment.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/Environment.java new file mode 100644 index 00000000000..9bdadb0071b --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/Environment.java @@ -0,0 +1,20 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.core; + +public final class Environment { + private final String url; + + private Environment(String url) { + this.url = url; + } + + public String getUrl() { + return this.url; + } + + public static Environment custom(String url) { + return new Environment(url); + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/FileStream.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/FileStream.java new file mode 100644 index 00000000000..cf1177b976d --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/FileStream.java @@ -0,0 +1,60 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.core; + +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a file stream with associated metadata for file uploads. + */ +public class FileStream { + private final InputStream inputStream; + private final String fileName; + private final MediaType contentType; + + /** + * Constructs a FileStream with the given input stream and optional metadata. + * + * @param inputStream The input stream of the file content. Must not be null. + * @param fileName The name of the file, or null if unknown. + * @param contentType The MIME type of the file content, or null if unknown. + * @throws NullPointerException if inputStream is null + */ + public FileStream(InputStream inputStream, @Nullable String fileName, @Nullable MediaType contentType) { + this.inputStream = Objects.requireNonNull(inputStream, "Input stream cannot be null"); + this.fileName = fileName; + this.contentType = contentType; + } + + public FileStream(InputStream inputStream) { + this(inputStream, null, null); + } + + public InputStream getInputStream() { + return inputStream; + } + + @Nullable + public String getFileName() { + return fileName; + } + + @Nullable + public MediaType getContentType() { + return contentType; + } + + /** + * Creates a RequestBody suitable for use with OkHttp client. + * + * @return A RequestBody instance representing this file stream. + */ + public RequestBody toRequestBody() { + return new InputStreamRequestBody(contentType, inputStream); + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/InputStreamRequestBody.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/InputStreamRequestBody.java new file mode 100644 index 00000000000..a97c3290df6 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/InputStreamRequestBody.java @@ -0,0 +1,79 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.core; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okhttp3.internal.Util; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; +import org.jetbrains.annotations.Nullable; + +/** + * A custom implementation of OkHttp's RequestBody that wraps an InputStream. + * This class allows streaming of data from an InputStream directly to an HTTP request body, + * which is useful for file uploads or sending large amounts of data without loading it all into memory. + */ +public class InputStreamRequestBody extends RequestBody { + private final InputStream inputStream; + private final MediaType contentType; + + /** + * Constructs an InputStreamRequestBody with the specified content type and input stream. + * + * @param contentType the MediaType of the content, or null if not known + * @param inputStream the InputStream containing the data to be sent + * @throws NullPointerException if inputStream is null + */ + public InputStreamRequestBody(@Nullable MediaType contentType, InputStream inputStream) { + this.contentType = contentType; + this.inputStream = Objects.requireNonNull(inputStream, "inputStream == null"); + } + + /** + * Returns the content type of this request body. + * + * @return the MediaType of the content, or null if not specified + */ + @Nullable + @Override + public MediaType contentType() { + return contentType; + } + + /** + * Returns the content length of this request body, if known. + * This method attempts to determine the length using the InputStream's available() method, + * which may not always accurately reflect the total length of the stream. + * + * @return the content length, or -1 if the length is unknown + * @throws IOException if an I/O error occurs + */ + @Override + public long contentLength() throws IOException { + return inputStream.available() == 0 ? -1 : inputStream.available(); + } + + /** + * Writes the content of the InputStream to the given BufferedSink. + * This method is responsible for transferring the data from the InputStream to the network request. + * + * @param sink the BufferedSink to write the content to + * @throws IOException if an I/O error occurs during writing + */ + @Override + public void writeTo(BufferedSink sink) throws IOException { + Source source = null; + try { + source = Okio.source(inputStream); + sink.writeAll(source); + } finally { + Util.closeQuietly(Objects.requireNonNull(source)); + } + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/MediaTypes.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/MediaTypes.java new file mode 100644 index 00000000000..d3f7e069c50 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/MediaTypes.java @@ -0,0 +1,13 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.core; + +import okhttp3.MediaType; + +public final class MediaTypes { + + public static final MediaType APPLICATION_JSON = MediaType.parse("application/json"); + + private MediaTypes() {} +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/ObjectMappers.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/ObjectMappers.java new file mode 100644 index 00000000000..f0bd140fc48 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/ObjectMappers.java @@ -0,0 +1,36 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.core; + +import com.fasterxml.jackson.annotation.JsonInclude; +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; + +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()); + } + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/RequestOptions.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/RequestOptions.java new file mode 100644 index 00000000000..a5cdf798262 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/RequestOptions.java @@ -0,0 +1,58 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.core; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +public final class RequestOptions { + private final Optional timeout; + + private final TimeUnit timeoutTimeUnit; + + private RequestOptions(Optional timeout, TimeUnit timeoutTimeUnit) { + this.timeout = timeout; + this.timeoutTimeUnit = timeoutTimeUnit; + } + + public Optional getTimeout() { + return timeout; + } + + public TimeUnit getTimeoutTimeUnit() { + return timeoutTimeUnit; + } + + public Map getHeaders() { + Map headers = new HashMap<>(); + return headers; + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private Optional timeout = Optional.empty(); + + private TimeUnit timeoutTimeUnit = TimeUnit.SECONDS; + + public Builder timeout(Integer timeout) { + this.timeout = Optional.of(timeout); + return this; + } + + public Builder timeout(Integer timeout, TimeUnit timeoutTimeUnit) { + this.timeout = Optional.of(timeout); + this.timeoutTimeUnit = timeoutTimeUnit; + return this; + } + + public RequestOptions build() { + return new RequestOptions(timeout, timeoutTimeUnit); + } + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/ResponseBodyInputStream.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/ResponseBodyInputStream.java new file mode 100644 index 00000000000..0cfc1775df0 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/ResponseBodyInputStream.java @@ -0,0 +1,45 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.core; + +import java.io.FilterInputStream; +import java.io.IOException; +import okhttp3.Response; + +/** + * A custom InputStream that wraps the InputStream from the OkHttp Response and ensures that the + * OkHttp Response object is properly closed when the stream is closed. + * + * This class extends FilterInputStream and takes an OkHttp Response object as a parameter. + * It retrieves the InputStream from the Response and overrides the close method to close + * both the InputStream and the Response object, ensuring proper resource management and preventing + * premature closure of the underlying HTTP connection. + */ +public class ResponseBodyInputStream extends FilterInputStream { + private final Response response; + + /** + * Constructs a ResponseBodyInputStream that wraps the InputStream from the given OkHttp + * Response object. + * + * @param response the OkHttp Response object from which the InputStream is retrieved + * @throws IOException if an I/O error occurs while retrieving the InputStream + */ + public ResponseBodyInputStream(Response response) throws IOException { + super(response.body().byteStream()); + this.response = response; + } + + /** + * Closes the InputStream and the associated OkHttp Response object. This ensures that the + * underlying HTTP connection is properly closed after the stream is no longer needed. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void close() throws IOException { + super.close(); + response.close(); // Ensure the response is closed when the stream is closed + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/ResponseBodyReader.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/ResponseBodyReader.java new file mode 100644 index 00000000000..20c8ec7594a --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/ResponseBodyReader.java @@ -0,0 +1,44 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.core; + +import java.io.FilterReader; +import java.io.IOException; +import okhttp3.Response; + +/** + * A custom Reader that wraps the Reader from the OkHttp Response and ensures that the + * OkHttp Response object is properly closed when the reader is closed. + * + * This class extends FilterReader and takes an OkHttp Response object as a parameter. + * It retrieves the Reader from the Response and overrides the close method to close + * both the Reader and the Response object, ensuring proper resource management and preventing + * premature closure of the underlying HTTP connection. + */ +public class ResponseBodyReader extends FilterReader { + private final Response response; + + /** + * Constructs a ResponseBodyReader that wraps the Reader from the given OkHttp Response object. + * + * @param response the OkHttp Response object from which the Reader is retrieved + * @throws IOException if an I/O error occurs while retrieving the Reader + */ + public ResponseBodyReader(Response response) throws IOException { + super(response.body().charStream()); + this.response = response; + } + + /** + * Closes the Reader and the associated OkHttp Response object. This ensures that the + * underlying HTTP connection is properly closed after the reader is no longer needed. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void close() throws IOException { + super.close(); + response.close(); // Ensure the response is closed when the reader is closed + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/RetryInterceptor.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/RetryInterceptor.java new file mode 100644 index 00000000000..e15651d30a1 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/RetryInterceptor.java @@ -0,0 +1,78 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.core; + +import java.io.IOException; +import java.time.Duration; +import java.util.Optional; +import java.util.Random; +import okhttp3.Interceptor; +import okhttp3.Response; + +public class RetryInterceptor implements Interceptor { + + private static final Duration ONE_SECOND = Duration.ofSeconds(1); + private final ExponentialBackoff backoff; + private final Random random = new Random(); + + public RetryInterceptor(int maxRetries) { + this.backoff = new ExponentialBackoff(maxRetries); + } + + @Override + public Response intercept(Chain chain) throws IOException { + Response response = chain.proceed(chain.request()); + + if (shouldRetry(response.code())) { + return retryChain(response, chain); + } + + return response; + } + + private Response retryChain(Response response, Chain chain) throws IOException { + Optional nextBackoff = this.backoff.nextBackoff(); + while (nextBackoff.isPresent()) { + try { + Thread.sleep(nextBackoff.get().toMillis()); + } catch (InterruptedException e) { + throw new IOException("Interrupted while trying request", e); + } + response.close(); + response = chain.proceed(chain.request()); + if (shouldRetry(response.code())) { + nextBackoff = this.backoff.nextBackoff(); + } else { + return response; + } + } + + return response; + } + + private static boolean shouldRetry(int statusCode) { + return statusCode == 408 || statusCode == 409 || statusCode == 429 || statusCode >= 500; + } + + private final class ExponentialBackoff { + + private final int maxNumRetries; + + private int retryNumber = 0; + + ExponentialBackoff(int maxNumRetries) { + this.maxNumRetries = maxNumRetries; + } + + public Optional nextBackoff() { + retryNumber += 1; + if (retryNumber > maxNumRetries) { + return Optional.empty(); + } + + int upperBound = (int) Math.pow(2, retryNumber); + return Optional.of(ONE_SECOND.multipliedBy(random.nextInt(upperBound))); + } + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/SeedServerSentEventsApiException.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/SeedServerSentEventsApiException.java new file mode 100644 index 00000000000..e05cee5451d --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/SeedServerSentEventsApiException.java @@ -0,0 +1,45 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.core; + +/** + * This exception type will be thrown for any non-2XX API responses. + */ +public class SeedServerSentEventsApiException extends SeedServerSentEventsException { + /** + * The error code of the response that triggered the exception. + */ + private final int statusCode; + + /** + * The body of the response that triggered the exception. + */ + private final Object body; + + public SeedServerSentEventsApiException(String message, int statusCode, Object body) { + super(message); + this.statusCode = statusCode; + this.body = body; + } + + /** + * @return the statusCode + */ + public int statusCode() { + return this.statusCode; + } + + /** + * @return the body + */ + public Object body() { + return this.body; + } + + @java.lang.Override + public String toString() { + return "SeedServerSentEventsApiException{" + "message: " + getMessage() + ", statusCode: " + statusCode + + ", body: " + body + "}"; + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/SeedServerSentEventsException.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/SeedServerSentEventsException.java new file mode 100644 index 00000000000..b0fd7ab79c0 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/SeedServerSentEventsException.java @@ -0,0 +1,17 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.core; + +/** + * This class serves as the base exception for all errors in the SDK. + */ +public class SeedServerSentEventsException extends RuntimeException { + public SeedServerSentEventsException(String message) { + super(message); + } + + public SeedServerSentEventsException(String message, Exception e) { + super(message, e); + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/Stream.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/Stream.java new file mode 100644 index 00000000000..b6bdbbfd5b2 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/Stream.java @@ -0,0 +1,97 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.core; + +import java.io.Reader; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Scanner; + +/** + * The {@code Stream} class implmenets {@link Iterable} to provide a simple mechanism for reading and parsing + * objects of a given type from data streamed via a {@link Reader} using a specified delimiter. + *

+ * {@code Stream} assumes that data is being pushed to the provided {@link Reader} asynchronously and utilizes a + * {@code Scanner} to block during iteration if the next object is not available. + * + * @param The type of objects in the stream. + */ +public final class Stream implements Iterable { + /** + * The {@link Class} of the objects in the stream. + */ + private final Class valueType; + /** + * The {@link Scanner} used for reading from the input stream and blocking when neede during iteration. + */ + private final Scanner scanner; + + /** + * Constructs a new {@code Stream} with the specified value type, reader, and delimiter. + * + * @param valueType The class of the objects in the stream. + * @param reader The reader that provides the streamed data. + * @param delimiter The delimiter used to separate elements in the stream. + */ + public Stream(Class valueType, Reader reader, String delimiter) { + this.scanner = new Scanner(reader).useDelimiter(delimiter); + this.valueType = valueType; + } + + /** + * Returns an iterator over the elements in this stream that blocks during iteration when the next object is + * not yet available. + * + * @return An iterator that can be used to traverse the elements in the stream. + */ + @Override + public Iterator iterator() { + return new Iterator() { + /** + * Returns {@code true} if there are more elements in the stream. + *

+ * Will block and wait for input if the stream has not ended and the next object is not yet available. + * + * @return {@code true} if there are more elements, {@code false} otherwise. + */ + @Override + public boolean hasNext() { + return scanner.hasNext(); + } + + /** + * Returns the next element in the stream. + *

+ * Will block and wait for input if the stream has not ended and the next object is not yet available. + * + * @return The next element in the stream. + * @throws NoSuchElementException If there are no more elements in the stream. + */ + @Override + public T next() { + if (!scanner.hasNext()) { + throw new NoSuchElementException(); + } else { + try { + T parsedResponse = ObjectMappers.JSON_MAPPER.readValue( + scanner.next().trim(), valueType); + return parsedResponse; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + /** + * Removing elements from {@code Stream} is not supported. + * + * @throws UnsupportedOperationException Always, as removal is not supported. + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/Suppliers.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/Suppliers.java new file mode 100644 index 00000000000..4d037f7dccf --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/core/Suppliers.java @@ -0,0 +1,23 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.core; + +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + +public final class Suppliers { + private Suppliers() {} + + public static Supplier memoize(Supplier delegate) { + AtomicReference value = new AtomicReference<>(); + return () -> { + T val = value.get(); + if (val == null) { + val = value.updateAndGet(cur -> cur == null ? Objects.requireNonNull(delegate.get()) : cur); + } + return val; + }; + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/resources/completions/CompletionsClient.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/resources/completions/CompletionsClient.java new file mode 100644 index 00000000000..a151c0b34b5 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/resources/completions/CompletionsClient.java @@ -0,0 +1,75 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.resources.completions; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.seed.serverSentEvents.core.ClientOptions; +import com.seed.serverSentEvents.core.MediaTypes; +import com.seed.serverSentEvents.core.ObjectMappers; +import com.seed.serverSentEvents.core.RequestOptions; +import com.seed.serverSentEvents.core.ResponseBodyReader; +import com.seed.serverSentEvents.core.SeedServerSentEventsApiException; +import com.seed.serverSentEvents.core.SeedServerSentEventsException; +import com.seed.serverSentEvents.core.Stream; +import com.seed.serverSentEvents.resources.completions.requests.StreamCompletionRequest; +import com.seed.serverSentEvents.resources.completions.types.StreamedCompletion; +import java.io.IOException; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class CompletionsClient { + protected final ClientOptions clientOptions; + + public CompletionsClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + public Iterable stream(StreamCompletionRequest request) { + return stream(request, null); + } + + public Iterable stream(StreamCompletionRequest request, RequestOptions requestOptions) { + HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("stream") + .build(); + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new SeedServerSentEventsException("Failed to serialize request", e); + } + Request okhttpRequest = new Request.Builder() + .url(httpUrl) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .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(); + if (response.isSuccessful()) { + return new Stream( + StreamedCompletion.class, new ResponseBodyReader(response), "[[DONE]]"); + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + throw new SeedServerSentEventsApiException( + "Error with status code " + response.code(), + response.code(), + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class)); + } catch (IOException e) { + throw new SeedServerSentEventsException("Network error executing HTTP request", e); + } + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/resources/completions/requests/StreamCompletionRequest.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/resources/completions/requests/StreamCompletionRequest.java new file mode 100644 index 00000000000..a27c8e22024 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/resources/completions/requests/StreamCompletionRequest.java @@ -0,0 +1,102 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.resources.completions.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.databind.annotation.JsonDeserialize; +import com.seed.serverSentEvents.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = StreamCompletionRequest.Builder.class) +public final class StreamCompletionRequest { + private final String query; + + private final Map additionalProperties; + + private StreamCompletionRequest(String query, Map additionalProperties) { + this.query = query; + this.additionalProperties = additionalProperties; + } + + @JsonProperty("query") + public String getQuery() { + return query; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof StreamCompletionRequest && equalTo((StreamCompletionRequest) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(StreamCompletionRequest other) { + return query.equals(other.query); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.query); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static QueryStage builder() { + return new Builder(); + } + + public interface QueryStage { + _FinalStage query(@NotNull String query); + + Builder from(StreamCompletionRequest other); + } + + public interface _FinalStage { + StreamCompletionRequest build(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements QueryStage, _FinalStage { + private String query; + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(StreamCompletionRequest other) { + query(other.getQuery()); + return this; + } + + @java.lang.Override + @JsonSetter("query") + public _FinalStage query(@NotNull String query) { + this.query = Objects.requireNonNull(query, "query must not be null"); + return this; + } + + @java.lang.Override + public StreamCompletionRequest build() { + return new StreamCompletionRequest(query, additionalProperties); + } + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/resources/completions/types/StreamedCompletion.java b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/resources/completions/types/StreamedCompletion.java new file mode 100644 index 00000000000..cf21a4b65e4 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/main/java/com/seed/serverSentEvents/resources/completions/types/StreamedCompletion.java @@ -0,0 +1,132 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents.resources.completions.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.serverSentEvents.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = StreamedCompletion.Builder.class) +public final class StreamedCompletion { + private final String delta; + + private final Optional tokens; + + private final Map additionalProperties; + + private StreamedCompletion(String delta, Optional tokens, Map additionalProperties) { + this.delta = delta; + this.tokens = tokens; + this.additionalProperties = additionalProperties; + } + + @JsonProperty("delta") + public String getDelta() { + return delta; + } + + @JsonProperty("tokens") + public Optional getTokens() { + return tokens; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof StreamedCompletion && equalTo((StreamedCompletion) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(StreamedCompletion other) { + return delta.equals(other.delta) && tokens.equals(other.tokens); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.delta, this.tokens); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static DeltaStage builder() { + return new Builder(); + } + + public interface DeltaStage { + _FinalStage delta(@NotNull String delta); + + Builder from(StreamedCompletion other); + } + + public interface _FinalStage { + StreamedCompletion build(); + + _FinalStage tokens(Optional tokens); + + _FinalStage tokens(Integer tokens); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements DeltaStage, _FinalStage { + private String delta; + + private Optional tokens = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(StreamedCompletion other) { + delta(other.getDelta()); + tokens(other.getTokens()); + return this; + } + + @java.lang.Override + @JsonSetter("delta") + public _FinalStage delta(@NotNull String delta) { + this.delta = Objects.requireNonNull(delta, "delta must not be null"); + return this; + } + + @java.lang.Override + public _FinalStage tokens(Integer tokens) { + this.tokens = Optional.ofNullable(tokens); + return this; + } + + @java.lang.Override + @JsonSetter(value = "tokens", nulls = Nulls.SKIP) + public _FinalStage tokens(Optional tokens) { + this.tokens = tokens; + return this; + } + + @java.lang.Override + public StreamedCompletion build() { + return new StreamedCompletion(delta, tokens, additionalProperties); + } + } +} diff --git a/seed/java-sdk/server-sent-event-examples/src/test/java/com/seed/serverSentEvents/TestClient.java b/seed/java-sdk/server-sent-event-examples/src/test/java/com/seed/serverSentEvents/TestClient.java new file mode 100644 index 00000000000..7f29c3d6426 --- /dev/null +++ b/seed/java-sdk/server-sent-event-examples/src/test/java/com/seed/serverSentEvents/TestClient.java @@ -0,0 +1,11 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.seed.serverSentEvents; + +public final class TestClient { + public void test() { + // Add tests here and mark this file in .fernignore + assert true; + } +} diff --git a/seed/php-sdk/exhaustive/.mock/definition/endpoints/content-type.yml b/seed/php-sdk/exhaustive/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/php-sdk/exhaustive/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/php-sdk/exhaustive/src/Endpoints/ContentType/ContentTypeClient.php b/seed/php-sdk/exhaustive/src/Endpoints/ContentType/ContentTypeClient.php new file mode 100644 index 00000000000..36afdc367b6 --- /dev/null +++ b/seed/php-sdk/exhaustive/src/Endpoints/ContentType/ContentTypeClient.php @@ -0,0 +1,94 @@ +client = $client; + } + + /** + * @param ObjectWithOptionalField $request + * @param ?array{ + * baseUrl?: string, + * } $options + * @throws SeedException + * @throws SeedApiException + */ + public function postJsonPatchContentType(ObjectWithOptionalField $request, ?array $options = null): void + { + try { + $response = $this->client->sendRequest( + new JsonApiRequest( + baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '', + path: "/foo/bar", + method: HttpMethod::POST, + body: $request, + ), + ); + $statusCode = $response->getStatusCode(); + if ($statusCode >= 200 && $statusCode < 400) { + return; + } + } 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 ObjectWithOptionalField $request + * @param ?array{ + * baseUrl?: string, + * } $options + * @throws SeedException + * @throws SeedApiException + */ + public function postJsonPatchContentWithCharsetType(ObjectWithOptionalField $request, ?array $options = null): void + { + try { + $response = $this->client->sendRequest( + new JsonApiRequest( + baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '', + path: "/foo/baz", + method: HttpMethod::POST, + body: $request, + ), + ); + $statusCode = $response->getStatusCode(); + if ($statusCode >= 200 && $statusCode < 400) { + return; + } + } catch (ClientExceptionInterface $e) { + throw new SeedException(message: $e->getMessage(), previous: $e); + } + throw new SeedApiException( + message: 'API request failed', + statusCode: $statusCode, + body: $response->getBody()->getContents(), + ); + } +} diff --git a/seed/php-sdk/exhaustive/src/Endpoints/EndpointsClient.php b/seed/php-sdk/exhaustive/src/Endpoints/EndpointsClient.php index 1fca36a88ae..e884c50ad0c 100644 --- a/seed/php-sdk/exhaustive/src/Endpoints/EndpointsClient.php +++ b/seed/php-sdk/exhaustive/src/Endpoints/EndpointsClient.php @@ -3,6 +3,7 @@ namespace Seed\Endpoints; use Seed\Endpoints\Container\ContainerClient; +use Seed\Endpoints\ContentType\ContentTypeClient; use Seed\Endpoints\Enum\EnumClient; use Seed\Endpoints\HttpMethods\HttpMethodsClient; use Seed\Endpoints\Object\ObjectClient; @@ -18,6 +19,11 @@ class EndpointsClient */ public ContainerClient $container; + /** + * @var ContentTypeClient $contentType + */ + public ContentTypeClient $contentType; + /** * @var EnumClient $enum */ @@ -61,6 +67,7 @@ public function __construct( ) { $this->client = $client; $this->container = new ContainerClient($this->client); + $this->contentType = new ContentTypeClient($this->client); $this->enum = new EnumClient($this->client); $this->httpMethods = new HttpMethodsClient($this->client); $this->object = new ObjectClient($this->client); diff --git a/seed/php-sdk/grpc-proto-exhaustive/.mock/generators.yml b/seed/php-sdk/grpc-proto-exhaustive/.mock/generators.yml index c23323621f2..972ed6d7b73 100644 --- a/seed/php-sdk/grpc-proto-exhaustive/.mock/generators.yml +++ b/seed/php-sdk/grpc-proto-exhaustive/.mock/generators.yml @@ -1,7 +1,6 @@ api: - - path: openapi/openapi.yml - - proto: - root: proto - target: proto/data/v1/data.proto - overrides: overrides.yml - local-generation: true + - proto: + root: proto + target: proto/data/v1/data.proto + overrides: overrides.yml + local-generation: true \ No newline at end of file diff --git a/seed/php-sdk/grpc-proto-exhaustive/.mock/openapi/openapi.yml b/seed/php-sdk/grpc-proto-exhaustive/.mock/openapi/openapi.yml deleted file mode 100644 index ebc23143df3..00000000000 --- a/seed/php-sdk/grpc-proto-exhaustive/.mock/openapi/openapi.yml +++ /dev/null @@ -1,33 +0,0 @@ -openapi: 3.0.3 -info: - title: Test API - version: 1.0.0 -servers: - - url: https://localhost -tags: - - name: dataservice -paths: - /foo: - post: - tag: dataservice - x-fern-sdk-group-name: - - dataservice - x-fern-sdk-method-name: foo - security: - - ApiKeyAuth: [] - operationId: foo - responses: - "200": - content: - application/json: - schema: - type: object - -security: - - ApiKeyAuth: [] -components: - securitySchemes: - ApiKeyAuth: - type: apiKey - in: header - name: X-API-Key diff --git a/seed/php-sdk/grpc-proto-exhaustive/src/Dataservice/DataserviceClient.php b/seed/php-sdk/grpc-proto-exhaustive/src/Dataservice/DataserviceClient.php index 69e79844853..b7c93f9ef7a 100644 --- a/seed/php-sdk/grpc-proto-exhaustive/src/Dataservice/DataserviceClient.php +++ b/seed/php-sdk/grpc-proto-exhaustive/src/Dataservice/DataserviceClient.php @@ -3,16 +3,14 @@ namespace Seed\Dataservice; use Seed\Core\Client\RawClient; +use Seed\Dataservice\Requests\UploadRequest; +use Seed\Types\UploadResponse; use Seed\Exceptions\SeedException; use Seed\Exceptions\SeedApiException; use Seed\Core\Json\JsonApiRequest; -use Seed\Environments; use Seed\Core\Client\HttpMethod; -use Seed\Core\Json\JsonDecoder; use JsonException; use Psr\Http\Client\ClientExceptionInterface; -use Seed\Dataservice\Requests\UploadRequest; -use Seed\Types\UploadResponse; use Seed\Dataservice\Requests\DeleteRequest; use Seed\Types\DeleteResponse; use Seed\Dataservice\Requests\DescribeRequest; @@ -42,41 +40,6 @@ public function __construct( $this->client = $client; } - /** - * @param ?array{ - * baseUrl?: string, - * } $options - * @return array - * @throws SeedException - * @throws SeedApiException - */ - public function foo(?array $options = null): array - { - try { - $response = $this->client->sendRequest( - new JsonApiRequest( - baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? Environments::Default_->value, - path: "foo", - method: HttpMethod::POST, - ), - ); - $statusCode = $response->getStatusCode(); - if ($statusCode >= 200 && $statusCode < 400) { - $json = $response->getBody()->getContents(); - return JsonDecoder::decodeArray($json, ['string' => 'mixed']); // @phpstan-ignore-line - } - } catch (JsonException $e) { - throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); - } 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 UploadRequest $request * @param ?array{ @@ -91,7 +54,7 @@ public function upload(UploadRequest $request, ?array $options = null): UploadRe try { $response = $this->client->sendRequest( new JsonApiRequest( - baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? Environments::Default_->value, + baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '', path: "data", method: HttpMethod::POST, body: $request, @@ -128,7 +91,7 @@ public function delete(DeleteRequest $request, ?array $options = null): DeleteRe try { $response = $this->client->sendRequest( new JsonApiRequest( - baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? Environments::Default_->value, + baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '', path: "data/delete", method: HttpMethod::POST, body: $request, @@ -165,7 +128,7 @@ public function describe(DescribeRequest $request, ?array $options = null): Desc try { $response = $this->client->sendRequest( new JsonApiRequest( - baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? Environments::Default_->value, + baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '', path: "data/describe", method: HttpMethod::POST, body: $request, @@ -209,7 +172,7 @@ public function fetch(FetchRequest $request, ?array $options = null): FetchRespo try { $response = $this->client->sendRequest( new JsonApiRequest( - baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? Environments::Default_->value, + baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '', path: "data/fetch", method: HttpMethod::GET, query: $query, @@ -259,7 +222,7 @@ public function list(ListRequest $request, ?array $options = null): ListResponse try { $response = $this->client->sendRequest( new JsonApiRequest( - baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? Environments::Default_->value, + baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '', path: "data/list", method: HttpMethod::GET, query: $query, @@ -296,7 +259,7 @@ public function query(QueryRequest $request, ?array $options = null): QueryRespo try { $response = $this->client->sendRequest( new JsonApiRequest( - baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? Environments::Default_->value, + baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '', path: "data/query", method: HttpMethod::POST, body: $request, @@ -333,7 +296,7 @@ public function update(UpdateRequest $request, ?array $options = null): UpdateRe try { $response = $this->client->sendRequest( new JsonApiRequest( - baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? Environments::Default_->value, + baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '', path: "data/update", method: HttpMethod::POST, body: $request, diff --git a/seed/php-sdk/grpc-proto-exhaustive/src/Environments.php b/seed/php-sdk/grpc-proto-exhaustive/src/Environments.php deleted file mode 100644 index 8227b311410..00000000000 --- a/seed/php-sdk/grpc-proto-exhaustive/src/Environments.php +++ /dev/null @@ -1,8 +0,0 @@ - 'Seed', 'X-Fern-SDK-Version' => '0.0.1', ]; - if ($apiKey != null) { - $defaultHeaders['X-API-Key'] = $apiKey; - } $this->options = $options ?? []; $this->options['headers'] = array_merge( diff --git a/seed/php-sdk/license/.github/workflows/ci.yml b/seed/php-sdk/license/.github/workflows/ci.yml new file mode 100644 index 00000000000..258bf33a19f --- /dev/null +++ b/seed/php-sdk/license/.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 + + unit-tests: + 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 \ No newline at end of file diff --git a/seed/php-sdk/license/.gitignore b/seed/php-sdk/license/.gitignore new file mode 100644 index 00000000000..f38efc46ade --- /dev/null +++ b/seed/php-sdk/license/.gitignore @@ -0,0 +1,4 @@ +.php-cs-fixer.cache +.phpunit.result.cache +composer.lock +vendor/ \ No newline at end of file diff --git a/seed/php-sdk/license/.mock/definition/__package__.yml b/seed/php-sdk/license/.mock/definition/__package__.yml new file mode 100644 index 00000000000..b1e4d4a878f --- /dev/null +++ b/seed/php-sdk/license/.mock/definition/__package__.yml @@ -0,0 +1,13 @@ +types: + Type: + docs: A simple type with just a name. + properties: + name: string + +service: + auth: false + base-path: / + endpoints: + get: + path: "/" + method: GET diff --git a/seed/php-sdk/license/.mock/definition/api.yml b/seed/php-sdk/license/.mock/definition/api.yml new file mode 100644 index 00000000000..5523ff1f181 --- /dev/null +++ b/seed/php-sdk/license/.mock/definition/api.yml @@ -0,0 +1 @@ +name: license diff --git a/seed/php-sdk/license/.mock/fern.config.json b/seed/php-sdk/license/.mock/fern.config.json new file mode 100644 index 00000000000..4c8e54ac313 --- /dev/null +++ b/seed/php-sdk/license/.mock/fern.config.json @@ -0,0 +1 @@ +{"organization": "fern-test", "version": "*"} \ No newline at end of file diff --git a/seed/php-sdk/license/.mock/generators.yml b/seed/php-sdk/license/.mock/generators.yml new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/seed/php-sdk/license/.mock/generators.yml @@ -0,0 +1 @@ +{} diff --git a/seed/php-sdk/license/composer.json b/seed/php-sdk/license/composer.json new file mode 100644 index 00000000000..5c96c0056e0 --- /dev/null +++ b/seed/php-sdk/license/composer.json @@ -0,0 +1,40 @@ + +{ + "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.9" + }, + "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" + } +} diff --git a/seed/php-sdk/license/phpstan.neon b/seed/php-sdk/license/phpstan.neon new file mode 100644 index 00000000000..29a11a92a19 --- /dev/null +++ b/seed/php-sdk/license/phpstan.neon @@ -0,0 +1,5 @@ +parameters: + level: max + paths: + - src + - tests \ No newline at end of file diff --git a/seed/php-sdk/license/phpunit.xml b/seed/php-sdk/license/phpunit.xml new file mode 100644 index 00000000000..54630a51163 --- /dev/null +++ b/seed/php-sdk/license/phpunit.xml @@ -0,0 +1,7 @@ + + + + tests + + + \ No newline at end of file diff --git a/seed/php-sdk/license/snippet-templates.json b/seed/php-sdk/license/snippet-templates.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/php-sdk/license/snippet.json b/seed/php-sdk/license/snippet.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/php-sdk/license/src/Core/Client/BaseApiRequest.php b/seed/php-sdk/license/src/Core/Client/BaseApiRequest.php new file mode 100644 index 00000000000..5e1283e2b6f --- /dev/null +++ b/seed/php-sdk/license/src/Core/Client/BaseApiRequest.php @@ -0,0 +1,22 @@ + $headers Additional headers for the request (optional) + * @param array $query Query parameters for the request (optional) + */ + public function __construct( + public readonly string $baseUrl, + public readonly string $path, + public readonly HttpMethod $method, + public readonly array $headers = [], + public readonly array $query = [], + ) { + } +} diff --git a/seed/php-sdk/license/src/Core/Client/HttpMethod.php b/seed/php-sdk/license/src/Core/Client/HttpMethod.php new file mode 100644 index 00000000000..b9a3e2d0321 --- /dev/null +++ b/seed/php-sdk/license/src/Core/Client/HttpMethod.php @@ -0,0 +1,12 @@ + $headers + */ + private array $headers; + + /** + * @param ?array{ + * baseUrl?: string, + * client?: ClientInterface, + * headers?: array, + * } $options + */ + public function __construct( + public readonly ?array $options = null, + ) { + $this->client = $this->options['client'] ?? new Client(); + $this->headers = $this->options['headers'] ?? []; + } + + /** + * @throws ClientExceptionInterface + */ + public function sendRequest( + BaseApiRequest $request, + ): ResponseInterface { + $httpRequest = $this->buildRequest($request); + return $this->client->send($httpRequest); + } + + private function buildRequest( + BaseApiRequest $request + ): Request { + $url = $this->buildUrl($request); + $headers = $this->encodeHeaders($request); + $body = $this->encodeRequestBody($request); + return new Request( + $request->method->name, + $url, + $headers, + $body, + ); + } + + /** + * @return array + */ + private function encodeHeaders( + BaseApiRequest $request + ): array { + return match (get_class($request)) { + JsonApiRequest::class => array_merge( + ["Content-Type" => "application/json"], + $this->headers, + $request->headers + ), + MultipartApiRequest::class => array_merge( + $this->headers, + $request->headers + ), + default => throw new InvalidArgumentException('Unsupported request type: ' . get_class($request)), + }; + } + + private function encodeRequestBody( + BaseApiRequest $request + ): ?StreamInterface { + return match (get_class($request)) { + JsonApiRequest::class => $request->body != null ? Utils::streamFor(json_encode($request->body)) : null, + MultipartApiRequest::class => $request->body != null ? new MultipartStream($request->body->toArray()) : null, + default => throw new InvalidArgumentException('Unsupported request type: '.get_class($request)), + }; + } + + private function buildUrl( + BaseApiRequest $request + ): string { + $baseUrl = $request->baseUrl; + $trimmedBaseUrl = rtrim($baseUrl, '/'); + $trimmedBasePath = ltrim($request->path, '/'); + $url = "{$trimmedBaseUrl}/{$trimmedBasePath}"; + + if (!empty($request->query)) { + $url .= '?' . $this->encodeQuery($request->query); + } + + return $url; + } + + /** + * @param array $query + */ + private function encodeQuery( + array $query + ): string { + $parts = []; + foreach ($query as $key => $value) { + if (is_array($value)) { + foreach ($value as $item) { + $parts[] = urlencode($key).'='.$this->encodeQueryValue($item); + } + } else { + $parts[] = urlencode($key).'='.$this->encodeQueryValue($value); + } + } + return implode('&', $parts); + } + + private function encodeQueryValue( + mixed $value + ): string { + if (is_string($value)) { + return urlencode($value); + } + if (is_scalar($value)) { + return urlencode((string)$value); + } + if (is_null($value)) { + return 'null'; + } + // Unreachable, but included for a best effort. + return urlencode(strval(json_encode($value))); + } +} diff --git a/seed/php-sdk/license/src/Core/Json/JsonApiRequest.php b/seed/php-sdk/license/src/Core/Json/JsonApiRequest.php new file mode 100644 index 00000000000..8fdf493606e --- /dev/null +++ b/seed/php-sdk/license/src/Core/Json/JsonApiRequest.php @@ -0,0 +1,28 @@ + $headers Additional headers for the request (optional) + * @param array $query Query parameters for the request (optional) + * @param mixed|null $body The JSON request body (optional) + */ + public function __construct( + string $baseUrl, + string $path, + HttpMethod $method, + array $headers = [], + array $query = [], + public readonly mixed $body = null + ) { + parent::__construct($baseUrl, $path, $method, $headers, $query); + } +} diff --git a/seed/php-sdk/license/src/Core/Json/JsonDecoder.php b/seed/php-sdk/license/src/Core/Json/JsonDecoder.php new file mode 100644 index 00000000000..2ddff027348 --- /dev/null +++ b/seed/php-sdk/license/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-sdk/license/src/Core/Json/JsonDeserializer.php b/seed/php-sdk/license/src/Core/Json/JsonDeserializer.php new file mode 100644 index 00000000000..5f0ca2d7ed0 --- /dev/null +++ b/seed/php-sdk/license/src/Core/Json/JsonDeserializer.php @@ -0,0 +1,204 @@ + $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 (Exception) { + 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-sdk/license/src/Core/Json/JsonEncoder.php b/seed/php-sdk/license/src/Core/Json/JsonEncoder.php new file mode 100644 index 00000000000..0dbf3fcc994 --- /dev/null +++ b/seed/php-sdk/license/src/Core/Json/JsonEncoder.php @@ -0,0 +1,20 @@ +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 = []; + foreach ($reflectionClass->getProperties() as $property) { + $jsonKey = self::getJsonKey($property) ?? $property->getName(); + + if (array_key_exists($jsonKey, $data)) { + $value = $data[$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; + } else { + $defaultValue = $property->getDefaultValue() ?? null; + $args[$property->getName()] = $defaultValue; + } + } + // @phpstan-ignore-next-line + return new static($args); + } + + /** + * 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-sdk/license/src/Core/Json/JsonSerializer.php b/seed/php-sdk/license/src/Core/Json/JsonSerializer.php new file mode 100644 index 00000000000..7dd6fe517af --- /dev/null +++ b/seed/php-sdk/license/src/Core/Json/JsonSerializer.php @@ -0,0 +1,192 @@ +format(Constant::DateFormat); + } + + /** + * Serializes a DateTime object into a string using the date-time format. + * + * @param DateTime $date The DateTime object to serialize. + * @return string The serialized date-time string. + */ + public static function serializeDateTime(DateTime $date): string + { + return $date->format(Constant::DateTimeFormat); + } + + /** + * 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-sdk/license/src/Core/Json/Utils.php b/seed/php-sdk/license/src/Core/Json/Utils.php new file mode 100644 index 00000000000..7577c058916 --- /dev/null +++ b/seed/php-sdk/license/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-sdk/license/src/Core/Multipart/MultipartApiRequest.php b/seed/php-sdk/license/src/Core/Multipart/MultipartApiRequest.php new file mode 100644 index 00000000000..7760366456c --- /dev/null +++ b/seed/php-sdk/license/src/Core/Multipart/MultipartApiRequest.php @@ -0,0 +1,28 @@ + $headers Additional headers for the request (optional) + * @param array $query Query parameters for the request (optional) + * @param ?MultipartFormData $body The multipart form data for the request (optional) + */ + public function __construct( + string $baseUrl, + string $path, + HttpMethod $method, + array $headers = [], + array $query = [], + public readonly ?MultipartFormData $body = null + ) { + parent::__construct($baseUrl, $path, $method, $headers, $query); + } +} diff --git a/seed/php-sdk/license/src/Core/Multipart/MultipartFormData.php b/seed/php-sdk/license/src/Core/Multipart/MultipartFormData.php new file mode 100644 index 00000000000..33bb67b05bd --- /dev/null +++ b/seed/php-sdk/license/src/Core/Multipart/MultipartFormData.php @@ -0,0 +1,61 @@ + + */ + private array $parts = []; + + /** + * Adds a new part to the multipart form data. + * + * @param string $name + * @param string|int|bool|float|StreamInterface $value + * @param ?string $contentType + */ + public function add( + string $name, + string|int|bool|float|StreamInterface $value, + ?string $contentType = null, + ): void { + $headers = $contentType != null ? ['Content-Type' => $contentType] : null; + self::addPart( + new MultipartFormDataPart( + name: $name, + value: $value, + headers: $headers, + ) + ); + } + + /** + * Adds a new part to the multipart form data. + * + * @param MultipartFormDataPart $part + */ + public function addPart(MultipartFormDataPart $part): void + { + $this->parts[] = $part; + } + + /** + * Converts the multipart form data into an array suitable + * for Guzzle's multipart form data. + * + * @return array + * }> + */ + public function toArray(): array + { + return array_map(fn ($part) => $part->toArray(), $this->parts); + } +} diff --git a/seed/php-sdk/license/src/Core/Multipart/MultipartFormDataPart.php b/seed/php-sdk/license/src/Core/Multipart/MultipartFormDataPart.php new file mode 100644 index 00000000000..c158903d84f --- /dev/null +++ b/seed/php-sdk/license/src/Core/Multipart/MultipartFormDataPart.php @@ -0,0 +1,76 @@ + + */ + private ?array $headers; + + /** + * @param string $name + * @param string|bool|float|int|StreamInterface $value + * @param ?string $filename + * @param ?array $headers + */ + public function __construct( + string $name, + string|bool|float|int|StreamInterface $value, + ?string $filename = null, + ?array $headers = null + ) { + $this->name = $name; + $this->contents = Utils::streamFor($value); + $this->filename = $filename; + $this->headers = $headers; + } + + /** + * Converts the multipart form data part into an array suitable + * for Guzzle's multipart form data. + * + * @return array{ + * name: string, + * contents: StreamInterface, + * filename?: string, + * headers?: array + * } + */ + public function toArray(): array + { + $formData = [ + 'name' => $this->name, + 'contents' => $this->contents, + ]; + + if ($this->filename != null) { + $formData['filename'] = $this->filename; + } + + if ($this->headers != null) { + $formData['headers'] = $this->headers; + } + + return $formData; + } +} diff --git a/seed/php-sdk/license/src/Core/Types/ArrayType.php b/seed/php-sdk/license/src/Core/Types/ArrayType.php new file mode 100644 index 00000000000..a26d29008ec --- /dev/null +++ b/seed/php-sdk/license/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-sdk/license/src/Core/Types/Constant.php b/seed/php-sdk/license/src/Core/Types/Constant.php new file mode 100644 index 00000000000..5ac4518cc6d --- /dev/null +++ b/seed/php-sdk/license/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-sdk/license/src/Exceptions/SeedApiException.php b/seed/php-sdk/license/src/Exceptions/SeedApiException.php new file mode 100644 index 00000000000..41a85392b70 --- /dev/null +++ b/seed/php-sdk/license/src/Exceptions/SeedApiException.php @@ -0,0 +1,53 @@ +body = $body; + parent::__construct($message, $statusCode, $previous); + } + + /** + * Returns the body of the response that triggered the exception. + * + * @return mixed + */ + public function getBody(): mixed + { + return $this->body; + } + + /** + * @return string + */ + 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 new file mode 100644 index 00000000000..45703527673 --- /dev/null +++ b/seed/php-sdk/license/src/Exceptions/SeedException.php @@ -0,0 +1,12 @@ +, + * } $options + */ + private ?array $options; + + /** + * @var RawClient $client + */ + private RawClient $client; + + /** + * @param ?array{ + * baseUrl?: string, + * client?: ClientInterface, + * headers?: array, + * } $options + */ + public function __construct( + ?array $options = null, + ) { + $defaultHeaders = [ + 'X-Fern-Language' => 'PHP', + 'X-Fern-SDK-Name' => 'Seed', + 'X-Fern-SDK-Version' => '0.0.1', + ]; + + $this->options = $options ?? []; + $this->options['headers'] = array_merge( + $defaultHeaders, + $this->options['headers'] ?? [], + ); + + $this->client = new RawClient( + options: $this->options, + ); + } + + /** + * @param ?array{ + * baseUrl?: string, + * } $options + * @throws SeedException + * @throws SeedApiException + */ + public function get(?array $options = null): void + { + try { + $response = $this->client->sendRequest( + new JsonApiRequest( + baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '', + path: "/", + method: HttpMethod::GET, + ), + ); + $statusCode = $response->getStatusCode(); + if ($statusCode >= 200 && $statusCode < 400) { + return; + } + } catch (ClientExceptionInterface $e) { + throw new SeedException(message: $e->getMessage(), previous: $e); + } + throw new SeedApiException( + message: 'API request failed', + statusCode: $statusCode, + body: $response->getBody()->getContents(), + ); + } +} diff --git a/seed/php-sdk/license/src/Types/Type.php b/seed/php-sdk/license/src/Types/Type.php new file mode 100644 index 00000000000..851351d8842 --- /dev/null +++ b/seed/php-sdk/license/src/Types/Type.php @@ -0,0 +1,29 @@ +name = $values['name']; + } +} diff --git a/seed/php-sdk/license/src/Utils/File.php b/seed/php-sdk/license/src/Utils/File.php new file mode 100644 index 00000000000..753748138d4 --- /dev/null +++ b/seed/php-sdk/license/src/Utils/File.php @@ -0,0 +1,125 @@ +filename = $filename; + $this->contentType = $contentType; + $this->stream = $stream; + } + + /** + * Creates a File instance from a filepath. + * + * @param string $filepath + * @param ?string $filename + * @param ?string $contentType + * @return File + * @throws Exception + */ + public static function createFromFilepath( + string $filepath, + ?string $filename = null, + ?string $contentType = null, + ): File { + $resource = fopen($filepath, 'r'); + if (!$resource) { + throw new Exception("Unable to open file $filepath"); + } + $stream = Utils::streamFor($resource); + if (!$stream->isReadable()) { + throw new Exception("File $filepath is not readable"); + } + return new self( + stream: $stream, + filename: $filename ?? basename($filepath), + contentType: $contentType, + ); + } + + /** + * Creates a File instance from a string. + * + * @param string $content + * @param ?string $filename + * @param ?string $contentType + * @return File + */ + public static function createFromString( + string $content, + ?string $filename, + ?string $contentType = null, + ): File { + return new self( + stream: Utils::streamFor($content), + filename: $filename, + contentType: $contentType, + ); + } + + /** + * Maps this File into a multipart form data part. + * + * @param string $name The name of the mutlipart form data part. + * @param ?string $contentType Overrides the Content-Type associated with the file, if any. + * @return MultipartFormDataPart + */ + public function toMultipartFormDataPart(string $name, ?string $contentType = null): MultipartFormDataPart + { + $contentType ??= $this->contentType; + $headers = $contentType != null + ? ['Content-Type' => $contentType] + : null; + + return new MultipartFormDataPart( + name: $name, + value: $this->stream, + filename: $this->filename, + headers: $headers, + ); + } + + /** + * Closes the file stream. + */ + public function close(): void + { + $this->stream->close(); + } + + /** + * Destructor to ensure stream is closed. + */ + public function __destruct() + { + $this->close(); + } +} diff --git a/seed/php-sdk/license/tests/Seed/Core/Client/RawClientTest.php b/seed/php-sdk/license/tests/Seed/Core/Client/RawClientTest.php new file mode 100644 index 00000000000..a1052cff3a5 --- /dev/null +++ b/seed/php-sdk/license/tests/Seed/Core/Client/RawClientTest.php @@ -0,0 +1,101 @@ +mockHandler = new MockHandler(); + $handlerStack = HandlerStack::create($this->mockHandler); + $client = new Client(['handler' => $handlerStack]); + $this->rawClient = new RawClient(['client' => $client]); + } + + public function testHeaders(): void + { + $this->mockHandler->append(new Response(200)); + + $request = new JsonApiRequest( + $this->baseUrl, + '/test', + HttpMethod::GET, + ['X-Custom-Header' => 'TestValue'] + ); + + $this->sendRequest($request); + + $lastRequest = $this->mockHandler->getLastRequest(); + assert($lastRequest instanceof RequestInterface); + $this->assertEquals('application/json', $lastRequest->getHeaderLine('Content-Type')); + $this->assertEquals('TestValue', $lastRequest->getHeaderLine('X-Custom-Header')); + } + + public function testQueryParameters(): void + { + $this->mockHandler->append(new Response(200)); + + $request = new JsonApiRequest( + $this->baseUrl, + '/test', + HttpMethod::GET, + [], + ['param1' => 'value1', 'param2' => ['a', 'b'], 'param3' => 'true'] + ); + + $this->sendRequest($request); + + $lastRequest = $this->mockHandler->getLastRequest(); + assert($lastRequest instanceof RequestInterface); + $this->assertEquals( + 'https://api.example.com/test?param1=value1¶m2=a¶m2=b¶m3=true', + (string)$lastRequest->getUri() + ); + } + + public function testJsonBody(): void + { + $this->mockHandler->append(new Response(200)); + + $body = ['key' => 'value']; + $request = new JsonApiRequest( + $this->baseUrl, + '/test', + HttpMethod::POST, + [], + [], + $body + ); + + $this->sendRequest($request); + + $lastRequest = $this->mockHandler->getLastRequest(); + assert($lastRequest instanceof RequestInterface); + $this->assertEquals('application/json', $lastRequest->getHeaderLine('Content-Type')); + $this->assertEquals(json_encode($body), (string)$lastRequest->getBody()); + } + + private function sendRequest(BaseApiRequest $request): void + { + try { + $this->rawClient->sendRequest($request); + } catch (\Throwable $e) { + $this->fail('An exception was thrown: ' . $e->getMessage()); + } + } +} diff --git a/seed/php-sdk/license/tests/Seed/Core/Json/DateArrayTest.php b/seed/php-sdk/license/tests/Seed/Core/Json/DateArrayTest.php new file mode 100644 index 00000000000..a72cfdbdd22 --- /dev/null +++ b/seed/php-sdk/license/tests/Seed/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-sdk/license/tests/Seed/Core/Json/EmptyArrayTest.php b/seed/php-sdk/license/tests/Seed/Core/Json/EmptyArrayTest.php new file mode 100644 index 00000000000..d243a08916d --- /dev/null +++ b/seed/php-sdk/license/tests/Seed/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-sdk/license/tests/Seed/Core/Json/EnumTest.php b/seed/php-sdk/license/tests/Seed/Core/Json/EnumTest.php new file mode 100644 index 00000000000..bf83d5b8ab0 --- /dev/null +++ b/seed/php-sdk/license/tests/Seed/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-sdk/license/tests/Seed/Core/Json/ExhaustiveTest.php b/seed/php-sdk/license/tests/Seed/Core/Json/ExhaustiveTest.php new file mode 100644 index 00000000000..f542d6a535d --- /dev/null +++ b/seed/php-sdk/license/tests/Seed/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:56+00:00', + '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-sdk/license/tests/Seed/Core/Json/InvalidTest.php b/seed/php-sdk/license/tests/Seed/Core/Json/InvalidTest.php new file mode 100644 index 00000000000..7d1d79406a5 --- /dev/null +++ b/seed/php-sdk/license/tests/Seed/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-sdk/license/tests/Seed/Core/Json/NestedUnionArrayTest.php b/seed/php-sdk/license/tests/Seed/Core/Json/NestedUnionArrayTest.php new file mode 100644 index 00000000000..0fcdd06667e --- /dev/null +++ b/seed/php-sdk/license/tests/Seed/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-sdk/license/tests/Seed/Core/Json/NullPropertyTest.php b/seed/php-sdk/license/tests/Seed/Core/Json/NullPropertyTest.php new file mode 100644 index 00000000000..ce20a244282 --- /dev/null +++ b/seed/php-sdk/license/tests/Seed/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-sdk/license/tests/Seed/Core/Json/NullableArrayTest.php b/seed/php-sdk/license/tests/Seed/Core/Json/NullableArrayTest.php new file mode 100644 index 00000000000..fe0f19de6b1 --- /dev/null +++ b/seed/php-sdk/license/tests/Seed/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-sdk/license/tests/Seed/Core/Json/ScalarTest.php b/seed/php-sdk/license/tests/Seed/Core/Json/ScalarTest.php new file mode 100644 index 00000000000..604b7c0b959 --- /dev/null +++ b/seed/php-sdk/license/tests/Seed/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-sdk/license/tests/Seed/Core/Json/TraitTest.php b/seed/php-sdk/license/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..837f239115f --- /dev/null +++ b/seed/php-sdk/license/tests/Seed/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-sdk/license/tests/Seed/Core/Json/UnionArrayTest.php b/seed/php-sdk/license/tests/Seed/Core/Json/UnionArrayTest.php new file mode 100644 index 00000000000..09933d2321d --- /dev/null +++ b/seed/php-sdk/license/tests/Seed/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:00+00:00', + 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-sdk/license/tests/Seed/Core/Json/UnionPropertyTest.php b/seed/php-sdk/license/tests/Seed/Core/Json/UnionPropertyTest.php new file mode 100644 index 00000000000..3119baace62 --- /dev/null +++ b/seed/php-sdk/license/tests/Seed/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/postman/exhaustive/.mock/definition/endpoints/content-type.yml b/seed/postman/exhaustive/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/postman/exhaustive/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/postman/exhaustive/collection.json b/seed/postman/exhaustive/collection.json index 1a1db68e047..82ff509fbac 100644 --- a/seed/postman/exhaustive/collection.json +++ b/seed/postman/exhaustive/collection.json @@ -599,6 +599,173 @@ } ] }, + { + "_type": "container", + "description": null, + "name": "Content Type", + "item": [ + { + "_type": "endpoint", + "name": "Post Json Patch Content Type", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}/foo/bar", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "foo", + "bar" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{\n \"string\": \"string\",\n \"integer\": 1,\n \"long\": 1000000,\n \"double\": 1.1,\n \"bool\": true,\n \"datetime\": \"2024-01-15T09:30:00Z\",\n \"date\": \"2023-01-15\",\n \"uuid\": \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n \"base64\": \"SGVsbG8gd29ybGQh\",\n \"list\": [\n \"list\",\n \"list\"\n ],\n \"set\": [\n \"set\"\n ],\n \"map\": {\n \"1\": \"map\"\n },\n \"bigint\": \"1000000\"\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/foo/bar", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "foo", + "bar" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{\n \"string\": \"string\",\n \"integer\": 1,\n \"long\": 1000000,\n \"double\": 1.1,\n \"bool\": true,\n \"datetime\": \"2024-01-15T09:30:00Z\",\n \"date\": \"2023-01-15\",\n \"uuid\": \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n \"base64\": \"SGVsbG8gd29ybGQh\",\n \"list\": [\n \"list\",\n \"list\"\n ],\n \"set\": [\n \"set\"\n ],\n \"map\": {\n \"1\": \"map\"\n },\n \"bigint\": \"1000000\"\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "description": null, + "body": "", + "_postman_previewlanguage": "json" + } + ] + }, + { + "_type": "endpoint", + "name": "Post Json Patch Content With Charset Type", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}/foo/baz", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "foo", + "baz" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{\n \"string\": \"string\",\n \"integer\": 1,\n \"long\": 1000000,\n \"double\": 1.1,\n \"bool\": true,\n \"datetime\": \"2024-01-15T09:30:00Z\",\n \"date\": \"2023-01-15\",\n \"uuid\": \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n \"base64\": \"SGVsbG8gd29ybGQh\",\n \"list\": [\n \"list\",\n \"list\"\n ],\n \"set\": [\n \"set\"\n ],\n \"map\": {\n \"1\": \"map\"\n },\n \"bigint\": \"1000000\"\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": null, + "url": { + "raw": "{{baseUrl}}/foo/baz", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "foo", + "baz" + ], + "query": [], + "variable": [] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": { + "mode": "raw", + "raw": "{\n \"string\": \"string\",\n \"integer\": 1,\n \"long\": 1000000,\n \"double\": 1.1,\n \"bool\": true,\n \"datetime\": \"2024-01-15T09:30:00Z\",\n \"date\": \"2023-01-15\",\n \"uuid\": \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n \"base64\": \"SGVsbG8gd29ybGQh\",\n \"list\": [\n \"list\",\n \"list\"\n ],\n \"set\": [\n \"set\"\n ],\n \"map\": {\n \"1\": \"map\"\n },\n \"bigint\": \"1000000\"\n}", + "options": { + "raw": { + "language": "json" + } + } + } + }, + "description": null, + "body": "", + "_postman_previewlanguage": "json" + } + ] + } + ] + }, { "_type": "container", "description": null, diff --git a/seed/postman/grpc-proto-exhaustive/.mock/generators.yml b/seed/postman/grpc-proto-exhaustive/.mock/generators.yml index c23323621f2..972ed6d7b73 100644 --- a/seed/postman/grpc-proto-exhaustive/.mock/generators.yml +++ b/seed/postman/grpc-proto-exhaustive/.mock/generators.yml @@ -1,7 +1,6 @@ api: - - path: openapi/openapi.yml - - proto: - root: proto - target: proto/data/v1/data.proto - overrides: overrides.yml - local-generation: true + - proto: + root: proto + target: proto/data/v1/data.proto + overrides: overrides.yml + local-generation: true \ No newline at end of file diff --git a/seed/postman/grpc-proto-exhaustive/.mock/openapi/openapi.yml b/seed/postman/grpc-proto-exhaustive/.mock/openapi/openapi.yml deleted file mode 100644 index ebc23143df3..00000000000 --- a/seed/postman/grpc-proto-exhaustive/.mock/openapi/openapi.yml +++ /dev/null @@ -1,33 +0,0 @@ -openapi: 3.0.3 -info: - title: Test API - version: 1.0.0 -servers: - - url: https://localhost -tags: - - name: dataservice -paths: - /foo: - post: - tag: dataservice - x-fern-sdk-group-name: - - dataservice - x-fern-sdk-method-name: foo - security: - - ApiKeyAuth: [] - operationId: foo - responses: - "200": - content: - application/json: - schema: - type: object - -security: - - ApiKeyAuth: [] -components: - securitySchemes: - ApiKeyAuth: - type: apiKey - in: header - name: X-API-Key diff --git a/seed/postman/grpc-proto-exhaustive/collection.json b/seed/postman/grpc-proto-exhaustive/collection.json index e9ec034ceb1..c0fea25880a 100644 --- a/seed/postman/grpc-proto-exhaustive/collection.json +++ b/seed/postman/grpc-proto-exhaustive/collection.json @@ -1,159 +1,23 @@ { "info": { - "name": "Test API", + "name": "\"\"", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "description": null }, "variable": [ { "key": "baseUrl", - "value": "https://localhost", - "type": "string" - }, - { - "key": "apiKey", "value": "", "type": "string" } ], - "auth": { - "type": "apikey", - "apikey": [ - { - "key": "value", - "value": "{{apiKey}}", - "type": "string" - }, - { - "key": "key", - "value": "X-API-Key", - "type": "string" - }, - { - "key": "in", - "value": "header", - "type": "string" - } - ] - }, + "auth": null, "item": [ { "_type": "container", "description": null, "name": "DataService", "item": [ - { - "_type": "endpoint", - "name": "Foo", - "request": { - "description": null, - "url": { - "raw": "{{baseUrl}}/foo", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "foo" - ], - "query": [], - "variable": [] - }, - "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, - { - "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}}/foo", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "foo" - ], - "query": [], - "variable": [] - }, - "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, - { - "type": "text", - "key": "Content-Type", - "value": "application/json" - } - ], - "method": "POST", - "auth": null, - "body": null - }, - "description": null, - "body": "{\n \"key\": \"value\"\n}", - "_postman_previewlanguage": "json" - }, - { - "name": "Success", - "status": "OK", - "code": 200, - "originalRequest": { - "description": null, - "url": { - "raw": "{{baseUrl}}/foo", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "foo" - ], - "query": [], - "variable": [] - }, - "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, - { - "type": "text", - "key": "Content-Type", - "value": "application/json" - } - ], - "method": "POST", - "auth": null, - "body": null - }, - "description": null, - "body": "{\n \"string\": {\n \"key\": \"value\"\n }\n}", - "_postman_previewlanguage": "json" - } - ] - }, { "_type": "endpoint", "name": "Upload", @@ -171,12 +35,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -214,12 +72,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -260,12 +112,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -308,12 +154,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -352,12 +192,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -399,12 +233,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -447,12 +275,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -491,12 +313,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -538,12 +354,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -586,12 +396,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -622,12 +426,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -672,12 +470,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -712,12 +504,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -748,12 +534,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -808,12 +588,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -848,12 +622,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -892,12 +660,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -939,12 +701,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -987,12 +743,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -1031,12 +781,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", @@ -1078,12 +822,6 @@ "variable": [] }, "header": [ - { - "key": "X-API-Key", - "value": "{{apiKey}}", - "type": "string", - "description": null - }, { "type": "text", "key": "Content-Type", diff --git a/seed/postman/license/.mock/definition/__package__.yml b/seed/postman/license/.mock/definition/__package__.yml new file mode 100644 index 00000000000..b1e4d4a878f --- /dev/null +++ b/seed/postman/license/.mock/definition/__package__.yml @@ -0,0 +1,13 @@ +types: + Type: + docs: A simple type with just a name. + properties: + name: string + +service: + auth: false + base-path: / + endpoints: + get: + path: "/" + method: GET diff --git a/seed/postman/license/.mock/definition/api.yml b/seed/postman/license/.mock/definition/api.yml new file mode 100644 index 00000000000..5523ff1f181 --- /dev/null +++ b/seed/postman/license/.mock/definition/api.yml @@ -0,0 +1 @@ +name: license diff --git a/seed/postman/license/.mock/fern.config.json b/seed/postman/license/.mock/fern.config.json new file mode 100644 index 00000000000..4c8e54ac313 --- /dev/null +++ b/seed/postman/license/.mock/fern.config.json @@ -0,0 +1 @@ +{"organization": "fern-test", "version": "*"} \ No newline at end of file diff --git a/seed/postman/license/.mock/generators.yml b/seed/postman/license/.mock/generators.yml new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/seed/postman/license/.mock/generators.yml @@ -0,0 +1 @@ +{} diff --git a/seed/postman/license/collection.json b/seed/postman/license/collection.json new file mode 100644 index 00000000000..cfcb7be8d47 --- /dev/null +++ b/seed/postman/license/collection.json @@ -0,0 +1,75 @@ +{ + "info": { + "name": "License", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "description": null + }, + "variable": [ + { + "key": "baseUrl", + "value": "", + "type": "string" + } + ], + "auth": null, + "item": [ + { + "_type": "endpoint", + "name": "Get", + "request": { + "description": null, + "url": { + "raw": "{{baseUrl}}", + "host": [ + "{{baseUrl}}" + ], + "path": [], + "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}}", + "host": [ + "{{baseUrl}}" + ], + "path": [], + "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/license/snippet-templates.json b/seed/postman/license/snippet-templates.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/postman/license/snippet.json b/seed/postman/license/snippet.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/ruby-sdk/exhaustive/extra-deps/.mock/definition/endpoints/content-type.yml b/seed/ruby-sdk/exhaustive/extra-deps/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ruby-sdk/exhaustive/extra-deps/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ruby-sdk/exhaustive/extra-deps/lib/fern_exhaustive/endpoints/client.rb b/seed/ruby-sdk/exhaustive/extra-deps/lib/fern_exhaustive/endpoints/client.rb index 68d3097a5d5..0703bd1b1f1 100644 --- a/seed/ruby-sdk/exhaustive/extra-deps/lib/fern_exhaustive/endpoints/client.rb +++ b/seed/ruby-sdk/exhaustive/extra-deps/lib/fern_exhaustive/endpoints/client.rb @@ -2,6 +2,7 @@ require_relative "../../requests" require_relative "container/client" +require_relative "content_type/client" require_relative "enum/client" require_relative "http_methods/client" require_relative "object/client" @@ -14,6 +15,8 @@ module Endpoints class Client # @return [SeedExhaustiveClient::Endpoints::ContainerClient] attr_reader :container + # @return [SeedExhaustiveClient::Endpoints::ContentTypeClient] + attr_reader :content_type # @return [SeedExhaustiveClient::Endpoints::EnumClient] attr_reader :enum # @return [SeedExhaustiveClient::Endpoints::HttpMethodsClient] @@ -31,6 +34,7 @@ class Client # @return [SeedExhaustiveClient::Endpoints::Client] def initialize(request_client:) @container = SeedExhaustiveClient::Endpoints::ContainerClient.new(request_client: request_client) + @content_type = SeedExhaustiveClient::Endpoints::ContentTypeClient.new(request_client: request_client) @enum = SeedExhaustiveClient::Endpoints::EnumClient.new(request_client: request_client) @http_methods = SeedExhaustiveClient::Endpoints::HttpMethodsClient.new(request_client: request_client) @object = SeedExhaustiveClient::Endpoints::ObjectClient.new(request_client: request_client) @@ -43,6 +47,8 @@ def initialize(request_client:) class AsyncClient # @return [SeedExhaustiveClient::Endpoints::AsyncContainerClient] attr_reader :container + # @return [SeedExhaustiveClient::Endpoints::AsyncContentTypeClient] + attr_reader :content_type # @return [SeedExhaustiveClient::Endpoints::AsyncEnumClient] attr_reader :enum # @return [SeedExhaustiveClient::Endpoints::AsyncHttpMethodsClient] @@ -60,6 +66,7 @@ class AsyncClient # @return [SeedExhaustiveClient::Endpoints::AsyncClient] def initialize(request_client:) @container = SeedExhaustiveClient::Endpoints::AsyncContainerClient.new(request_client: request_client) + @content_type = SeedExhaustiveClient::Endpoints::AsyncContentTypeClient.new(request_client: request_client) @enum = SeedExhaustiveClient::Endpoints::AsyncEnumClient.new(request_client: request_client) @http_methods = SeedExhaustiveClient::Endpoints::AsyncHttpMethodsClient.new(request_client: request_client) @object = SeedExhaustiveClient::Endpoints::AsyncObjectClient.new(request_client: request_client) diff --git a/seed/ruby-sdk/exhaustive/extra-deps/lib/fern_exhaustive/endpoints/content_type/client.rb b/seed/ruby-sdk/exhaustive/extra-deps/lib/fern_exhaustive/endpoints/content_type/client.rb new file mode 100644 index 00000000000..ea1a2258975 --- /dev/null +++ b/seed/ruby-sdk/exhaustive/extra-deps/lib/fern_exhaustive/endpoints/content_type/client.rb @@ -0,0 +1,179 @@ +# frozen_string_literal: true + +require_relative "../../../requests" +require_relative "../../types/object/types/object_with_optional_field" +require "async" + +module SeedExhaustiveClient + module Endpoints + class ContentTypeClient + # @return [SeedExhaustiveClient::RequestClient] + attr_reader :request_client + + # @param request_client [SeedExhaustiveClient::RequestClient] + # @return [SeedExhaustiveClient::Endpoints::ContentTypeClient] + def initialize(request_client:) + @request_client = request_client + end + + # @param request [Hash] Request of type SeedExhaustiveClient::Types::Object_::ObjectWithOptionalField, as a Hash + # * :string (String) + # * :integer (Integer) + # * :long (Long) + # * :double (Float) + # * :bool (Boolean) + # * :datetime (DateTime) + # * :date (Date) + # * :uuid (String) + # * :base_64 (String) + # * :list (Array) + # * :set (Set) + # * :map (Hash{Integer => String}) + # * :bigint (String) + # @param request_options [SeedExhaustiveClient::RequestOptions] + # @return [Void] + # @example + # exhaustive = SeedExhaustiveClient::Client.new(base_url: "https://api.example.com", token: "YOUR_AUTH_TOKEN") + # exhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: "string", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse("2024-01-15T09:30:00.000Z"), date: Date.parse("2023-01-15"), uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", base_64: "SGVsbG8gd29ybGQh", list: ["list", "list"], set: Set["set"], map: { 1: "map" }, bigint: "1000000" }) + def post_json_patch_content_type(request:, request_options: nil) + @request_client.conn.post do |req| + req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? + req.headers["Authorization"] = request_options.token unless request_options&.token.nil? + req.headers = { + **(req.headers || {}), + **@request_client.get_headers, + **(request_options&.additional_headers || {}) + }.compact + unless request_options.nil? || request_options&.additional_query_parameters.nil? + req.params = { **(request_options&.additional_query_parameters || {}) }.compact + end + req.body = { **(request || {}), **(request_options&.additional_body_parameters || {}) }.compact + req.url "#{@request_client.get_url(request_options: request_options)}/foo/bar" + end + end + + # @param request [Hash] Request of type SeedExhaustiveClient::Types::Object_::ObjectWithOptionalField, as a Hash + # * :string (String) + # * :integer (Integer) + # * :long (Long) + # * :double (Float) + # * :bool (Boolean) + # * :datetime (DateTime) + # * :date (Date) + # * :uuid (String) + # * :base_64 (String) + # * :list (Array) + # * :set (Set) + # * :map (Hash{Integer => String}) + # * :bigint (String) + # @param request_options [SeedExhaustiveClient::RequestOptions] + # @return [Void] + # @example + # exhaustive = SeedExhaustiveClient::Client.new(base_url: "https://api.example.com", token: "YOUR_AUTH_TOKEN") + # exhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: "string", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse("2024-01-15T09:30:00.000Z"), date: Date.parse("2023-01-15"), uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", base_64: "SGVsbG8gd29ybGQh", list: ["list", "list"], set: Set["set"], map: { 1: "map" }, bigint: "1000000" }) + def post_json_patch_content_with_charset_type(request:, request_options: nil) + @request_client.conn.post do |req| + req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? + req.headers["Authorization"] = request_options.token unless request_options&.token.nil? + req.headers = { + **(req.headers || {}), + **@request_client.get_headers, + **(request_options&.additional_headers || {}) + }.compact + unless request_options.nil? || request_options&.additional_query_parameters.nil? + req.params = { **(request_options&.additional_query_parameters || {}) }.compact + end + req.body = { **(request || {}), **(request_options&.additional_body_parameters || {}) }.compact + req.url "#{@request_client.get_url(request_options: request_options)}/foo/baz" + end + end + end + + class AsyncContentTypeClient + # @return [SeedExhaustiveClient::AsyncRequestClient] + attr_reader :request_client + + # @param request_client [SeedExhaustiveClient::AsyncRequestClient] + # @return [SeedExhaustiveClient::Endpoints::AsyncContentTypeClient] + def initialize(request_client:) + @request_client = request_client + end + + # @param request [Hash] Request of type SeedExhaustiveClient::Types::Object_::ObjectWithOptionalField, as a Hash + # * :string (String) + # * :integer (Integer) + # * :long (Long) + # * :double (Float) + # * :bool (Boolean) + # * :datetime (DateTime) + # * :date (Date) + # * :uuid (String) + # * :base_64 (String) + # * :list (Array) + # * :set (Set) + # * :map (Hash{Integer => String}) + # * :bigint (String) + # @param request_options [SeedExhaustiveClient::RequestOptions] + # @return [Void] + # @example + # exhaustive = SeedExhaustiveClient::Client.new(base_url: "https://api.example.com", token: "YOUR_AUTH_TOKEN") + # exhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: "string", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse("2024-01-15T09:30:00.000Z"), date: Date.parse("2023-01-15"), uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", base_64: "SGVsbG8gd29ybGQh", list: ["list", "list"], set: Set["set"], map: { 1: "map" }, bigint: "1000000" }) + def post_json_patch_content_type(request:, request_options: nil) + Async do + @request_client.conn.post do |req| + req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? + req.headers["Authorization"] = request_options.token unless request_options&.token.nil? + req.headers = { + **(req.headers || {}), + **@request_client.get_headers, + **(request_options&.additional_headers || {}) + }.compact + unless request_options.nil? || request_options&.additional_query_parameters.nil? + req.params = { **(request_options&.additional_query_parameters || {}) }.compact + end + req.body = { **(request || {}), **(request_options&.additional_body_parameters || {}) }.compact + req.url "#{@request_client.get_url(request_options: request_options)}/foo/bar" + end + end + end + + # @param request [Hash] Request of type SeedExhaustiveClient::Types::Object_::ObjectWithOptionalField, as a Hash + # * :string (String) + # * :integer (Integer) + # * :long (Long) + # * :double (Float) + # * :bool (Boolean) + # * :datetime (DateTime) + # * :date (Date) + # * :uuid (String) + # * :base_64 (String) + # * :list (Array) + # * :set (Set) + # * :map (Hash{Integer => String}) + # * :bigint (String) + # @param request_options [SeedExhaustiveClient::RequestOptions] + # @return [Void] + # @example + # exhaustive = SeedExhaustiveClient::Client.new(base_url: "https://api.example.com", token: "YOUR_AUTH_TOKEN") + # exhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: "string", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse("2024-01-15T09:30:00.000Z"), date: Date.parse("2023-01-15"), uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", base_64: "SGVsbG8gd29ybGQh", list: ["list", "list"], set: Set["set"], map: { 1: "map" }, bigint: "1000000" }) + def post_json_patch_content_with_charset_type(request:, request_options: nil) + Async do + @request_client.conn.post do |req| + req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? + req.headers["Authorization"] = request_options.token unless request_options&.token.nil? + req.headers = { + **(req.headers || {}), + **@request_client.get_headers, + **(request_options&.additional_headers || {}) + }.compact + unless request_options.nil? || request_options&.additional_query_parameters.nil? + req.params = { **(request_options&.additional_query_parameters || {}) }.compact + end + req.body = { **(request || {}), **(request_options&.additional_body_parameters || {}) }.compact + req.url "#{@request_client.get_url(request_options: request_options)}/foo/baz" + end + end + end + end + end +end diff --git a/seed/ruby-sdk/exhaustive/extra-deps/snippet.json b/seed/ruby-sdk/exhaustive/extra-deps/snippet.json index 7f40ab4c6bf..9fba4b7a2a3 100644 --- a/seed/ruby-sdk/exhaustive/extra-deps/snippet.json +++ b/seed/ruby-sdk/exhaustive/extra-deps/snippet.json @@ -154,6 +154,50 @@ "type": "ruby" } }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, { "id": { "path": "/enum", @@ -902,6 +946,50 @@ "type": "ruby" } }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, { "id": { "path": "/enum", diff --git a/seed/ruby-sdk/exhaustive/flattened-module-structure/.mock/definition/endpoints/content-type.yml b/seed/ruby-sdk/exhaustive/flattened-module-structure/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ruby-sdk/exhaustive/flattened-module-structure/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ruby-sdk/exhaustive/flattened-module-structure/lib/fern_exhaustive/endpoints/client.rb b/seed/ruby-sdk/exhaustive/flattened-module-structure/lib/fern_exhaustive/endpoints/client.rb index 68d3097a5d5..0703bd1b1f1 100644 --- a/seed/ruby-sdk/exhaustive/flattened-module-structure/lib/fern_exhaustive/endpoints/client.rb +++ b/seed/ruby-sdk/exhaustive/flattened-module-structure/lib/fern_exhaustive/endpoints/client.rb @@ -2,6 +2,7 @@ require_relative "../../requests" require_relative "container/client" +require_relative "content_type/client" require_relative "enum/client" require_relative "http_methods/client" require_relative "object/client" @@ -14,6 +15,8 @@ module Endpoints class Client # @return [SeedExhaustiveClient::Endpoints::ContainerClient] attr_reader :container + # @return [SeedExhaustiveClient::Endpoints::ContentTypeClient] + attr_reader :content_type # @return [SeedExhaustiveClient::Endpoints::EnumClient] attr_reader :enum # @return [SeedExhaustiveClient::Endpoints::HttpMethodsClient] @@ -31,6 +34,7 @@ class Client # @return [SeedExhaustiveClient::Endpoints::Client] def initialize(request_client:) @container = SeedExhaustiveClient::Endpoints::ContainerClient.new(request_client: request_client) + @content_type = SeedExhaustiveClient::Endpoints::ContentTypeClient.new(request_client: request_client) @enum = SeedExhaustiveClient::Endpoints::EnumClient.new(request_client: request_client) @http_methods = SeedExhaustiveClient::Endpoints::HttpMethodsClient.new(request_client: request_client) @object = SeedExhaustiveClient::Endpoints::ObjectClient.new(request_client: request_client) @@ -43,6 +47,8 @@ def initialize(request_client:) class AsyncClient # @return [SeedExhaustiveClient::Endpoints::AsyncContainerClient] attr_reader :container + # @return [SeedExhaustiveClient::Endpoints::AsyncContentTypeClient] + attr_reader :content_type # @return [SeedExhaustiveClient::Endpoints::AsyncEnumClient] attr_reader :enum # @return [SeedExhaustiveClient::Endpoints::AsyncHttpMethodsClient] @@ -60,6 +66,7 @@ class AsyncClient # @return [SeedExhaustiveClient::Endpoints::AsyncClient] def initialize(request_client:) @container = SeedExhaustiveClient::Endpoints::AsyncContainerClient.new(request_client: request_client) + @content_type = SeedExhaustiveClient::Endpoints::AsyncContentTypeClient.new(request_client: request_client) @enum = SeedExhaustiveClient::Endpoints::AsyncEnumClient.new(request_client: request_client) @http_methods = SeedExhaustiveClient::Endpoints::AsyncHttpMethodsClient.new(request_client: request_client) @object = SeedExhaustiveClient::Endpoints::AsyncObjectClient.new(request_client: request_client) diff --git a/seed/ruby-sdk/exhaustive/flattened-module-structure/lib/fern_exhaustive/endpoints/content_type/client.rb b/seed/ruby-sdk/exhaustive/flattened-module-structure/lib/fern_exhaustive/endpoints/content_type/client.rb new file mode 100644 index 00000000000..bb881201293 --- /dev/null +++ b/seed/ruby-sdk/exhaustive/flattened-module-structure/lib/fern_exhaustive/endpoints/content_type/client.rb @@ -0,0 +1,179 @@ +# frozen_string_literal: true + +require_relative "../../../requests" +require_relative "../../types/object/types/object_with_optional_field" +require "async" + +module SeedExhaustiveClient + module Endpoints + class ContentTypeClient + # @return [SeedExhaustiveClient::RequestClient] + attr_reader :request_client + + # @param request_client [SeedExhaustiveClient::RequestClient] + # @return [SeedExhaustiveClient::Endpoints::ContentTypeClient] + def initialize(request_client:) + @request_client = request_client + end + + # @param request [Hash] Request of type SeedExhaustiveClient::Types::Object_::Types::ObjectWithOptionalField, as a Hash + # * :string (String) + # * :integer (Integer) + # * :long (Long) + # * :double (Float) + # * :bool (Boolean) + # * :datetime (DateTime) + # * :date (Date) + # * :uuid (String) + # * :base_64 (String) + # * :list (Array) + # * :set (Set) + # * :map (Hash{Integer => String}) + # * :bigint (String) + # @param request_options [SeedExhaustiveClient::RequestOptions] + # @return [Void] + # @example + # exhaustive = SeedExhaustiveClient::Client.new(base_url: "https://api.example.com", token: "YOUR_AUTH_TOKEN") + # exhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: "string", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse("2024-01-15T09:30:00.000Z"), date: Date.parse("2023-01-15"), uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", base_64: "SGVsbG8gd29ybGQh", list: ["list", "list"], set: Set["set"], map: { 1: "map" }, bigint: "1000000" }) + def post_json_patch_content_type(request:, request_options: nil) + @request_client.conn.post do |req| + req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? + req.headers["Authorization"] = request_options.token unless request_options&.token.nil? + req.headers = { + **(req.headers || {}), + **@request_client.get_headers, + **(request_options&.additional_headers || {}) + }.compact + unless request_options.nil? || request_options&.additional_query_parameters.nil? + req.params = { **(request_options&.additional_query_parameters || {}) }.compact + end + req.body = { **(request || {}), **(request_options&.additional_body_parameters || {}) }.compact + req.url "#{@request_client.get_url(request_options: request_options)}/foo/bar" + end + end + + # @param request [Hash] Request of type SeedExhaustiveClient::Types::Object_::Types::ObjectWithOptionalField, as a Hash + # * :string (String) + # * :integer (Integer) + # * :long (Long) + # * :double (Float) + # * :bool (Boolean) + # * :datetime (DateTime) + # * :date (Date) + # * :uuid (String) + # * :base_64 (String) + # * :list (Array) + # * :set (Set) + # * :map (Hash{Integer => String}) + # * :bigint (String) + # @param request_options [SeedExhaustiveClient::RequestOptions] + # @return [Void] + # @example + # exhaustive = SeedExhaustiveClient::Client.new(base_url: "https://api.example.com", token: "YOUR_AUTH_TOKEN") + # exhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: "string", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse("2024-01-15T09:30:00.000Z"), date: Date.parse("2023-01-15"), uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", base_64: "SGVsbG8gd29ybGQh", list: ["list", "list"], set: Set["set"], map: { 1: "map" }, bigint: "1000000" }) + def post_json_patch_content_with_charset_type(request:, request_options: nil) + @request_client.conn.post do |req| + req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? + req.headers["Authorization"] = request_options.token unless request_options&.token.nil? + req.headers = { + **(req.headers || {}), + **@request_client.get_headers, + **(request_options&.additional_headers || {}) + }.compact + unless request_options.nil? || request_options&.additional_query_parameters.nil? + req.params = { **(request_options&.additional_query_parameters || {}) }.compact + end + req.body = { **(request || {}), **(request_options&.additional_body_parameters || {}) }.compact + req.url "#{@request_client.get_url(request_options: request_options)}/foo/baz" + end + end + end + + class AsyncContentTypeClient + # @return [SeedExhaustiveClient::AsyncRequestClient] + attr_reader :request_client + + # @param request_client [SeedExhaustiveClient::AsyncRequestClient] + # @return [SeedExhaustiveClient::Endpoints::AsyncContentTypeClient] + def initialize(request_client:) + @request_client = request_client + end + + # @param request [Hash] Request of type SeedExhaustiveClient::Types::Object_::Types::ObjectWithOptionalField, as a Hash + # * :string (String) + # * :integer (Integer) + # * :long (Long) + # * :double (Float) + # * :bool (Boolean) + # * :datetime (DateTime) + # * :date (Date) + # * :uuid (String) + # * :base_64 (String) + # * :list (Array) + # * :set (Set) + # * :map (Hash{Integer => String}) + # * :bigint (String) + # @param request_options [SeedExhaustiveClient::RequestOptions] + # @return [Void] + # @example + # exhaustive = SeedExhaustiveClient::Client.new(base_url: "https://api.example.com", token: "YOUR_AUTH_TOKEN") + # exhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: "string", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse("2024-01-15T09:30:00.000Z"), date: Date.parse("2023-01-15"), uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", base_64: "SGVsbG8gd29ybGQh", list: ["list", "list"], set: Set["set"], map: { 1: "map" }, bigint: "1000000" }) + def post_json_patch_content_type(request:, request_options: nil) + Async do + @request_client.conn.post do |req| + req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? + req.headers["Authorization"] = request_options.token unless request_options&.token.nil? + req.headers = { + **(req.headers || {}), + **@request_client.get_headers, + **(request_options&.additional_headers || {}) + }.compact + unless request_options.nil? || request_options&.additional_query_parameters.nil? + req.params = { **(request_options&.additional_query_parameters || {}) }.compact + end + req.body = { **(request || {}), **(request_options&.additional_body_parameters || {}) }.compact + req.url "#{@request_client.get_url(request_options: request_options)}/foo/bar" + end + end + end + + # @param request [Hash] Request of type SeedExhaustiveClient::Types::Object_::Types::ObjectWithOptionalField, as a Hash + # * :string (String) + # * :integer (Integer) + # * :long (Long) + # * :double (Float) + # * :bool (Boolean) + # * :datetime (DateTime) + # * :date (Date) + # * :uuid (String) + # * :base_64 (String) + # * :list (Array) + # * :set (Set) + # * :map (Hash{Integer => String}) + # * :bigint (String) + # @param request_options [SeedExhaustiveClient::RequestOptions] + # @return [Void] + # @example + # exhaustive = SeedExhaustiveClient::Client.new(base_url: "https://api.example.com", token: "YOUR_AUTH_TOKEN") + # exhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: "string", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse("2024-01-15T09:30:00.000Z"), date: Date.parse("2023-01-15"), uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", base_64: "SGVsbG8gd29ybGQh", list: ["list", "list"], set: Set["set"], map: { 1: "map" }, bigint: "1000000" }) + def post_json_patch_content_with_charset_type(request:, request_options: nil) + Async do + @request_client.conn.post do |req| + req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? + req.headers["Authorization"] = request_options.token unless request_options&.token.nil? + req.headers = { + **(req.headers || {}), + **@request_client.get_headers, + **(request_options&.additional_headers || {}) + }.compact + unless request_options.nil? || request_options&.additional_query_parameters.nil? + req.params = { **(request_options&.additional_query_parameters || {}) }.compact + end + req.body = { **(request || {}), **(request_options&.additional_body_parameters || {}) }.compact + req.url "#{@request_client.get_url(request_options: request_options)}/foo/baz" + end + end + end + end + end +end diff --git a/seed/ruby-sdk/exhaustive/flattened-module-structure/snippet.json b/seed/ruby-sdk/exhaustive/flattened-module-structure/snippet.json index 7f40ab4c6bf..9fba4b7a2a3 100644 --- a/seed/ruby-sdk/exhaustive/flattened-module-structure/snippet.json +++ b/seed/ruby-sdk/exhaustive/flattened-module-structure/snippet.json @@ -154,6 +154,50 @@ "type": "ruby" } }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, { "id": { "path": "/enum", @@ -902,6 +946,50 @@ "type": "ruby" } }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, { "id": { "path": "/enum", diff --git a/seed/ruby-sdk/exhaustive/no-custom-config/.mock/definition/endpoints/content-type.yml b/seed/ruby-sdk/exhaustive/no-custom-config/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ruby-sdk/exhaustive/no-custom-config/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ruby-sdk/exhaustive/no-custom-config/lib/fern_exhaustive/endpoints/client.rb b/seed/ruby-sdk/exhaustive/no-custom-config/lib/fern_exhaustive/endpoints/client.rb index 68d3097a5d5..0703bd1b1f1 100644 --- a/seed/ruby-sdk/exhaustive/no-custom-config/lib/fern_exhaustive/endpoints/client.rb +++ b/seed/ruby-sdk/exhaustive/no-custom-config/lib/fern_exhaustive/endpoints/client.rb @@ -2,6 +2,7 @@ require_relative "../../requests" require_relative "container/client" +require_relative "content_type/client" require_relative "enum/client" require_relative "http_methods/client" require_relative "object/client" @@ -14,6 +15,8 @@ module Endpoints class Client # @return [SeedExhaustiveClient::Endpoints::ContainerClient] attr_reader :container + # @return [SeedExhaustiveClient::Endpoints::ContentTypeClient] + attr_reader :content_type # @return [SeedExhaustiveClient::Endpoints::EnumClient] attr_reader :enum # @return [SeedExhaustiveClient::Endpoints::HttpMethodsClient] @@ -31,6 +34,7 @@ class Client # @return [SeedExhaustiveClient::Endpoints::Client] def initialize(request_client:) @container = SeedExhaustiveClient::Endpoints::ContainerClient.new(request_client: request_client) + @content_type = SeedExhaustiveClient::Endpoints::ContentTypeClient.new(request_client: request_client) @enum = SeedExhaustiveClient::Endpoints::EnumClient.new(request_client: request_client) @http_methods = SeedExhaustiveClient::Endpoints::HttpMethodsClient.new(request_client: request_client) @object = SeedExhaustiveClient::Endpoints::ObjectClient.new(request_client: request_client) @@ -43,6 +47,8 @@ def initialize(request_client:) class AsyncClient # @return [SeedExhaustiveClient::Endpoints::AsyncContainerClient] attr_reader :container + # @return [SeedExhaustiveClient::Endpoints::AsyncContentTypeClient] + attr_reader :content_type # @return [SeedExhaustiveClient::Endpoints::AsyncEnumClient] attr_reader :enum # @return [SeedExhaustiveClient::Endpoints::AsyncHttpMethodsClient] @@ -60,6 +66,7 @@ class AsyncClient # @return [SeedExhaustiveClient::Endpoints::AsyncClient] def initialize(request_client:) @container = SeedExhaustiveClient::Endpoints::AsyncContainerClient.new(request_client: request_client) + @content_type = SeedExhaustiveClient::Endpoints::AsyncContentTypeClient.new(request_client: request_client) @enum = SeedExhaustiveClient::Endpoints::AsyncEnumClient.new(request_client: request_client) @http_methods = SeedExhaustiveClient::Endpoints::AsyncHttpMethodsClient.new(request_client: request_client) @object = SeedExhaustiveClient::Endpoints::AsyncObjectClient.new(request_client: request_client) diff --git a/seed/ruby-sdk/exhaustive/no-custom-config/lib/fern_exhaustive/endpoints/content_type/client.rb b/seed/ruby-sdk/exhaustive/no-custom-config/lib/fern_exhaustive/endpoints/content_type/client.rb new file mode 100644 index 00000000000..ea1a2258975 --- /dev/null +++ b/seed/ruby-sdk/exhaustive/no-custom-config/lib/fern_exhaustive/endpoints/content_type/client.rb @@ -0,0 +1,179 @@ +# frozen_string_literal: true + +require_relative "../../../requests" +require_relative "../../types/object/types/object_with_optional_field" +require "async" + +module SeedExhaustiveClient + module Endpoints + class ContentTypeClient + # @return [SeedExhaustiveClient::RequestClient] + attr_reader :request_client + + # @param request_client [SeedExhaustiveClient::RequestClient] + # @return [SeedExhaustiveClient::Endpoints::ContentTypeClient] + def initialize(request_client:) + @request_client = request_client + end + + # @param request [Hash] Request of type SeedExhaustiveClient::Types::Object_::ObjectWithOptionalField, as a Hash + # * :string (String) + # * :integer (Integer) + # * :long (Long) + # * :double (Float) + # * :bool (Boolean) + # * :datetime (DateTime) + # * :date (Date) + # * :uuid (String) + # * :base_64 (String) + # * :list (Array) + # * :set (Set) + # * :map (Hash{Integer => String}) + # * :bigint (String) + # @param request_options [SeedExhaustiveClient::RequestOptions] + # @return [Void] + # @example + # exhaustive = SeedExhaustiveClient::Client.new(base_url: "https://api.example.com", token: "YOUR_AUTH_TOKEN") + # exhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: "string", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse("2024-01-15T09:30:00.000Z"), date: Date.parse("2023-01-15"), uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", base_64: "SGVsbG8gd29ybGQh", list: ["list", "list"], set: Set["set"], map: { 1: "map" }, bigint: "1000000" }) + def post_json_patch_content_type(request:, request_options: nil) + @request_client.conn.post do |req| + req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? + req.headers["Authorization"] = request_options.token unless request_options&.token.nil? + req.headers = { + **(req.headers || {}), + **@request_client.get_headers, + **(request_options&.additional_headers || {}) + }.compact + unless request_options.nil? || request_options&.additional_query_parameters.nil? + req.params = { **(request_options&.additional_query_parameters || {}) }.compact + end + req.body = { **(request || {}), **(request_options&.additional_body_parameters || {}) }.compact + req.url "#{@request_client.get_url(request_options: request_options)}/foo/bar" + end + end + + # @param request [Hash] Request of type SeedExhaustiveClient::Types::Object_::ObjectWithOptionalField, as a Hash + # * :string (String) + # * :integer (Integer) + # * :long (Long) + # * :double (Float) + # * :bool (Boolean) + # * :datetime (DateTime) + # * :date (Date) + # * :uuid (String) + # * :base_64 (String) + # * :list (Array) + # * :set (Set) + # * :map (Hash{Integer => String}) + # * :bigint (String) + # @param request_options [SeedExhaustiveClient::RequestOptions] + # @return [Void] + # @example + # exhaustive = SeedExhaustiveClient::Client.new(base_url: "https://api.example.com", token: "YOUR_AUTH_TOKEN") + # exhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: "string", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse("2024-01-15T09:30:00.000Z"), date: Date.parse("2023-01-15"), uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", base_64: "SGVsbG8gd29ybGQh", list: ["list", "list"], set: Set["set"], map: { 1: "map" }, bigint: "1000000" }) + def post_json_patch_content_with_charset_type(request:, request_options: nil) + @request_client.conn.post do |req| + req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? + req.headers["Authorization"] = request_options.token unless request_options&.token.nil? + req.headers = { + **(req.headers || {}), + **@request_client.get_headers, + **(request_options&.additional_headers || {}) + }.compact + unless request_options.nil? || request_options&.additional_query_parameters.nil? + req.params = { **(request_options&.additional_query_parameters || {}) }.compact + end + req.body = { **(request || {}), **(request_options&.additional_body_parameters || {}) }.compact + req.url "#{@request_client.get_url(request_options: request_options)}/foo/baz" + end + end + end + + class AsyncContentTypeClient + # @return [SeedExhaustiveClient::AsyncRequestClient] + attr_reader :request_client + + # @param request_client [SeedExhaustiveClient::AsyncRequestClient] + # @return [SeedExhaustiveClient::Endpoints::AsyncContentTypeClient] + def initialize(request_client:) + @request_client = request_client + end + + # @param request [Hash] Request of type SeedExhaustiveClient::Types::Object_::ObjectWithOptionalField, as a Hash + # * :string (String) + # * :integer (Integer) + # * :long (Long) + # * :double (Float) + # * :bool (Boolean) + # * :datetime (DateTime) + # * :date (Date) + # * :uuid (String) + # * :base_64 (String) + # * :list (Array) + # * :set (Set) + # * :map (Hash{Integer => String}) + # * :bigint (String) + # @param request_options [SeedExhaustiveClient::RequestOptions] + # @return [Void] + # @example + # exhaustive = SeedExhaustiveClient::Client.new(base_url: "https://api.example.com", token: "YOUR_AUTH_TOKEN") + # exhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: "string", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse("2024-01-15T09:30:00.000Z"), date: Date.parse("2023-01-15"), uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", base_64: "SGVsbG8gd29ybGQh", list: ["list", "list"], set: Set["set"], map: { 1: "map" }, bigint: "1000000" }) + def post_json_patch_content_type(request:, request_options: nil) + Async do + @request_client.conn.post do |req| + req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? + req.headers["Authorization"] = request_options.token unless request_options&.token.nil? + req.headers = { + **(req.headers || {}), + **@request_client.get_headers, + **(request_options&.additional_headers || {}) + }.compact + unless request_options.nil? || request_options&.additional_query_parameters.nil? + req.params = { **(request_options&.additional_query_parameters || {}) }.compact + end + req.body = { **(request || {}), **(request_options&.additional_body_parameters || {}) }.compact + req.url "#{@request_client.get_url(request_options: request_options)}/foo/bar" + end + end + end + + # @param request [Hash] Request of type SeedExhaustiveClient::Types::Object_::ObjectWithOptionalField, as a Hash + # * :string (String) + # * :integer (Integer) + # * :long (Long) + # * :double (Float) + # * :bool (Boolean) + # * :datetime (DateTime) + # * :date (Date) + # * :uuid (String) + # * :base_64 (String) + # * :list (Array) + # * :set (Set) + # * :map (Hash{Integer => String}) + # * :bigint (String) + # @param request_options [SeedExhaustiveClient::RequestOptions] + # @return [Void] + # @example + # exhaustive = SeedExhaustiveClient::Client.new(base_url: "https://api.example.com", token: "YOUR_AUTH_TOKEN") + # exhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: "string", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse("2024-01-15T09:30:00.000Z"), date: Date.parse("2023-01-15"), uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", base_64: "SGVsbG8gd29ybGQh", list: ["list", "list"], set: Set["set"], map: { 1: "map" }, bigint: "1000000" }) + def post_json_patch_content_with_charset_type(request:, request_options: nil) + Async do + @request_client.conn.post do |req| + req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? + req.headers["Authorization"] = request_options.token unless request_options&.token.nil? + req.headers = { + **(req.headers || {}), + **@request_client.get_headers, + **(request_options&.additional_headers || {}) + }.compact + unless request_options.nil? || request_options&.additional_query_parameters.nil? + req.params = { **(request_options&.additional_query_parameters || {}) }.compact + end + req.body = { **(request || {}), **(request_options&.additional_body_parameters || {}) }.compact + req.url "#{@request_client.get_url(request_options: request_options)}/foo/baz" + end + end + end + end + end +end diff --git a/seed/ruby-sdk/exhaustive/no-custom-config/snippet.json b/seed/ruby-sdk/exhaustive/no-custom-config/snippet.json index 7f40ab4c6bf..9fba4b7a2a3 100644 --- a/seed/ruby-sdk/exhaustive/no-custom-config/snippet.json +++ b/seed/ruby-sdk/exhaustive/no-custom-config/snippet.json @@ -154,6 +154,50 @@ "type": "ruby" } }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, { "id": { "path": "/enum", @@ -902,6 +946,50 @@ "type": "ruby" } }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "client": "require \"fern_exhaustive\"\n\nexhaustive = SeedExhaustiveClient::Client.new(base_url: \"https://api.example.com\", token: \"YOUR_AUTH_TOKEN\")\nexhaustive.endpoints.content_type.post_json_patch_content_with_charset_type(request: { string: \"string\", integer: 1, long: 1000000, double: 1.1, bool: true, datetime: DateTime.parse(\"2024-01-15T09:30:00.000Z\"), date: Date.parse(\"2023-01-15\"), uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\", base_64: \"SGVsbG8gd29ybGQh\", list: [\"list\", \"list\"], set: Set[\"set\"], map: { 1: \"map\" }, bigint: \"1000000\" })", + "type": "ruby" + } + }, { "id": { "path": "/enum", diff --git a/seed/ruby-sdk/grpc-proto-exhaustive/.mock/generators.yml b/seed/ruby-sdk/grpc-proto-exhaustive/.mock/generators.yml index c23323621f2..972ed6d7b73 100644 --- a/seed/ruby-sdk/grpc-proto-exhaustive/.mock/generators.yml +++ b/seed/ruby-sdk/grpc-proto-exhaustive/.mock/generators.yml @@ -1,7 +1,6 @@ api: - - path: openapi/openapi.yml - - proto: - root: proto - target: proto/data/v1/data.proto - overrides: overrides.yml - local-generation: true + - proto: + root: proto + target: proto/data/v1/data.proto + overrides: overrides.yml + local-generation: true \ No newline at end of file diff --git a/seed/ruby-sdk/grpc-proto-exhaustive/.mock/openapi/openapi.yml b/seed/ruby-sdk/grpc-proto-exhaustive/.mock/openapi/openapi.yml deleted file mode 100644 index ebc23143df3..00000000000 --- a/seed/ruby-sdk/grpc-proto-exhaustive/.mock/openapi/openapi.yml +++ /dev/null @@ -1,33 +0,0 @@ -openapi: 3.0.3 -info: - title: Test API - version: 1.0.0 -servers: - - url: https://localhost -tags: - - name: dataservice -paths: - /foo: - post: - tag: dataservice - x-fern-sdk-group-name: - - dataservice - x-fern-sdk-method-name: foo - security: - - ApiKeyAuth: [] - operationId: foo - responses: - "200": - content: - application/json: - schema: - type: object - -security: - - ApiKeyAuth: [] -components: - securitySchemes: - ApiKeyAuth: - type: apiKey - in: header - name: X-API-Key diff --git a/seed/ruby-sdk/grpc-proto-exhaustive/lib/environment.rb b/seed/ruby-sdk/grpc-proto-exhaustive/lib/environment.rb deleted file mode 100644 index d22f8c57f0d..00000000000 --- a/seed/ruby-sdk/grpc-proto-exhaustive/lib/environment.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -module SeedApiClient - class Environment - DEFAULT = "https://localhost" - end -end diff --git a/seed/ruby-sdk/grpc-proto-exhaustive/lib/fern_grpc_proto_exhaustive.rb b/seed/ruby-sdk/grpc-proto-exhaustive/lib/fern_grpc_proto_exhaustive.rb index 102b9913a3d..e9cf4dc09e8 100644 --- a/seed/ruby-sdk/grpc-proto-exhaustive/lib/fern_grpc_proto_exhaustive.rb +++ b/seed/ruby-sdk/grpc-proto-exhaustive/lib/fern_grpc_proto_exhaustive.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require_relative "environment" require_relative "types_export" require_relative "requests" require_relative "fern_grpc_proto_exhaustive/dataservice/client" @@ -11,19 +10,14 @@ class Client attr_reader :dataservice # @param base_url [String] - # @param environment [SeedApiClient::Environment] # @param max_retries [Long] The number of times to retry a failed request, defaults to 2. # @param timeout_in_seconds [Long] - # @param api_key [String] # @return [SeedApiClient::Client] - def initialize(api_key:, base_url: nil, environment: SeedApiClient::Environment::DEFAULT, max_retries: nil, - timeout_in_seconds: nil) + def initialize(base_url: nil, max_retries: nil, timeout_in_seconds: nil) @request_client = SeedApiClient::RequestClient.new( base_url: base_url, - environment: environment, max_retries: max_retries, - timeout_in_seconds: timeout_in_seconds, - api_key: api_key + timeout_in_seconds: timeout_in_seconds ) @dataservice = SeedApiClient::DataserviceClient.new(request_client: @request_client) end @@ -34,19 +28,14 @@ class AsyncClient attr_reader :dataservice # @param base_url [String] - # @param environment [SeedApiClient::Environment] # @param max_retries [Long] The number of times to retry a failed request, defaults to 2. # @param timeout_in_seconds [Long] - # @param api_key [String] # @return [SeedApiClient::AsyncClient] - def initialize(api_key:, base_url: nil, environment: SeedApiClient::Environment::DEFAULT, max_retries: nil, - timeout_in_seconds: nil) + def initialize(base_url: nil, max_retries: nil, timeout_in_seconds: nil) @async_request_client = SeedApiClient::AsyncRequestClient.new( base_url: base_url, - environment: environment, max_retries: max_retries, - timeout_in_seconds: timeout_in_seconds, - api_key: api_key + timeout_in_seconds: timeout_in_seconds ) @dataservice = SeedApiClient::AsyncDataserviceClient.new(request_client: @async_request_client) end diff --git a/seed/ruby-sdk/grpc-proto-exhaustive/lib/fern_grpc_proto_exhaustive/dataservice/client.rb b/seed/ruby-sdk/grpc-proto-exhaustive/lib/fern_grpc_proto_exhaustive/dataservice/client.rb index e303d2ecb84..5f593e4c3ce 100644 --- a/seed/ruby-sdk/grpc-proto-exhaustive/lib/fern_grpc_proto_exhaustive/dataservice/client.rb +++ b/seed/ruby-sdk/grpc-proto-exhaustive/lib/fern_grpc_proto_exhaustive/dataservice/client.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require_relative "../../requests" -require "json" require_relative "../types/column" require_relative "../types/upload_response" require_relative "../types/metadata" @@ -26,35 +25,6 @@ def initialize(request_client:) @request_client = request_client end - # @param request_options [SeedApiClient::RequestOptions] - # @return [Hash{String => Object}] - # @example - # api = SeedApiClient::Client.new( - # base_url: "https://api.example.com", - # environment: SeedApiClient::Environment::DEFAULT, - # api_key: "YOUR_API_KEY" - # ) - # api.dataservice.foo - def foo(request_options: nil) - response = @request_client.conn.post do |req| - req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? - req.headers["X-API-Key"] = request_options.api_key unless request_options&.api_key.nil? - req.headers = { - **(req.headers || {}), - **@request_client.get_headers, - **(request_options&.additional_headers || {}) - }.compact - unless request_options.nil? || request_options&.additional_query_parameters.nil? - req.params = { **(request_options&.additional_query_parameters || {}) }.compact - end - unless request_options.nil? || request_options&.additional_body_parameters.nil? - req.body = { **(request_options&.additional_body_parameters || {}) }.compact - end - req.url "#{@request_client.get_url(request_options: request_options)}/foo" - end - JSON.parse(response.body) - end - # @param columns [Array] Request of type Array, as a Hash # * :id (String) # * :values (Array) @@ -66,16 +36,11 @@ def foo(request_options: nil) # @param request_options [SeedApiClient::RequestOptions] # @return [SeedApiClient::UploadResponse] # @example - # api = SeedApiClient::Client.new( - # base_url: "https://api.example.com", - # environment: SeedApiClient::Environment::DEFAULT, - # api_key: "YOUR_API_KEY" - # ) + # api = SeedApiClient::Client.new(base_url: "https://api.example.com") # api.dataservice.upload(columns: [{ id: "id", values: [1.1] }]) def upload(columns:, namespace: nil, request_options: nil) response = @request_client.conn.post do |req| req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? - req.headers["X-API-Key"] = request_options.api_key unless request_options&.api_key.nil? req.headers = { **(req.headers || {}), **@request_client.get_headers, @@ -101,16 +66,11 @@ def upload(columns:, namespace: nil, request_options: nil) # @param request_options [SeedApiClient::RequestOptions] # @return [SeedApiClient::DeleteResponse] # @example - # api = SeedApiClient::Client.new( - # base_url: "https://api.example.com", - # environment: SeedApiClient::Environment::DEFAULT, - # api_key: "YOUR_API_KEY" - # ) + # api = SeedApiClient::Client.new(base_url: "https://api.example.com") # api.dataservice.delete def delete(ids: nil, delete_all: nil, namespace: nil, filter: nil, request_options: nil) response = @request_client.conn.post do |req| req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? - req.headers["X-API-Key"] = request_options.api_key unless request_options&.api_key.nil? req.headers = { **(req.headers || {}), **@request_client.get_headers, @@ -135,16 +95,11 @@ def delete(ids: nil, delete_all: nil, namespace: nil, filter: nil, request_optio # @param request_options [SeedApiClient::RequestOptions] # @return [SeedApiClient::DescribeResponse] # @example - # api = SeedApiClient::Client.new( - # base_url: "https://api.example.com", - # environment: SeedApiClient::Environment::DEFAULT, - # api_key: "YOUR_API_KEY" - # ) + # api = SeedApiClient::Client.new(base_url: "https://api.example.com") # api.dataservice.describe def describe(filter: nil, request_options: nil) response = @request_client.conn.post do |req| req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? - req.headers["X-API-Key"] = request_options.api_key unless request_options&.api_key.nil? req.headers = { **(req.headers || {}), **@request_client.get_headers, @@ -164,16 +119,11 @@ def describe(filter: nil, request_options: nil) # @param request_options [SeedApiClient::RequestOptions] # @return [SeedApiClient::FetchResponse] # @example - # api = SeedApiClient::Client.new( - # base_url: "https://api.example.com", - # environment: SeedApiClient::Environment::DEFAULT, - # api_key: "YOUR_API_KEY" - # ) + # api = SeedApiClient::Client.new(base_url: "https://api.example.com") # api.dataservice.fetch def fetch(ids: nil, namespace: nil, request_options: nil) response = @request_client.conn.get do |req| req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? - req.headers["X-API-Key"] = request_options.api_key unless request_options&.api_key.nil? req.headers = { **(req.headers || {}), **@request_client.get_headers, @@ -199,16 +149,11 @@ def fetch(ids: nil, namespace: nil, request_options: nil) # @param request_options [SeedApiClient::RequestOptions] # @return [SeedApiClient::ListResponse] # @example - # api = SeedApiClient::Client.new( - # base_url: "https://api.example.com", - # environment: SeedApiClient::Environment::DEFAULT, - # api_key: "YOUR_API_KEY" - # ) + # api = SeedApiClient::Client.new(base_url: "https://api.example.com") # api.dataservice.list def list(prefix: nil, limit: nil, pagination_token: nil, namespace: nil, request_options: nil) response = @request_client.conn.get do |req| req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? - req.headers["X-API-Key"] = request_options.api_key unless request_options&.api_key.nil? req.headers = { **(req.headers || {}), **@request_client.get_headers, @@ -250,17 +195,12 @@ def list(prefix: nil, limit: nil, pagination_token: nil, namespace: nil, request # @param request_options [SeedApiClient::RequestOptions] # @return [SeedApiClient::QueryResponse] # @example - # api = SeedApiClient::Client.new( - # base_url: "https://api.example.com", - # environment: SeedApiClient::Environment::DEFAULT, - # api_key: "YOUR_API_KEY" - # ) + # api = SeedApiClient::Client.new(base_url: "https://api.example.com") # api.dataservice.query(top_k: 1) def query(top_k:, namespace: nil, filter: nil, include_values: nil, include_metadata: nil, queries: nil, column: nil, id: nil, indexed_data: nil, request_options: nil) response = @request_client.conn.post do |req| req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? - req.headers["X-API-Key"] = request_options.api_key unless request_options&.api_key.nil? req.headers = { **(req.headers || {}), **@request_client.get_headers, @@ -296,16 +236,11 @@ def query(top_k:, namespace: nil, filter: nil, include_values: nil, include_meta # @param request_options [SeedApiClient::RequestOptions] # @return [SeedApiClient::UpdateResponse] # @example - # api = SeedApiClient::Client.new( - # base_url: "https://api.example.com", - # environment: SeedApiClient::Environment::DEFAULT, - # api_key: "YOUR_API_KEY" - # ) + # api = SeedApiClient::Client.new(base_url: "https://api.example.com") # api.dataservice.update(id: "id") def update(id:, values: nil, set_metadata: nil, namespace: nil, indexed_data: nil, request_options: nil) response = @request_client.conn.post do |req| req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? - req.headers["X-API-Key"] = request_options.api_key unless request_options&.api_key.nil? req.headers = { **(req.headers || {}), **@request_client.get_headers, @@ -338,38 +273,6 @@ def initialize(request_client:) @request_client = request_client end - # @param request_options [SeedApiClient::RequestOptions] - # @return [Hash{String => Object}] - # @example - # api = SeedApiClient::Client.new( - # base_url: "https://api.example.com", - # environment: SeedApiClient::Environment::DEFAULT, - # api_key: "YOUR_API_KEY" - # ) - # api.dataservice.foo - def foo(request_options: nil) - Async do - response = @request_client.conn.post do |req| - req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? - req.headers["X-API-Key"] = request_options.api_key unless request_options&.api_key.nil? - req.headers = { - **(req.headers || {}), - **@request_client.get_headers, - **(request_options&.additional_headers || {}) - }.compact - unless request_options.nil? || request_options&.additional_query_parameters.nil? - req.params = { **(request_options&.additional_query_parameters || {}) }.compact - end - unless request_options.nil? || request_options&.additional_body_parameters.nil? - req.body = { **(request_options&.additional_body_parameters || {}) }.compact - end - req.url "#{@request_client.get_url(request_options: request_options)}/foo" - end - parsed_json = JSON.parse(response.body) - parsed_json - end - end - # @param columns [Array] Request of type Array, as a Hash # * :id (String) # * :values (Array) @@ -381,17 +284,12 @@ def foo(request_options: nil) # @param request_options [SeedApiClient::RequestOptions] # @return [SeedApiClient::UploadResponse] # @example - # api = SeedApiClient::Client.new( - # base_url: "https://api.example.com", - # environment: SeedApiClient::Environment::DEFAULT, - # api_key: "YOUR_API_KEY" - # ) + # api = SeedApiClient::Client.new(base_url: "https://api.example.com") # api.dataservice.upload(columns: [{ id: "id", values: [1.1] }]) def upload(columns:, namespace: nil, request_options: nil) Async do response = @request_client.conn.post do |req| req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? - req.headers["X-API-Key"] = request_options.api_key unless request_options&.api_key.nil? req.headers = { **(req.headers || {}), **@request_client.get_headers, @@ -418,17 +316,12 @@ def upload(columns:, namespace: nil, request_options: nil) # @param request_options [SeedApiClient::RequestOptions] # @return [SeedApiClient::DeleteResponse] # @example - # api = SeedApiClient::Client.new( - # base_url: "https://api.example.com", - # environment: SeedApiClient::Environment::DEFAULT, - # api_key: "YOUR_API_KEY" - # ) + # api = SeedApiClient::Client.new(base_url: "https://api.example.com") # api.dataservice.delete def delete(ids: nil, delete_all: nil, namespace: nil, filter: nil, request_options: nil) Async do response = @request_client.conn.post do |req| req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? - req.headers["X-API-Key"] = request_options.api_key unless request_options&.api_key.nil? req.headers = { **(req.headers || {}), **@request_client.get_headers, @@ -454,17 +347,12 @@ def delete(ids: nil, delete_all: nil, namespace: nil, filter: nil, request_optio # @param request_options [SeedApiClient::RequestOptions] # @return [SeedApiClient::DescribeResponse] # @example - # api = SeedApiClient::Client.new( - # base_url: "https://api.example.com", - # environment: SeedApiClient::Environment::DEFAULT, - # api_key: "YOUR_API_KEY" - # ) + # api = SeedApiClient::Client.new(base_url: "https://api.example.com") # api.dataservice.describe def describe(filter: nil, request_options: nil) Async do response = @request_client.conn.post do |req| req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? - req.headers["X-API-Key"] = request_options.api_key unless request_options&.api_key.nil? req.headers = { **(req.headers || {}), **@request_client.get_headers, @@ -485,17 +373,12 @@ def describe(filter: nil, request_options: nil) # @param request_options [SeedApiClient::RequestOptions] # @return [SeedApiClient::FetchResponse] # @example - # api = SeedApiClient::Client.new( - # base_url: "https://api.example.com", - # environment: SeedApiClient::Environment::DEFAULT, - # api_key: "YOUR_API_KEY" - # ) + # api = SeedApiClient::Client.new(base_url: "https://api.example.com") # api.dataservice.fetch def fetch(ids: nil, namespace: nil, request_options: nil) Async do response = @request_client.conn.get do |req| req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? - req.headers["X-API-Key"] = request_options.api_key unless request_options&.api_key.nil? req.headers = { **(req.headers || {}), **@request_client.get_headers, @@ -522,17 +405,12 @@ def fetch(ids: nil, namespace: nil, request_options: nil) # @param request_options [SeedApiClient::RequestOptions] # @return [SeedApiClient::ListResponse] # @example - # api = SeedApiClient::Client.new( - # base_url: "https://api.example.com", - # environment: SeedApiClient::Environment::DEFAULT, - # api_key: "YOUR_API_KEY" - # ) + # api = SeedApiClient::Client.new(base_url: "https://api.example.com") # api.dataservice.list def list(prefix: nil, limit: nil, pagination_token: nil, namespace: nil, request_options: nil) Async do response = @request_client.conn.get do |req| req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? - req.headers["X-API-Key"] = request_options.api_key unless request_options&.api_key.nil? req.headers = { **(req.headers || {}), **@request_client.get_headers, @@ -575,18 +453,13 @@ def list(prefix: nil, limit: nil, pagination_token: nil, namespace: nil, request # @param request_options [SeedApiClient::RequestOptions] # @return [SeedApiClient::QueryResponse] # @example - # api = SeedApiClient::Client.new( - # base_url: "https://api.example.com", - # environment: SeedApiClient::Environment::DEFAULT, - # api_key: "YOUR_API_KEY" - # ) + # api = SeedApiClient::Client.new(base_url: "https://api.example.com") # api.dataservice.query(top_k: 1) def query(top_k:, namespace: nil, filter: nil, include_values: nil, include_metadata: nil, queries: nil, column: nil, id: nil, indexed_data: nil, request_options: nil) Async do response = @request_client.conn.post do |req| req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? - req.headers["X-API-Key"] = request_options.api_key unless request_options&.api_key.nil? req.headers = { **(req.headers || {}), **@request_client.get_headers, @@ -623,17 +496,12 @@ def query(top_k:, namespace: nil, filter: nil, include_values: nil, include_meta # @param request_options [SeedApiClient::RequestOptions] # @return [SeedApiClient::UpdateResponse] # @example - # api = SeedApiClient::Client.new( - # base_url: "https://api.example.com", - # environment: SeedApiClient::Environment::DEFAULT, - # api_key: "YOUR_API_KEY" - # ) + # api = SeedApiClient::Client.new(base_url: "https://api.example.com") # api.dataservice.update(id: "id") def update(id:, values: nil, set_metadata: nil, namespace: nil, indexed_data: nil, request_options: nil) Async do response = @request_client.conn.post do |req| req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? - req.headers["X-API-Key"] = request_options.api_key unless request_options&.api_key.nil? req.headers = { **(req.headers || {}), **@request_client.get_headers, diff --git a/seed/ruby-sdk/grpc-proto-exhaustive/lib/requests.rb b/seed/ruby-sdk/grpc-proto-exhaustive/lib/requests.rb index d181557796e..e6adb4478dd 100644 --- a/seed/ruby-sdk/grpc-proto-exhaustive/lib/requests.rb +++ b/seed/ruby-sdk/grpc-proto-exhaustive/lib/requests.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require_relative "environment" require "faraday" require "faraday/retry" require "async/http/faraday" @@ -11,22 +10,13 @@ class RequestClient attr_reader :conn # @return [String] attr_reader :base_url - # @return [String] - attr_reader :api_key - # @return [String] - attr_reader :default_environment # @param base_url [String] - # @param environment [SeedApiClient::Environment] # @param max_retries [Long] The number of times to retry a failed request, defaults to 2. # @param timeout_in_seconds [Long] - # @param api_key [String] # @return [SeedApiClient::RequestClient] - def initialize(api_key:, base_url: nil, environment: SeedApiClient::Environment::DEFAULT, max_retries: nil, - timeout_in_seconds: nil) - @default_environment = environment - @base_url = environment || base_url - @api_key = api_key + def initialize(base_url: nil, max_retries: nil, timeout_in_seconds: nil) + @base_url = base_url @conn = Faraday.new do |faraday| faraday.request :json faraday.response :raise_error, include_request: true @@ -38,18 +28,16 @@ def initialize(api_key:, base_url: nil, environment: SeedApiClient::Environment: # @param request_options [SeedApiClient::RequestOptions] # @return [String] def get_url(request_options: nil) - request_options&.base_url || @default_environment || @base_url + request_options&.base_url || @base_url end # @return [Hash{String => String}] def get_headers - headers = { + { "X-Fern-Language": "Ruby", "X-Fern-SDK-Name": "fern_grpc_proto_exhaustive", "X-Fern-SDK-Version": "0.0.1" } - headers["X-API-Key"] = (@api_key.is_a?(Method) ? @api_key.call : @api_key) unless @api_key.nil? - headers end end @@ -58,22 +46,13 @@ class AsyncRequestClient attr_reader :conn # @return [String] attr_reader :base_url - # @return [String] - attr_reader :api_key - # @return [String] - attr_reader :default_environment # @param base_url [String] - # @param environment [SeedApiClient::Environment] # @param max_retries [Long] The number of times to retry a failed request, defaults to 2. # @param timeout_in_seconds [Long] - # @param api_key [String] # @return [SeedApiClient::AsyncRequestClient] - def initialize(api_key:, base_url: nil, environment: SeedApiClient::Environment::DEFAULT, max_retries: nil, - timeout_in_seconds: nil) - @default_environment = environment - @base_url = environment || base_url - @api_key = api_key + def initialize(base_url: nil, max_retries: nil, timeout_in_seconds: nil) + @base_url = base_url @conn = Faraday.new do |faraday| faraday.request :json faraday.response :raise_error, include_request: true @@ -86,18 +65,16 @@ def initialize(api_key:, base_url: nil, environment: SeedApiClient::Environment: # @param request_options [SeedApiClient::RequestOptions] # @return [String] def get_url(request_options: nil) - request_options&.base_url || @default_environment || @base_url + request_options&.base_url || @base_url end # @return [Hash{String => String}] def get_headers - headers = { + { "X-Fern-Language": "Ruby", "X-Fern-SDK-Name": "fern_grpc_proto_exhaustive", "X-Fern-SDK-Version": "0.0.1" } - headers["X-API-Key"] = (@api_key.is_a?(Method) ? @api_key.call : @api_key) unless @api_key.nil? - headers end end @@ -106,8 +83,6 @@ def get_headers class RequestOptions # @return [String] attr_reader :base_url - # @return [String] - attr_reader :api_key # @return [Hash{String => Object}] attr_reader :additional_headers # @return [Hash{String => Object}] @@ -118,16 +93,14 @@ class RequestOptions attr_reader :timeout_in_seconds # @param base_url [String] - # @param api_key [String] # @param additional_headers [Hash{String => Object}] # @param additional_query_parameters [Hash{String => Object}] # @param additional_body_parameters [Hash{String => Object}] # @param timeout_in_seconds [Long] # @return [SeedApiClient::RequestOptions] - def initialize(base_url: nil, api_key: nil, additional_headers: nil, additional_query_parameters: nil, + def initialize(base_url: nil, additional_headers: nil, additional_query_parameters: nil, additional_body_parameters: nil, timeout_in_seconds: nil) @base_url = base_url - @api_key = api_key @additional_headers = additional_headers @additional_query_parameters = additional_query_parameters @additional_body_parameters = additional_body_parameters @@ -140,8 +113,6 @@ def initialize(base_url: nil, api_key: nil, additional_headers: nil, additional_ class IdempotencyRequestOptions # @return [String] attr_reader :base_url - # @return [String] - attr_reader :api_key # @return [Hash{String => Object}] attr_reader :additional_headers # @return [Hash{String => Object}] @@ -152,16 +123,14 @@ class IdempotencyRequestOptions attr_reader :timeout_in_seconds # @param base_url [String] - # @param api_key [String] # @param additional_headers [Hash{String => Object}] # @param additional_query_parameters [Hash{String => Object}] # @param additional_body_parameters [Hash{String => Object}] # @param timeout_in_seconds [Long] # @return [SeedApiClient::IdempotencyRequestOptions] - def initialize(base_url: nil, api_key: nil, additional_headers: nil, additional_query_parameters: nil, + def initialize(base_url: nil, additional_headers: nil, additional_query_parameters: nil, additional_body_parameters: nil, timeout_in_seconds: nil) @base_url = base_url - @api_key = api_key @additional_headers = additional_headers @additional_query_parameters = additional_query_parameters @additional_body_parameters = additional_body_parameters diff --git a/seed/ruby-sdk/grpc-proto-exhaustive/snippet.json b/seed/ruby-sdk/grpc-proto-exhaustive/snippet.json index ebab85334d4..4512da57430 100644 --- a/seed/ruby-sdk/grpc-proto-exhaustive/snippet.json +++ b/seed/ruby-sdk/grpc-proto-exhaustive/snippet.json @@ -1,16 +1,5 @@ { "endpoints": [ - { - "id": { - "path": "/foo", - "method": "POST", - "identifierOverride": "endpoint_dataservice.foo" - }, - "snippet": { - "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(\n base_url: \"https://api.example.com\",\n environment: SeedApiClient::Environment::DEFAULT,\n api_key: \"YOUR_API_KEY\"\n)\napi.dataservice.foo", - "type": "ruby" - } - }, { "id": { "path": "/data", @@ -18,7 +7,7 @@ "identifierOverride": "endpoint_dataservice.upload" }, "snippet": { - "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(\n base_url: \"https://api.example.com\",\n environment: SeedApiClient::Environment::DEFAULT,\n api_key: \"YOUR_API_KEY\"\n)\napi.dataservice.upload(columns: [{ id: \"id\", values: [1.1] }])", + "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(base_url: \"https://api.example.com\")\napi.dataservice.upload(columns: [{ id: \"id\", values: [1.1] }])", "type": "ruby" } }, @@ -29,7 +18,7 @@ "identifierOverride": "endpoint_dataservice.delete" }, "snippet": { - "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(\n base_url: \"https://api.example.com\",\n environment: SeedApiClient::Environment::DEFAULT,\n api_key: \"YOUR_API_KEY\"\n)\napi.dataservice.delete", + "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(base_url: \"https://api.example.com\")\napi.dataservice.delete", "type": "ruby" } }, @@ -40,7 +29,7 @@ "identifierOverride": "endpoint_dataservice.describe" }, "snippet": { - "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(\n base_url: \"https://api.example.com\",\n environment: SeedApiClient::Environment::DEFAULT,\n api_key: \"YOUR_API_KEY\"\n)\napi.dataservice.describe", + "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(base_url: \"https://api.example.com\")\napi.dataservice.describe", "type": "ruby" } }, @@ -51,7 +40,7 @@ "identifierOverride": "endpoint_dataservice.fetch" }, "snippet": { - "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(\n base_url: \"https://api.example.com\",\n environment: SeedApiClient::Environment::DEFAULT,\n api_key: \"YOUR_API_KEY\"\n)\napi.dataservice.fetch", + "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(base_url: \"https://api.example.com\")\napi.dataservice.fetch", "type": "ruby" } }, @@ -62,7 +51,7 @@ "identifierOverride": "endpoint_dataservice.list" }, "snippet": { - "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(\n base_url: \"https://api.example.com\",\n environment: SeedApiClient::Environment::DEFAULT,\n api_key: \"YOUR_API_KEY\"\n)\napi.dataservice.list", + "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(base_url: \"https://api.example.com\")\napi.dataservice.list", "type": "ruby" } }, @@ -73,7 +62,7 @@ "identifierOverride": "endpoint_dataservice.query" }, "snippet": { - "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(\n base_url: \"https://api.example.com\",\n environment: SeedApiClient::Environment::DEFAULT,\n api_key: \"YOUR_API_KEY\"\n)\napi.dataservice.query(top_k: 1)", + "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(base_url: \"https://api.example.com\")\napi.dataservice.query(top_k: 1)", "type": "ruby" } }, @@ -84,18 +73,7 @@ "identifierOverride": "endpoint_dataservice.update" }, "snippet": { - "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(\n base_url: \"https://api.example.com\",\n environment: SeedApiClient::Environment::DEFAULT,\n api_key: \"YOUR_API_KEY\"\n)\napi.dataservice.update(id: \"id\")", - "type": "ruby" - } - }, - { - "id": { - "path": "/foo", - "method": "POST", - "identifierOverride": "endpoint_dataservice.foo" - }, - "snippet": { - "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(\n base_url: \"https://api.example.com\",\n environment: SeedApiClient::Environment::DEFAULT,\n api_key: \"YOUR_API_KEY\"\n)\napi.dataservice.foo", + "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(base_url: \"https://api.example.com\")\napi.dataservice.update(id: \"id\")", "type": "ruby" } }, @@ -106,7 +84,7 @@ "identifierOverride": "endpoint_dataservice.upload" }, "snippet": { - "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(\n base_url: \"https://api.example.com\",\n environment: SeedApiClient::Environment::DEFAULT,\n api_key: \"YOUR_API_KEY\"\n)\napi.dataservice.upload(columns: [{ id: \"id\", values: [1.1] }])", + "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(base_url: \"https://api.example.com\")\napi.dataservice.upload(columns: [{ id: \"id\", values: [1.1] }])", "type": "ruby" } }, @@ -117,7 +95,7 @@ "identifierOverride": "endpoint_dataservice.delete" }, "snippet": { - "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(\n base_url: \"https://api.example.com\",\n environment: SeedApiClient::Environment::DEFAULT,\n api_key: \"YOUR_API_KEY\"\n)\napi.dataservice.delete", + "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(base_url: \"https://api.example.com\")\napi.dataservice.delete", "type": "ruby" } }, @@ -128,7 +106,7 @@ "identifierOverride": "endpoint_dataservice.describe" }, "snippet": { - "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(\n base_url: \"https://api.example.com\",\n environment: SeedApiClient::Environment::DEFAULT,\n api_key: \"YOUR_API_KEY\"\n)\napi.dataservice.describe", + "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(base_url: \"https://api.example.com\")\napi.dataservice.describe", "type": "ruby" } }, @@ -139,7 +117,7 @@ "identifierOverride": "endpoint_dataservice.fetch" }, "snippet": { - "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(\n base_url: \"https://api.example.com\",\n environment: SeedApiClient::Environment::DEFAULT,\n api_key: \"YOUR_API_KEY\"\n)\napi.dataservice.fetch", + "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(base_url: \"https://api.example.com\")\napi.dataservice.fetch", "type": "ruby" } }, @@ -150,7 +128,7 @@ "identifierOverride": "endpoint_dataservice.list" }, "snippet": { - "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(\n base_url: \"https://api.example.com\",\n environment: SeedApiClient::Environment::DEFAULT,\n api_key: \"YOUR_API_KEY\"\n)\napi.dataservice.list", + "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(base_url: \"https://api.example.com\")\napi.dataservice.list", "type": "ruby" } }, @@ -161,7 +139,7 @@ "identifierOverride": "endpoint_dataservice.query" }, "snippet": { - "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(\n base_url: \"https://api.example.com\",\n environment: SeedApiClient::Environment::DEFAULT,\n api_key: \"YOUR_API_KEY\"\n)\napi.dataservice.query(top_k: 1)", + "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(base_url: \"https://api.example.com\")\napi.dataservice.query(top_k: 1)", "type": "ruby" } }, @@ -172,7 +150,7 @@ "identifierOverride": "endpoint_dataservice.update" }, "snippet": { - "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(\n base_url: \"https://api.example.com\",\n environment: SeedApiClient::Environment::DEFAULT,\n api_key: \"YOUR_API_KEY\"\n)\napi.dataservice.update(id: \"id\")", + "client": "require \"fern_grpc_proto_exhaustive\"\n\napi = SeedApiClient::Client.new(base_url: \"https://api.example.com\")\napi.dataservice.update(id: \"id\")", "type": "ruby" } } diff --git a/seed/ruby-sdk/license/.github/workflows/publish.yml b/seed/ruby-sdk/license/.github/workflows/publish.yml new file mode 100644 index 00000000000..5e2ecc90264 --- /dev/null +++ b/seed/ruby-sdk/license/.github/workflows/publish.yml @@ -0,0 +1,26 @@ +name: Publish + +on: [push] +jobs: + publish: + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.7 + bundler-cache: true + + - name: Test gem + run: bundle install && bundle exec rake test + + - name: Build and Push Gem + env: + GEM_HOST_API_KEY: ${{ secrets. }} + run: | + gem build fern_license.gemspec + + gem push fern_license-*.gem --host diff --git a/seed/ruby-sdk/license/.gitignore b/seed/ruby-sdk/license/.gitignore new file mode 100644 index 00000000000..a97c182a2e1 --- /dev/null +++ b/seed/ruby-sdk/license/.gitignore @@ -0,0 +1,10 @@ +/.bundle/ +/.yardoc +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ +*.gem +.env diff --git a/seed/ruby-sdk/license/.mock/definition/__package__.yml b/seed/ruby-sdk/license/.mock/definition/__package__.yml new file mode 100644 index 00000000000..b1e4d4a878f --- /dev/null +++ b/seed/ruby-sdk/license/.mock/definition/__package__.yml @@ -0,0 +1,13 @@ +types: + Type: + docs: A simple type with just a name. + properties: + name: string + +service: + auth: false + base-path: / + endpoints: + get: + path: "/" + method: GET diff --git a/seed/ruby-sdk/license/.mock/definition/api.yml b/seed/ruby-sdk/license/.mock/definition/api.yml new file mode 100644 index 00000000000..5523ff1f181 --- /dev/null +++ b/seed/ruby-sdk/license/.mock/definition/api.yml @@ -0,0 +1 @@ +name: license diff --git a/seed/ruby-sdk/license/.mock/fern.config.json b/seed/ruby-sdk/license/.mock/fern.config.json new file mode 100644 index 00000000000..4c8e54ac313 --- /dev/null +++ b/seed/ruby-sdk/license/.mock/fern.config.json @@ -0,0 +1 @@ +{"organization": "fern-test", "version": "*"} \ No newline at end of file diff --git a/seed/ruby-sdk/license/.mock/generators.yml b/seed/ruby-sdk/license/.mock/generators.yml new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/seed/ruby-sdk/license/.mock/generators.yml @@ -0,0 +1 @@ +{} diff --git a/seed/ruby-sdk/license/.rubocop.yml b/seed/ruby-sdk/license/.rubocop.yml new file mode 100644 index 00000000000..c1d2344d6e6 --- /dev/null +++ b/seed/ruby-sdk/license/.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-sdk/license/Gemfile b/seed/ruby-sdk/license/Gemfile new file mode 100644 index 00000000000..49bd9cd0173 --- /dev/null +++ b/seed/ruby-sdk/license/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-sdk/license/README.md b/seed/ruby-sdk/license/README.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/ruby-sdk/license/Rakefile b/seed/ruby-sdk/license/Rakefile new file mode 100644 index 00000000000..2bdbce0cf2c --- /dev/null +++ b/seed/ruby-sdk/license/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-sdk/license/fern_license.gemspec b/seed/ruby-sdk/license/fern_license.gemspec new file mode 100644 index 00000000000..42e8f684b30 --- /dev/null +++ b/seed/ruby-sdk/license/fern_license.gemspec @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require_relative "lib/gemconfig" + +Gem::Specification.new do |spec| + spec.name = "fern_license" + spec.version = "0.0.1" + spec.authors = SeedLicenseClient::Gemconfig::AUTHORS + spec.email = SeedLicenseClient::Gemconfig::EMAIL + spec.summary = SeedLicenseClient::Gemconfig::SUMMARY + spec.description = SeedLicenseClient::Gemconfig::DESCRIPTION + spec.homepage = SeedLicenseClient::Gemconfig::HOMEPAGE + spec.required_ruby_version = ">= 2.7.0" + spec.metadata["homepage_uri"] = spec.homepage + spec.metadata["source_code_uri"] = SeedLicenseClient::Gemconfig::SOURCE_CODE_URI + spec.metadata["changelog_uri"] = SeedLicenseClient::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"] + spec.add_dependency "async-http-faraday", ">= 0.0", "< 1.0" + spec.add_dependency "faraday", ">= 1.10", "< 3.0" + spec.add_dependency "faraday-net_http", ">= 1.0", "< 4.0" + spec.add_dependency "faraday-retry", ">= 1.0", "< 3.0" +end diff --git a/seed/ruby-sdk/license/lib/fern_license.rb b/seed/ruby-sdk/license/lib/fern_license.rb new file mode 100644 index 00000000000..8e2876f7a3b --- /dev/null +++ b/seed/ruby-sdk/license/lib/fern_license.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require_relative "types_export" +require_relative "requests" + +module SeedLicenseClient + class Client + # @param base_url [String] + # @param max_retries [Long] The number of times to retry a failed request, defaults to 2. + # @param timeout_in_seconds [Long] + # @return [SeedLicenseClient::Client] + def initialize(base_url: nil, max_retries: nil, timeout_in_seconds: nil) + @request_client = SeedLicenseClient::RequestClient.new( + base_url: base_url, + max_retries: max_retries, + timeout_in_seconds: timeout_in_seconds + ) + end + + # @param request_options [SeedLicenseClient::RequestOptions] + # @return [Void] + # @example + # license = SeedLicenseClient::Client.new(base_url: "https://api.example.com") + # license.get + def get(request_options: nil) + @request_client.conn.get do |req| + req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? + req.headers = { + **(req.headers || {}), + **@request_client.get_headers, + **(request_options&.additional_headers || {}) + }.compact + unless request_options.nil? || request_options&.additional_query_parameters.nil? + req.params = { **(request_options&.additional_query_parameters || {}) }.compact + end + unless request_options.nil? || request_options&.additional_body_parameters.nil? + req.body = { **(request_options&.additional_body_parameters || {}) }.compact + end + req.url "#{@request_client.get_url(request_options: request_options)}/" + end + end + end + + class AsyncClient + # @param base_url [String] + # @param max_retries [Long] The number of times to retry a failed request, defaults to 2. + # @param timeout_in_seconds [Long] + # @return [SeedLicenseClient::AsyncClient] + def initialize(base_url: nil, max_retries: nil, timeout_in_seconds: nil) + @async_request_client = SeedLicenseClient::AsyncRequestClient.new( + base_url: base_url, + max_retries: max_retries, + timeout_in_seconds: timeout_in_seconds + ) + end + + # @param request_options [SeedLicenseClient::RequestOptions] + # @return [Void] + # @example + # license = SeedLicenseClient::Client.new(base_url: "https://api.example.com") + # license.get + def get(request_options: nil) + @async_request_client.conn.get do |req| + req.options.timeout = request_options.timeout_in_seconds unless request_options&.timeout_in_seconds.nil? + req.headers = { + **(req.headers || {}), + **@async_request_client.get_headers, + **(request_options&.additional_headers || {}) + }.compact + unless request_options.nil? || request_options&.additional_query_parameters.nil? + req.params = { **(request_options&.additional_query_parameters || {}) }.compact + end + unless request_options.nil? || request_options&.additional_body_parameters.nil? + req.body = { **(request_options&.additional_body_parameters || {}) }.compact + end + req.url "#{@async_request_client.get_url(request_options: request_options)}/" + end + end + end +end diff --git a/seed/ruby-sdk/license/lib/fern_license/types/type.rb b/seed/ruby-sdk/license/lib/fern_license/types/type.rb new file mode 100644 index 00000000000..7a7d65521a3 --- /dev/null +++ b/seed/ruby-sdk/license/lib/fern_license/types/type.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require "ostruct" +require "json" + +module SeedLicenseClient + # A simple type with just a name. + class Type + # @return [String] + attr_reader :name + # @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 name [String] + # @param additional_properties [OpenStruct] Additional properties unmapped to the current class definition + # @return [SeedLicenseClient::Type] + def initialize(name:, additional_properties: nil) + @name = name + @additional_properties = additional_properties + @_field_set = { "name": name } + end + + # Deserialize a JSON object to an instance of Type + # + # @param json_object [String] + # @return [SeedLicenseClient::Type] + def self.from_json(json_object:) + struct = JSON.parse(json_object, object_class: OpenStruct) + parsed_json = JSON.parse(json_object) + name = parsed_json["name"] + new(name: name, additional_properties: struct) + end + + # Serialize an instance of Type 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.name.is_a?(String) != false || raise("Passed value for field obj.name is not the expected type, validation failed.") + end + end +end diff --git a/seed/ruby-sdk/license/lib/gemconfig.rb b/seed/ruby-sdk/license/lib/gemconfig.rb new file mode 100644 index 00000000000..57abf9e4343 --- /dev/null +++ b/seed/ruby-sdk/license/lib/gemconfig.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module SeedLicenseClient + module Gemconfig + VERSION = "" + AUTHORS = [""].freeze + EMAIL = "" + SUMMARY = "" + DESCRIPTION = "" + HOMEPAGE = "https://github.com/license/fern" + SOURCE_CODE_URI = "https://github.com/license/fern" + CHANGELOG_URI = "https://github.com/license/fern/blob/master/CHANGELOG.md" + end +end diff --git a/seed/ruby-sdk/license/lib/requests.rb b/seed/ruby-sdk/license/lib/requests.rb new file mode 100644 index 00000000000..e1ca9d0d831 --- /dev/null +++ b/seed/ruby-sdk/license/lib/requests.rb @@ -0,0 +1,132 @@ +# frozen_string_literal: true + +require "faraday" +require "faraday/retry" +require "async/http/faraday" + +module SeedLicenseClient + class RequestClient + # @return [Faraday] + attr_reader :conn + # @return [String] + attr_reader :base_url + + # @param base_url [String] + # @param max_retries [Long] The number of times to retry a failed request, defaults to 2. + # @param timeout_in_seconds [Long] + # @return [SeedLicenseClient::RequestClient] + def initialize(base_url: nil, max_retries: nil, timeout_in_seconds: nil) + @base_url = base_url + @conn = Faraday.new do |faraday| + faraday.request :json + faraday.response :raise_error, include_request: true + faraday.request :retry, { max: max_retries } unless max_retries.nil? + faraday.options.timeout = timeout_in_seconds unless timeout_in_seconds.nil? + end + end + + # @param request_options [SeedLicenseClient::RequestOptions] + # @return [String] + def get_url(request_options: nil) + request_options&.base_url || @base_url + end + + # @return [Hash{String => String}] + def get_headers + { "X-Fern-Language": "Ruby", "X-Fern-SDK-Name": "fern_license", "X-Fern-SDK-Version": "0.0.1" } + end + end + + class AsyncRequestClient + # @return [Faraday] + attr_reader :conn + # @return [String] + attr_reader :base_url + + # @param base_url [String] + # @param max_retries [Long] The number of times to retry a failed request, defaults to 2. + # @param timeout_in_seconds [Long] + # @return [SeedLicenseClient::AsyncRequestClient] + def initialize(base_url: nil, max_retries: nil, timeout_in_seconds: nil) + @base_url = base_url + @conn = Faraday.new do |faraday| + faraday.request :json + faraday.response :raise_error, include_request: true + faraday.adapter :async_http + faraday.request :retry, { max: max_retries } unless max_retries.nil? + faraday.options.timeout = timeout_in_seconds unless timeout_in_seconds.nil? + end + end + + # @param request_options [SeedLicenseClient::RequestOptions] + # @return [String] + def get_url(request_options: nil) + request_options&.base_url || @base_url + end + + # @return [Hash{String => String}] + def get_headers + { "X-Fern-Language": "Ruby", "X-Fern-SDK-Name": "fern_license", "X-Fern-SDK-Version": "0.0.1" } + end + end + + # Additional options for request-specific configuration when calling APIs via the + # SDK. + class RequestOptions + # @return [String] + attr_reader :base_url + # @return [Hash{String => Object}] + attr_reader :additional_headers + # @return [Hash{String => Object}] + attr_reader :additional_query_parameters + # @return [Hash{String => Object}] + attr_reader :additional_body_parameters + # @return [Long] + attr_reader :timeout_in_seconds + + # @param base_url [String] + # @param additional_headers [Hash{String => Object}] + # @param additional_query_parameters [Hash{String => Object}] + # @param additional_body_parameters [Hash{String => Object}] + # @param timeout_in_seconds [Long] + # @return [SeedLicenseClient::RequestOptions] + def initialize(base_url: nil, additional_headers: nil, additional_query_parameters: nil, + additional_body_parameters: nil, timeout_in_seconds: nil) + @base_url = base_url + @additional_headers = additional_headers + @additional_query_parameters = additional_query_parameters + @additional_body_parameters = additional_body_parameters + @timeout_in_seconds = timeout_in_seconds + end + end + + # Additional options for request-specific configuration when calling APIs via the + # SDK. + class IdempotencyRequestOptions + # @return [String] + attr_reader :base_url + # @return [Hash{String => Object}] + attr_reader :additional_headers + # @return [Hash{String => Object}] + attr_reader :additional_query_parameters + # @return [Hash{String => Object}] + attr_reader :additional_body_parameters + # @return [Long] + attr_reader :timeout_in_seconds + + # @param base_url [String] + # @param additional_headers [Hash{String => Object}] + # @param additional_query_parameters [Hash{String => Object}] + # @param additional_body_parameters [Hash{String => Object}] + # @param timeout_in_seconds [Long] + # @return [SeedLicenseClient::IdempotencyRequestOptions] + def initialize(base_url: nil, additional_headers: nil, additional_query_parameters: nil, + additional_body_parameters: nil, timeout_in_seconds: nil) + @base_url = base_url + @additional_headers = additional_headers + @additional_query_parameters = additional_query_parameters + @additional_body_parameters = additional_body_parameters + @timeout_in_seconds = timeout_in_seconds + end + end +end diff --git a/seed/ruby-sdk/license/lib/types_export.rb b/seed/ruby-sdk/license/lib/types_export.rb new file mode 100644 index 00000000000..d1d9b3ad2e5 --- /dev/null +++ b/seed/ruby-sdk/license/lib/types_export.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require_relative "fern_license/types/type" diff --git a/seed/ruby-sdk/license/snippet-templates.json b/seed/ruby-sdk/license/snippet-templates.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/ruby-sdk/license/snippet.json b/seed/ruby-sdk/license/snippet.json new file mode 100644 index 00000000000..72daf6febdb --- /dev/null +++ b/seed/ruby-sdk/license/snippet.json @@ -0,0 +1,27 @@ +{ + "endpoints": [ + { + "id": { + "path": "/", + "method": "GET", + "identifierOverride": "endpoint_.get" + }, + "snippet": { + "client": "require \"fern_license\"\n\nlicense = SeedLicenseClient::Client.new(base_url: \"https://api.example.com\")\nlicense.get", + "type": "ruby" + } + }, + { + "id": { + "path": "/", + "method": "GET", + "identifierOverride": "endpoint_.get" + }, + "snippet": { + "client": "require \"fern_license\"\n\nlicense = SeedLicenseClient::Client.new(base_url: \"https://api.example.com\")\nlicense.get", + "type": "ruby" + } + } + ], + "types": {} +} \ No newline at end of file diff --git a/seed/ruby-sdk/license/test/test_fern_license.rb b/seed/ruby-sdk/license/test/test_fern_license.rb new file mode 100644 index 00000000000..1dccff08a83 --- /dev/null +++ b/seed/ruby-sdk/license/test/test_fern_license.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require_relative "test_helper" +require "fern_license" + +# Basic SeedLicenseClient tests +class TestSeedLicenseClient < Minitest::Test + def test_function + # SeedLicenseClient::Client.new + end +end diff --git a/seed/ruby-sdk/license/test/test_helper.rb b/seed/ruby-sdk/license/test/test_helper.rb new file mode 100644 index 00000000000..a15f19b167b --- /dev/null +++ b/seed/ruby-sdk/license/test/test_helper.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +$LOAD_PATH.unshift File.expand_path("../lib", __dir__) + +require "minitest/autorun" +require "fern_license" diff --git a/seed/ts-express/exhaustive/allow-extra-fields/.mock/definition/endpoints/content-type.yml b/seed/ts-express/exhaustive/allow-extra-fields/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ts-express/exhaustive/allow-extra-fields/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ts-express/exhaustive/allow-extra-fields/api/resources/endpoints/resources/contentType/index.ts b/seed/ts-express/exhaustive/allow-extra-fields/api/resources/endpoints/resources/contentType/index.ts new file mode 100644 index 00000000000..6261f896364 --- /dev/null +++ b/seed/ts-express/exhaustive/allow-extra-fields/api/resources/endpoints/resources/contentType/index.ts @@ -0,0 +1 @@ +export * from "./service"; diff --git a/seed/ts-express/exhaustive/allow-extra-fields/api/resources/endpoints/resources/contentType/service/ContentTypeService.ts b/seed/ts-express/exhaustive/allow-extra-fields/api/resources/endpoints/resources/contentType/service/ContentTypeService.ts new file mode 100644 index 00000000000..f8f783847a3 --- /dev/null +++ b/seed/ts-express/exhaustive/allow-extra-fields/api/resources/endpoints/resources/contentType/service/ContentTypeService.ts @@ -0,0 +1,129 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as SeedExhaustive from "../../../../../index"; +import express from "express"; +import * as serializers from "../../../../../../serialization/index"; +import * as errors from "../../../../../../errors/index"; + +export interface ContentTypeServiceMethods { + postJsonPatchContentType( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction + ): void | Promise; + postJsonPatchContentWithCharsetType( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction + ): void | Promise; +} + +export class ContentTypeService { + private router; + + constructor(private readonly methods: ContentTypeServiceMethods, 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("/bar", async (req, res, next) => { + const request = serializers.types.ObjectWithOptionalField.parse(req.body); + if (request.ok) { + req.body = request.value; + try { + await this.methods.postJsonPatchContentType( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next + ); + next(); + } catch (error) { + if (error instanceof errors.SeedExhaustiveError) { + console.warn( + `Endpoint 'postJsonPatchContentType' 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("/baz", async (req, res, next) => { + const request = serializers.types.ObjectWithOptionalField.parse(req.body); + if (request.ok) { + req.body = request.value; + try { + await this.methods.postJsonPatchContentWithCharsetType( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next + ); + next(); + } catch (error) { + if (error instanceof errors.SeedExhaustiveError) { + console.warn( + `Endpoint 'postJsonPatchContentWithCharsetType' 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/exhaustive/allow-extra-fields/api/resources/endpoints/resources/contentType/service/index.ts b/seed/ts-express/exhaustive/allow-extra-fields/api/resources/endpoints/resources/contentType/service/index.ts new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/seed/ts-express/exhaustive/allow-extra-fields/api/resources/endpoints/resources/contentType/service/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-express/exhaustive/allow-extra-fields/api/resources/endpoints/resources/index.ts b/seed/ts-express/exhaustive/allow-extra-fields/api/resources/endpoints/resources/index.ts index 633969f6739..745c85151a2 100644 --- a/seed/ts-express/exhaustive/allow-extra-fields/api/resources/endpoints/resources/index.ts +++ b/seed/ts-express/exhaustive/allow-extra-fields/api/resources/endpoints/resources/index.ts @@ -1,4 +1,5 @@ export * as container from "./container"; +export * as contentType from "./contentType"; export * as enum_ from "./enum"; export * as httpMethods from "./httpMethods"; export * as object from "./object"; diff --git a/seed/ts-express/exhaustive/allow-extra-fields/register.ts b/seed/ts-express/exhaustive/allow-extra-fields/register.ts index de149aaf08e..28cf35e18ce 100644 --- a/seed/ts-express/exhaustive/allow-extra-fields/register.ts +++ b/seed/ts-express/exhaustive/allow-extra-fields/register.ts @@ -8,6 +8,7 @@ import { NoAuthService } from "./api/resources/noAuth/service/NoAuthService"; import { NoReqBodyService } from "./api/resources/noReqBody/service/NoReqBodyService"; import { ReqWithHeadersService } from "./api/resources/reqWithHeaders/service/ReqWithHeadersService"; import { ContainerService as endpoints_ContainerService } from "./api/resources/endpoints/resources/container/service/ContainerService"; +import { ContentTypeService as endpoints_ContentTypeService } from "./api/resources/endpoints/resources/contentType/service/ContentTypeService"; import { EnumService as endpoints_EnumService } from "./api/resources/endpoints/resources/enum/service/EnumService"; import { HttpMethodsService as endpoints_HttpMethodsService } from "./api/resources/endpoints/resources/httpMethods/service/HttpMethodsService"; import { ObjectService as endpoints_ObjectService } from "./api/resources/endpoints/resources/object/service/ObjectService"; @@ -24,6 +25,7 @@ export function register( reqWithHeaders: ReqWithHeadersService; endpoints: { container: endpoints_ContainerService; + contentType: endpoints_ContentTypeService; enum: endpoints_EnumService; httpMethods: endpoints_HttpMethodsService; object: endpoints_ObjectService; @@ -34,6 +36,7 @@ export function register( } ): void { (expressApp as any).use("/container", services.endpoints.container.toRouter()); + (expressApp as any).use("/foo", services.endpoints.contentType.toRouter()); (expressApp as any).use("/enum", services.endpoints.enum.toRouter()); (expressApp as any).use("/http-methods", services.endpoints.httpMethods.toRouter()); (expressApp as any).use("/object", services.endpoints.object.toRouter()); diff --git a/seed/ts-express/exhaustive/no-custom-config/.mock/definition/endpoints/content-type.yml b/seed/ts-express/exhaustive/no-custom-config/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ts-express/exhaustive/no-custom-config/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ts-express/exhaustive/no-custom-config/api/resources/endpoints/resources/contentType/index.ts b/seed/ts-express/exhaustive/no-custom-config/api/resources/endpoints/resources/contentType/index.ts new file mode 100644 index 00000000000..6261f896364 --- /dev/null +++ b/seed/ts-express/exhaustive/no-custom-config/api/resources/endpoints/resources/contentType/index.ts @@ -0,0 +1 @@ +export * from "./service"; diff --git a/seed/ts-express/exhaustive/no-custom-config/api/resources/endpoints/resources/contentType/service/ContentTypeService.ts b/seed/ts-express/exhaustive/no-custom-config/api/resources/endpoints/resources/contentType/service/ContentTypeService.ts new file mode 100644 index 00000000000..f8f783847a3 --- /dev/null +++ b/seed/ts-express/exhaustive/no-custom-config/api/resources/endpoints/resources/contentType/service/ContentTypeService.ts @@ -0,0 +1,129 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as SeedExhaustive from "../../../../../index"; +import express from "express"; +import * as serializers from "../../../../../../serialization/index"; +import * as errors from "../../../../../../errors/index"; + +export interface ContentTypeServiceMethods { + postJsonPatchContentType( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction + ): void | Promise; + postJsonPatchContentWithCharsetType( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction + ): void | Promise; +} + +export class ContentTypeService { + private router; + + constructor(private readonly methods: ContentTypeServiceMethods, 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("/bar", async (req, res, next) => { + const request = serializers.types.ObjectWithOptionalField.parse(req.body); + if (request.ok) { + req.body = request.value; + try { + await this.methods.postJsonPatchContentType( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next + ); + next(); + } catch (error) { + if (error instanceof errors.SeedExhaustiveError) { + console.warn( + `Endpoint 'postJsonPatchContentType' 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("/baz", async (req, res, next) => { + const request = serializers.types.ObjectWithOptionalField.parse(req.body); + if (request.ok) { + req.body = request.value; + try { + await this.methods.postJsonPatchContentWithCharsetType( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next + ); + next(); + } catch (error) { + if (error instanceof errors.SeedExhaustiveError) { + console.warn( + `Endpoint 'postJsonPatchContentWithCharsetType' 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/exhaustive/no-custom-config/api/resources/endpoints/resources/contentType/service/index.ts b/seed/ts-express/exhaustive/no-custom-config/api/resources/endpoints/resources/contentType/service/index.ts new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/seed/ts-express/exhaustive/no-custom-config/api/resources/endpoints/resources/contentType/service/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-express/exhaustive/no-custom-config/api/resources/endpoints/resources/index.ts b/seed/ts-express/exhaustive/no-custom-config/api/resources/endpoints/resources/index.ts index 633969f6739..745c85151a2 100644 --- a/seed/ts-express/exhaustive/no-custom-config/api/resources/endpoints/resources/index.ts +++ b/seed/ts-express/exhaustive/no-custom-config/api/resources/endpoints/resources/index.ts @@ -1,4 +1,5 @@ export * as container from "./container"; +export * as contentType from "./contentType"; export * as enum_ from "./enum"; export * as httpMethods from "./httpMethods"; export * as object from "./object"; diff --git a/seed/ts-express/exhaustive/no-custom-config/register.ts b/seed/ts-express/exhaustive/no-custom-config/register.ts index de149aaf08e..28cf35e18ce 100644 --- a/seed/ts-express/exhaustive/no-custom-config/register.ts +++ b/seed/ts-express/exhaustive/no-custom-config/register.ts @@ -8,6 +8,7 @@ import { NoAuthService } from "./api/resources/noAuth/service/NoAuthService"; import { NoReqBodyService } from "./api/resources/noReqBody/service/NoReqBodyService"; import { ReqWithHeadersService } from "./api/resources/reqWithHeaders/service/ReqWithHeadersService"; import { ContainerService as endpoints_ContainerService } from "./api/resources/endpoints/resources/container/service/ContainerService"; +import { ContentTypeService as endpoints_ContentTypeService } from "./api/resources/endpoints/resources/contentType/service/ContentTypeService"; import { EnumService as endpoints_EnumService } from "./api/resources/endpoints/resources/enum/service/EnumService"; import { HttpMethodsService as endpoints_HttpMethodsService } from "./api/resources/endpoints/resources/httpMethods/service/HttpMethodsService"; import { ObjectService as endpoints_ObjectService } from "./api/resources/endpoints/resources/object/service/ObjectService"; @@ -24,6 +25,7 @@ export function register( reqWithHeaders: ReqWithHeadersService; endpoints: { container: endpoints_ContainerService; + contentType: endpoints_ContentTypeService; enum: endpoints_EnumService; httpMethods: endpoints_HttpMethodsService; object: endpoints_ObjectService; @@ -34,6 +36,7 @@ export function register( } ): void { (expressApp as any).use("/container", services.endpoints.container.toRouter()); + (expressApp as any).use("/foo", services.endpoints.contentType.toRouter()); (expressApp as any).use("/enum", services.endpoints.enum.toRouter()); (expressApp as any).use("/http-methods", services.endpoints.httpMethods.toRouter()); (expressApp as any).use("/object", services.endpoints.object.toRouter()); diff --git a/seed/ts-express/exhaustive/no-optional-properties/.mock/definition/endpoints/content-type.yml b/seed/ts-express/exhaustive/no-optional-properties/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ts-express/exhaustive/no-optional-properties/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ts-express/exhaustive/no-optional-properties/api/resources/endpoints/resources/contentType/index.ts b/seed/ts-express/exhaustive/no-optional-properties/api/resources/endpoints/resources/contentType/index.ts new file mode 100644 index 00000000000..6261f896364 --- /dev/null +++ b/seed/ts-express/exhaustive/no-optional-properties/api/resources/endpoints/resources/contentType/index.ts @@ -0,0 +1 @@ +export * from "./service"; diff --git a/seed/ts-express/exhaustive/no-optional-properties/api/resources/endpoints/resources/contentType/service/ContentTypeService.ts b/seed/ts-express/exhaustive/no-optional-properties/api/resources/endpoints/resources/contentType/service/ContentTypeService.ts new file mode 100644 index 00000000000..f8f783847a3 --- /dev/null +++ b/seed/ts-express/exhaustive/no-optional-properties/api/resources/endpoints/resources/contentType/service/ContentTypeService.ts @@ -0,0 +1,129 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as SeedExhaustive from "../../../../../index"; +import express from "express"; +import * as serializers from "../../../../../../serialization/index"; +import * as errors from "../../../../../../errors/index"; + +export interface ContentTypeServiceMethods { + postJsonPatchContentType( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction + ): void | Promise; + postJsonPatchContentWithCharsetType( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction + ): void | Promise; +} + +export class ContentTypeService { + private router; + + constructor(private readonly methods: ContentTypeServiceMethods, 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("/bar", async (req, res, next) => { + const request = serializers.types.ObjectWithOptionalField.parse(req.body); + if (request.ok) { + req.body = request.value; + try { + await this.methods.postJsonPatchContentType( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next + ); + next(); + } catch (error) { + if (error instanceof errors.SeedExhaustiveError) { + console.warn( + `Endpoint 'postJsonPatchContentType' 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("/baz", async (req, res, next) => { + const request = serializers.types.ObjectWithOptionalField.parse(req.body); + if (request.ok) { + req.body = request.value; + try { + await this.methods.postJsonPatchContentWithCharsetType( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next + ); + next(); + } catch (error) { + if (error instanceof errors.SeedExhaustiveError) { + console.warn( + `Endpoint 'postJsonPatchContentWithCharsetType' 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/exhaustive/no-optional-properties/api/resources/endpoints/resources/contentType/service/index.ts b/seed/ts-express/exhaustive/no-optional-properties/api/resources/endpoints/resources/contentType/service/index.ts new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/seed/ts-express/exhaustive/no-optional-properties/api/resources/endpoints/resources/contentType/service/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-express/exhaustive/no-optional-properties/api/resources/endpoints/resources/index.ts b/seed/ts-express/exhaustive/no-optional-properties/api/resources/endpoints/resources/index.ts index 633969f6739..745c85151a2 100644 --- a/seed/ts-express/exhaustive/no-optional-properties/api/resources/endpoints/resources/index.ts +++ b/seed/ts-express/exhaustive/no-optional-properties/api/resources/endpoints/resources/index.ts @@ -1,4 +1,5 @@ export * as container from "./container"; +export * as contentType from "./contentType"; export * as enum_ from "./enum"; export * as httpMethods from "./httpMethods"; export * as object from "./object"; diff --git a/seed/ts-express/exhaustive/no-optional-properties/register.ts b/seed/ts-express/exhaustive/no-optional-properties/register.ts index de149aaf08e..28cf35e18ce 100644 --- a/seed/ts-express/exhaustive/no-optional-properties/register.ts +++ b/seed/ts-express/exhaustive/no-optional-properties/register.ts @@ -8,6 +8,7 @@ import { NoAuthService } from "./api/resources/noAuth/service/NoAuthService"; import { NoReqBodyService } from "./api/resources/noReqBody/service/NoReqBodyService"; import { ReqWithHeadersService } from "./api/resources/reqWithHeaders/service/ReqWithHeadersService"; import { ContainerService as endpoints_ContainerService } from "./api/resources/endpoints/resources/container/service/ContainerService"; +import { ContentTypeService as endpoints_ContentTypeService } from "./api/resources/endpoints/resources/contentType/service/ContentTypeService"; import { EnumService as endpoints_EnumService } from "./api/resources/endpoints/resources/enum/service/EnumService"; import { HttpMethodsService as endpoints_HttpMethodsService } from "./api/resources/endpoints/resources/httpMethods/service/HttpMethodsService"; import { ObjectService as endpoints_ObjectService } from "./api/resources/endpoints/resources/object/service/ObjectService"; @@ -24,6 +25,7 @@ export function register( reqWithHeaders: ReqWithHeadersService; endpoints: { container: endpoints_ContainerService; + contentType: endpoints_ContentTypeService; enum: endpoints_EnumService; httpMethods: endpoints_HttpMethodsService; object: endpoints_ObjectService; @@ -34,6 +36,7 @@ export function register( } ): void { (expressApp as any).use("/container", services.endpoints.container.toRouter()); + (expressApp as any).use("/foo", services.endpoints.contentType.toRouter()); (expressApp as any).use("/enum", services.endpoints.enum.toRouter()); (expressApp as any).use("/http-methods", services.endpoints.httpMethods.toRouter()); (expressApp as any).use("/object", services.endpoints.object.toRouter()); diff --git a/seed/ts-express/exhaustive/retain-original-casing/.mock/definition/endpoints/content-type.yml b/seed/ts-express/exhaustive/retain-original-casing/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ts-express/exhaustive/retain-original-casing/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ts-express/exhaustive/retain-original-casing/api/resources/endpoints/resources/contentType/index.ts b/seed/ts-express/exhaustive/retain-original-casing/api/resources/endpoints/resources/contentType/index.ts new file mode 100644 index 00000000000..6261f896364 --- /dev/null +++ b/seed/ts-express/exhaustive/retain-original-casing/api/resources/endpoints/resources/contentType/index.ts @@ -0,0 +1 @@ +export * from "./service"; diff --git a/seed/ts-express/exhaustive/retain-original-casing/api/resources/endpoints/resources/contentType/service/ContentTypeService.ts b/seed/ts-express/exhaustive/retain-original-casing/api/resources/endpoints/resources/contentType/service/ContentTypeService.ts new file mode 100644 index 00000000000..f8f783847a3 --- /dev/null +++ b/seed/ts-express/exhaustive/retain-original-casing/api/resources/endpoints/resources/contentType/service/ContentTypeService.ts @@ -0,0 +1,129 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as SeedExhaustive from "../../../../../index"; +import express from "express"; +import * as serializers from "../../../../../../serialization/index"; +import * as errors from "../../../../../../errors/index"; + +export interface ContentTypeServiceMethods { + postJsonPatchContentType( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction + ): void | Promise; + postJsonPatchContentWithCharsetType( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction + ): void | Promise; +} + +export class ContentTypeService { + private router; + + constructor(private readonly methods: ContentTypeServiceMethods, 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("/bar", async (req, res, next) => { + const request = serializers.types.ObjectWithOptionalField.parse(req.body); + if (request.ok) { + req.body = request.value; + try { + await this.methods.postJsonPatchContentType( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next + ); + next(); + } catch (error) { + if (error instanceof errors.SeedExhaustiveError) { + console.warn( + `Endpoint 'postJsonPatchContentType' 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("/baz", async (req, res, next) => { + const request = serializers.types.ObjectWithOptionalField.parse(req.body); + if (request.ok) { + req.body = request.value; + try { + await this.methods.postJsonPatchContentWithCharsetType( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next + ); + next(); + } catch (error) { + if (error instanceof errors.SeedExhaustiveError) { + console.warn( + `Endpoint 'postJsonPatchContentWithCharsetType' 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/exhaustive/retain-original-casing/api/resources/endpoints/resources/contentType/service/index.ts b/seed/ts-express/exhaustive/retain-original-casing/api/resources/endpoints/resources/contentType/service/index.ts new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/seed/ts-express/exhaustive/retain-original-casing/api/resources/endpoints/resources/contentType/service/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-express/exhaustive/retain-original-casing/api/resources/endpoints/resources/index.ts b/seed/ts-express/exhaustive/retain-original-casing/api/resources/endpoints/resources/index.ts index 633969f6739..745c85151a2 100644 --- a/seed/ts-express/exhaustive/retain-original-casing/api/resources/endpoints/resources/index.ts +++ b/seed/ts-express/exhaustive/retain-original-casing/api/resources/endpoints/resources/index.ts @@ -1,4 +1,5 @@ export * as container from "./container"; +export * as contentType from "./contentType"; export * as enum_ from "./enum"; export * as httpMethods from "./httpMethods"; export * as object from "./object"; diff --git a/seed/ts-express/exhaustive/retain-original-casing/register.ts b/seed/ts-express/exhaustive/retain-original-casing/register.ts index de149aaf08e..28cf35e18ce 100644 --- a/seed/ts-express/exhaustive/retain-original-casing/register.ts +++ b/seed/ts-express/exhaustive/retain-original-casing/register.ts @@ -8,6 +8,7 @@ import { NoAuthService } from "./api/resources/noAuth/service/NoAuthService"; import { NoReqBodyService } from "./api/resources/noReqBody/service/NoReqBodyService"; import { ReqWithHeadersService } from "./api/resources/reqWithHeaders/service/ReqWithHeadersService"; import { ContainerService as endpoints_ContainerService } from "./api/resources/endpoints/resources/container/service/ContainerService"; +import { ContentTypeService as endpoints_ContentTypeService } from "./api/resources/endpoints/resources/contentType/service/ContentTypeService"; import { EnumService as endpoints_EnumService } from "./api/resources/endpoints/resources/enum/service/EnumService"; import { HttpMethodsService as endpoints_HttpMethodsService } from "./api/resources/endpoints/resources/httpMethods/service/HttpMethodsService"; import { ObjectService as endpoints_ObjectService } from "./api/resources/endpoints/resources/object/service/ObjectService"; @@ -24,6 +25,7 @@ export function register( reqWithHeaders: ReqWithHeadersService; endpoints: { container: endpoints_ContainerService; + contentType: endpoints_ContentTypeService; enum: endpoints_EnumService; httpMethods: endpoints_HttpMethodsService; object: endpoints_ObjectService; @@ -34,6 +36,7 @@ export function register( } ): void { (expressApp as any).use("/container", services.endpoints.container.toRouter()); + (expressApp as any).use("/foo", services.endpoints.contentType.toRouter()); (expressApp as any).use("/enum", services.endpoints.enum.toRouter()); (expressApp as any).use("/http-methods", services.endpoints.httpMethods.toRouter()); (expressApp as any).use("/object", services.endpoints.object.toRouter()); diff --git a/seed/ts-express/exhaustive/union-utils/.mock/definition/endpoints/content-type.yml b/seed/ts-express/exhaustive/union-utils/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ts-express/exhaustive/union-utils/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ts-express/exhaustive/union-utils/api/resources/endpoints/resources/contentType/index.ts b/seed/ts-express/exhaustive/union-utils/api/resources/endpoints/resources/contentType/index.ts new file mode 100644 index 00000000000..6261f896364 --- /dev/null +++ b/seed/ts-express/exhaustive/union-utils/api/resources/endpoints/resources/contentType/index.ts @@ -0,0 +1 @@ +export * from "./service"; diff --git a/seed/ts-express/exhaustive/union-utils/api/resources/endpoints/resources/contentType/service/ContentTypeService.ts b/seed/ts-express/exhaustive/union-utils/api/resources/endpoints/resources/contentType/service/ContentTypeService.ts new file mode 100644 index 00000000000..97f792313bf --- /dev/null +++ b/seed/ts-express/exhaustive/union-utils/api/resources/endpoints/resources/contentType/service/ContentTypeService.ts @@ -0,0 +1,125 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as SeedExhaustive from "../../../../../index"; +import express from "express"; +import * as serializers from "../../../../../../serialization/index"; +import * as errors from "../../../../../../errors/index"; + +export interface ContentTypeServiceMethods { + postJsonPatchContentType( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction + ): void | Promise; + postJsonPatchContentWithCharsetType( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction + ): void | Promise; +} + +export class ContentTypeService { + private router; + + constructor(private readonly methods: ContentTypeServiceMethods, 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("/bar", async (req, res, next) => { + const request = serializers.types.ObjectWithOptionalField.parse(req.body); + if (request.ok) { + req.body = request.value; + try { + await this.methods.postJsonPatchContentType( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next + ); + next(); + } catch (error) { + if (error instanceof errors.SeedExhaustiveError) { + console.warn( + `Endpoint 'postJsonPatchContentType' 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); + } + next(error); + } + } else { + res.status(422).json({ + errors: request.errors.map( + (error) => ["request", ...error.path].join(" -> ") + ": " + error.message + ), + }); + next(request.errors); + } + }); + this.router.post("/baz", async (req, res, next) => { + const request = serializers.types.ObjectWithOptionalField.parse(req.body); + if (request.ok) { + req.body = request.value; + try { + await this.methods.postJsonPatchContentWithCharsetType( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next + ); + next(); + } catch (error) { + if (error instanceof errors.SeedExhaustiveError) { + console.warn( + `Endpoint 'postJsonPatchContentWithCharsetType' 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); + } + 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/exhaustive/union-utils/api/resources/endpoints/resources/contentType/service/index.ts b/seed/ts-express/exhaustive/union-utils/api/resources/endpoints/resources/contentType/service/index.ts new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/seed/ts-express/exhaustive/union-utils/api/resources/endpoints/resources/contentType/service/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-express/exhaustive/union-utils/api/resources/endpoints/resources/index.ts b/seed/ts-express/exhaustive/union-utils/api/resources/endpoints/resources/index.ts index 633969f6739..745c85151a2 100644 --- a/seed/ts-express/exhaustive/union-utils/api/resources/endpoints/resources/index.ts +++ b/seed/ts-express/exhaustive/union-utils/api/resources/endpoints/resources/index.ts @@ -1,4 +1,5 @@ export * as container from "./container"; +export * as contentType from "./contentType"; export * as enum_ from "./enum"; export * as httpMethods from "./httpMethods"; export * as object from "./object"; diff --git a/seed/ts-express/exhaustive/union-utils/register.ts b/seed/ts-express/exhaustive/union-utils/register.ts index de149aaf08e..28cf35e18ce 100644 --- a/seed/ts-express/exhaustive/union-utils/register.ts +++ b/seed/ts-express/exhaustive/union-utils/register.ts @@ -8,6 +8,7 @@ import { NoAuthService } from "./api/resources/noAuth/service/NoAuthService"; import { NoReqBodyService } from "./api/resources/noReqBody/service/NoReqBodyService"; import { ReqWithHeadersService } from "./api/resources/reqWithHeaders/service/ReqWithHeadersService"; import { ContainerService as endpoints_ContainerService } from "./api/resources/endpoints/resources/container/service/ContainerService"; +import { ContentTypeService as endpoints_ContentTypeService } from "./api/resources/endpoints/resources/contentType/service/ContentTypeService"; import { EnumService as endpoints_EnumService } from "./api/resources/endpoints/resources/enum/service/EnumService"; import { HttpMethodsService as endpoints_HttpMethodsService } from "./api/resources/endpoints/resources/httpMethods/service/HttpMethodsService"; import { ObjectService as endpoints_ObjectService } from "./api/resources/endpoints/resources/object/service/ObjectService"; @@ -24,6 +25,7 @@ export function register( reqWithHeaders: ReqWithHeadersService; endpoints: { container: endpoints_ContainerService; + contentType: endpoints_ContentTypeService; enum: endpoints_EnumService; httpMethods: endpoints_HttpMethodsService; object: endpoints_ObjectService; @@ -34,6 +36,7 @@ export function register( } ): void { (expressApp as any).use("/container", services.endpoints.container.toRouter()); + (expressApp as any).use("/foo", services.endpoints.contentType.toRouter()); (expressApp as any).use("/enum", services.endpoints.enum.toRouter()); (expressApp as any).use("/http-methods", services.endpoints.httpMethods.toRouter()); (expressApp as any).use("/object", services.endpoints.object.toRouter()); diff --git a/seed/ts-express/license/.mock/definition/__package__.yml b/seed/ts-express/license/.mock/definition/__package__.yml new file mode 100644 index 00000000000..b1e4d4a878f --- /dev/null +++ b/seed/ts-express/license/.mock/definition/__package__.yml @@ -0,0 +1,13 @@ +types: + Type: + docs: A simple type with just a name. + properties: + name: string + +service: + auth: false + base-path: / + endpoints: + get: + path: "/" + method: GET diff --git a/seed/ts-express/license/.mock/definition/api.yml b/seed/ts-express/license/.mock/definition/api.yml new file mode 100644 index 00000000000..5523ff1f181 --- /dev/null +++ b/seed/ts-express/license/.mock/definition/api.yml @@ -0,0 +1 @@ +name: license diff --git a/seed/ts-express/license/.mock/fern.config.json b/seed/ts-express/license/.mock/fern.config.json new file mode 100644 index 00000000000..4c8e54ac313 --- /dev/null +++ b/seed/ts-express/license/.mock/fern.config.json @@ -0,0 +1 @@ +{"organization": "fern-test", "version": "*"} \ No newline at end of file diff --git a/seed/ts-express/license/.mock/generators.yml b/seed/ts-express/license/.mock/generators.yml new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/seed/ts-express/license/.mock/generators.yml @@ -0,0 +1 @@ +{} diff --git a/seed/ts-express/license/api/index.ts b/seed/ts-express/license/api/index.ts new file mode 100644 index 00000000000..fcc81debec4 --- /dev/null +++ b/seed/ts-express/license/api/index.ts @@ -0,0 +1,2 @@ +export * from "./types"; +export * from "./service"; diff --git a/seed/ts-express/license/api/service/RootService.ts b/seed/ts-express/license/api/service/RootService.ts new file mode 100644 index 00000000000..5a579962bb8 --- /dev/null +++ b/seed/ts-express/license/api/service/RootService.ts @@ -0,0 +1,68 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import express from "express"; +import * as errors from "../../errors/index"; + +export interface RootServiceMethods { + get( + req: express.Request, + res: { + send: () => Promise; + cookie: (cookie: string, value: string, options?: express.CookieOptions) => void; + locals: any; + }, + next: express.NextFunction + ): void | Promise; +} + +export class RootService { + private router; + + constructor(private readonly methods: RootServiceMethods, 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("/", async (req, res, next) => { + try { + await this.methods.get( + req as any, + { + send: async () => { + res.sendStatus(204); + }, + cookie: res.cookie.bind(res), + locals: res.locals, + }, + next + ); + next(); + } catch (error) { + if (error instanceof errors.SeedLicenseError) { + console.warn( + `Endpoint 'get' 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/license/api/service/index.ts b/seed/ts-express/license/api/service/index.ts new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/seed/ts-express/license/api/service/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-express/license/api/types/Type.ts b/seed/ts-express/license/api/types/Type.ts new file mode 100644 index 00000000000..cf5fb80f4d8 --- /dev/null +++ b/seed/ts-express/license/api/types/Type.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * A simple type with just a name. + */ +export interface Type { + name: string; +} diff --git a/seed/ts-express/license/api/types/index.ts b/seed/ts-express/license/api/types/index.ts new file mode 100644 index 00000000000..3edf389a5d5 --- /dev/null +++ b/seed/ts-express/license/api/types/index.ts @@ -0,0 +1 @@ +export * from "./Type"; diff --git a/seed/ts-express/license/core/index.ts b/seed/ts-express/license/core/index.ts new file mode 100644 index 00000000000..3ae53c06d38 --- /dev/null +++ b/seed/ts-express/license/core/index.ts @@ -0,0 +1 @@ +export * as serialization from "./schemas"; diff --git a/seed/ts-express/license/core/schemas/Schema.ts b/seed/ts-express/license/core/schemas/Schema.ts new file mode 100644 index 00000000000..2a72eacec99 --- /dev/null +++ b/seed/ts-express/license/core/schemas/Schema.ts @@ -0,0 +1,99 @@ +import { SchemaUtils } from "./builders"; + +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", + RECORD: "record", + SET: "set", + UNION: "union", + UNDISCRIMINATED_UNION: "undiscriminatedUnion", + OPTIONAL: "optional", +} 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/license/core/schemas/builders/bigint/bigint.ts b/seed/ts-express/license/core/schemas/builders/bigint/bigint.ts new file mode 100644 index 00000000000..dc9c742e007 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/bigint/bigint.ts @@ -0,0 +1,50 @@ +import { BaseSchema, Schema, SchemaType } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils"; + +export function bigint(): Schema { + const baseSchema: BaseSchema = { + parse: (raw, { breadcrumbsPrefix = [] } = {}) => { + if (typeof raw !== "string") { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(raw, "string"), + }, + ], + }; + } + return { + ok: true, + value: BigInt(raw), + }; + }, + json: (bigint, { breadcrumbsPrefix = [] } = {}) => { + if (typeof bigint === "bigint") { + return { + ok: true, + value: bigint.toString(), + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(bigint, "bigint"), + }, + ], + }; + } + }, + getType: () => SchemaType.BIGINT, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} diff --git a/seed/ts-express/license/core/schemas/builders/bigint/index.ts b/seed/ts-express/license/core/schemas/builders/bigint/index.ts new file mode 100644 index 00000000000..e5843043fcb --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/bigint/index.ts @@ -0,0 +1 @@ +export { bigint } from "./bigint"; diff --git a/seed/ts-express/license/core/schemas/builders/date/date.ts b/seed/ts-express/license/core/schemas/builders/date/date.ts new file mode 100644 index 00000000000..b70f24b045a --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/date/date.ts @@ -0,0 +1,65 @@ +import { BaseSchema, Schema, SchemaType } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils"; + +// 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/license/core/schemas/builders/date/index.ts b/seed/ts-express/license/core/schemas/builders/date/index.ts new file mode 100644 index 00000000000..187b29040f6 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/date/index.ts @@ -0,0 +1 @@ +export { date } from "./date"; diff --git a/seed/ts-express/license/core/schemas/builders/enum/enum.ts b/seed/ts-express/license/core/schemas/builders/enum/enum.ts new file mode 100644 index 00000000000..c1e24d69dec --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/enum/enum.ts @@ -0,0 +1,43 @@ +import { 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/license/core/schemas/builders/enum/index.ts b/seed/ts-express/license/core/schemas/builders/enum/index.ts new file mode 100644 index 00000000000..fe6faed93e3 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/enum/index.ts @@ -0,0 +1 @@ +export { enum_ } from "./enum"; diff --git a/seed/ts-express/license/core/schemas/builders/index.ts b/seed/ts-express/license/core/schemas/builders/index.ts new file mode 100644 index 00000000000..65211f92522 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/index.ts @@ -0,0 +1,14 @@ +export * from "./bigint"; +export * from "./date"; +export * from "./enum"; +export * from "./lazy"; +export * from "./list"; +export * from "./literals"; +export * from "./object"; +export * from "./object-like"; +export * from "./primitives"; +export * from "./record"; +export * from "./schema-utils"; +export * from "./set"; +export * from "./undiscriminated-union"; +export * from "./union"; diff --git a/seed/ts-express/license/core/schemas/builders/lazy/index.ts b/seed/ts-express/license/core/schemas/builders/lazy/index.ts new file mode 100644 index 00000000000..77420fb031c --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/lazy/index.ts @@ -0,0 +1,3 @@ +export { lazy } from "./lazy"; +export type { SchemaGetter } from "./lazy"; +export { lazyObject } from "./lazyObject"; diff --git a/seed/ts-express/license/core/schemas/builders/lazy/lazy.ts b/seed/ts-express/license/core/schemas/builders/lazy/lazy.ts new file mode 100644 index 00000000000..835c61f8a56 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/lazy/lazy.ts @@ -0,0 +1,32 @@ +import { BaseSchema, Schema } from "../../Schema"; +import { getSchemaUtils } from "../schema-utils"; + +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/license/core/schemas/builders/lazy/lazyObject.ts b/seed/ts-express/license/core/schemas/builders/lazy/lazyObject.ts new file mode 100644 index 00000000000..38c9e28404b --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/lazy/lazyObject.ts @@ -0,0 +1,20 @@ +import { getObjectUtils } from "../object"; +import { getObjectLikeUtils } from "../object-like"; +import { BaseObjectSchema, ObjectSchema } from "../object/types"; +import { getSchemaUtils } from "../schema-utils"; +import { constructLazyBaseSchema, getMemoizedSchema, 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/license/core/schemas/builders/list/index.ts b/seed/ts-express/license/core/schemas/builders/list/index.ts new file mode 100644 index 00000000000..25f4bcc1737 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/list/index.ts @@ -0,0 +1 @@ +export { list } from "./list"; diff --git a/seed/ts-express/license/core/schemas/builders/list/list.ts b/seed/ts-express/license/core/schemas/builders/list/list.ts new file mode 100644 index 00000000000..e4c5c4a4a99 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/list/list.ts @@ -0,0 +1,73 @@ +import { BaseSchema, MaybeValid, Schema, SchemaType, ValidationError } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils"; + +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/license/core/schemas/builders/literals/booleanLiteral.ts b/seed/ts-express/license/core/schemas/builders/literals/booleanLiteral.ts new file mode 100644 index 00000000000..a83d22cd48a --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/literals/booleanLiteral.ts @@ -0,0 +1,29 @@ +import { 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/license/core/schemas/builders/literals/index.ts b/seed/ts-express/license/core/schemas/builders/literals/index.ts new file mode 100644 index 00000000000..d2bf08fc6ca --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/literals/index.ts @@ -0,0 +1,2 @@ +export { stringLiteral } from "./stringLiteral"; +export { booleanLiteral } from "./booleanLiteral"; diff --git a/seed/ts-express/license/core/schemas/builders/literals/stringLiteral.ts b/seed/ts-express/license/core/schemas/builders/literals/stringLiteral.ts new file mode 100644 index 00000000000..3939b76b48d --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/literals/stringLiteral.ts @@ -0,0 +1,29 @@ +import { 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/license/core/schemas/builders/object-like/getObjectLikeUtils.ts b/seed/ts-express/license/core/schemas/builders/object-like/getObjectLikeUtils.ts new file mode 100644 index 00000000000..8331d08da89 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/object-like/getObjectLikeUtils.ts @@ -0,0 +1,79 @@ +import { BaseSchema } from "../../Schema"; +import { filterObject } from "../../utils/filterObject"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { isPlainObject } from "../../utils/isPlainObject"; +import { getSchemaUtils } from "../schema-utils"; +import { 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/license/core/schemas/builders/object-like/index.ts b/seed/ts-express/license/core/schemas/builders/object-like/index.ts new file mode 100644 index 00000000000..c342e72cf9d --- /dev/null +++ b/seed/ts-express/license/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/license/core/schemas/builders/object-like/types.ts b/seed/ts-express/license/core/schemas/builders/object-like/types.ts new file mode 100644 index 00000000000..75b3698729c --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/object-like/types.ts @@ -0,0 +1,11 @@ +import { 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/license/core/schemas/builders/object/index.ts b/seed/ts-express/license/core/schemas/builders/object/index.ts new file mode 100644 index 00000000000..e3f4388db28 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/object/index.ts @@ -0,0 +1,22 @@ +export { getObjectUtils, object } from "./object"; +export { objectWithoutOptionalProperties } from "./objectWithoutOptionalProperties"; +export type { + inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas, + inferParsedObjectWithoutOptionalPropertiesFromPropertySchemas, +} from "./objectWithoutOptionalProperties"; +export { isProperty, property } from "./property"; +export type { 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/license/core/schemas/builders/object/object.ts b/seed/ts-express/license/core/schemas/builders/object/object.ts new file mode 100644 index 00000000000..e00136d72fc --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/object/object.ts @@ -0,0 +1,324 @@ +import { MaybeValid, Schema, SchemaType, 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"; +import { getSchemaUtils } from "../schema-utils"; +import { isProperty } from "./property"; +import { + 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: unknown) => 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: unknown) => 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); + 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), + }); + }, + json: (parsed, opts) => { + return validateAndTransformExtendedObject({ + extensionKeys: extension._getParsedProperties(), + value: parsed, + transformBase: (parsedBase) => schema.json(parsedBase, opts), + transformExtension: (parsedExtension) => extension.json(parsedExtension, opts), + }); + }, + getType: () => SchemaType.OBJECT, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + ...getObjectUtils(baseSchema), + }; + }, + }; +} + +function validateAndTransformExtendedObject({ + extensionKeys, + value, + transformBase, + transformExtension, +}: { + extensionKeys: (keyof PreTransformedExtension)[]; + value: unknown; + transformBase: (value: unknown) => MaybeValid; + transformExtension: (value: unknown) => MaybeValid; +}): MaybeValid { + 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: + return true; + default: + return false; + } +} diff --git a/seed/ts-express/license/core/schemas/builders/object/objectWithoutOptionalProperties.ts b/seed/ts-express/license/core/schemas/builders/object/objectWithoutOptionalProperties.ts new file mode 100644 index 00000000000..a0951f48efc --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/object/objectWithoutOptionalProperties.ts @@ -0,0 +1,18 @@ +import { object } from "./object"; +import { 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/license/core/schemas/builders/object/property.ts b/seed/ts-express/license/core/schemas/builders/object/property.ts new file mode 100644 index 00000000000..d245c4b193a --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/object/property.ts @@ -0,0 +1,23 @@ +import { 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/license/core/schemas/builders/object/types.ts b/seed/ts-express/license/core/schemas/builders/object/types.ts new file mode 100644 index 00000000000..de9bb4074e5 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/object/types.ts @@ -0,0 +1,72 @@ +import { BaseSchema, inferParsed, inferRaw, Schema } from "../../Schema"; +import { addQuestionMarksToNullableProperties } from "../../utils/addQuestionMarksToNullableProperties"; +import { ObjectLikeUtils } from "../object-like"; +import { SchemaUtils } from "../schema-utils"; +import { 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; +} + +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/license/core/schemas/builders/primitives/any.ts b/seed/ts-express/license/core/schemas/builders/primitives/any.ts new file mode 100644 index 00000000000..fcaeb04255a --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/primitives/any.ts @@ -0,0 +1,4 @@ +import { SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; + +export const any = createIdentitySchemaCreator(SchemaType.ANY, (value) => ({ ok: true, value })); diff --git a/seed/ts-express/license/core/schemas/builders/primitives/boolean.ts b/seed/ts-express/license/core/schemas/builders/primitives/boolean.ts new file mode 100644 index 00000000000..fad60562120 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/primitives/boolean.ts @@ -0,0 +1,25 @@ +import { SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export const boolean = 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/license/core/schemas/builders/primitives/index.ts b/seed/ts-express/license/core/schemas/builders/primitives/index.ts new file mode 100644 index 00000000000..788f9416bfe --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/primitives/index.ts @@ -0,0 +1,5 @@ +export { any } from "./any"; +export { boolean } from "./boolean"; +export { number } from "./number"; +export { string } from "./string"; +export { unknown } from "./unknown"; diff --git a/seed/ts-express/license/core/schemas/builders/primitives/number.ts b/seed/ts-express/license/core/schemas/builders/primitives/number.ts new file mode 100644 index 00000000000..c2689456936 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/primitives/number.ts @@ -0,0 +1,25 @@ +import { SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export const number = 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/license/core/schemas/builders/primitives/string.ts b/seed/ts-express/license/core/schemas/builders/primitives/string.ts new file mode 100644 index 00000000000..949f1f2a630 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/primitives/string.ts @@ -0,0 +1,25 @@ +import { SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export const string = 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/license/core/schemas/builders/primitives/unknown.ts b/seed/ts-express/license/core/schemas/builders/primitives/unknown.ts new file mode 100644 index 00000000000..4d5249571f5 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/primitives/unknown.ts @@ -0,0 +1,4 @@ +import { SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; + +export const unknown = createIdentitySchemaCreator(SchemaType.UNKNOWN, (value) => ({ ok: true, value })); diff --git a/seed/ts-express/license/core/schemas/builders/record/index.ts b/seed/ts-express/license/core/schemas/builders/record/index.ts new file mode 100644 index 00000000000..82e25c5c2af --- /dev/null +++ b/seed/ts-express/license/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/license/core/schemas/builders/record/record.ts b/seed/ts-express/license/core/schemas/builders/record/record.ts new file mode 100644 index 00000000000..6683ac3609f --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/record/record.ts @@ -0,0 +1,130 @@ +import { MaybeValid, Schema, SchemaType, 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"; +import { 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]) => { + // skip nullish keys + if (value == null) { + return accPromise; + } + + const acc = accPromise; + + let key: string | number = stringKey; + if (isKeyNumeric) { + const numberKey = stringKey.length > 0 ? Number(stringKey) : NaN; + if (!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/license/core/schemas/builders/record/types.ts b/seed/ts-express/license/core/schemas/builders/record/types.ts new file mode 100644 index 00000000000..eb82cc7f65c --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/record/types.ts @@ -0,0 +1,17 @@ +import { BaseSchema } from "../../Schema"; +import { SchemaUtils } from "../schema-utils"; + +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/license/core/schemas/builders/schema-utils/JsonError.ts b/seed/ts-express/license/core/schemas/builders/schema-utils/JsonError.ts new file mode 100644 index 00000000000..2b89ca0e7ad --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/schema-utils/JsonError.ts @@ -0,0 +1,9 @@ +import { 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/license/core/schemas/builders/schema-utils/ParseError.ts b/seed/ts-express/license/core/schemas/builders/schema-utils/ParseError.ts new file mode 100644 index 00000000000..d056eb45cf7 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/schema-utils/ParseError.ts @@ -0,0 +1,9 @@ +import { 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/license/core/schemas/builders/schema-utils/getSchemaUtils.ts b/seed/ts-express/license/core/schemas/builders/schema-utils/getSchemaUtils.ts new file mode 100644 index 00000000000..79ecad92132 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/schema-utils/getSchemaUtils.ts @@ -0,0 +1,105 @@ +import { BaseSchema, Schema, SchemaOptions, SchemaType } from "../../Schema"; +import { JsonError } from "./JsonError"; +import { ParseError } from "./ParseError"; + +export interface SchemaUtils { + optional: () => 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 { + optional: () => optional(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 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 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/license/core/schemas/builders/schema-utils/index.ts b/seed/ts-express/license/core/schemas/builders/schema-utils/index.ts new file mode 100644 index 00000000000..aa04e051dfa --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/schema-utils/index.ts @@ -0,0 +1,4 @@ +export { getSchemaUtils, optional, transform } from "./getSchemaUtils"; +export type { SchemaUtils } from "./getSchemaUtils"; +export { JsonError } from "./JsonError"; +export { ParseError } from "./ParseError"; diff --git a/seed/ts-express/license/core/schemas/builders/schema-utils/stringifyValidationErrors.ts b/seed/ts-express/license/core/schemas/builders/schema-utils/stringifyValidationErrors.ts new file mode 100644 index 00000000000..4160f0a2617 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/schema-utils/stringifyValidationErrors.ts @@ -0,0 +1,8 @@ +import { 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/license/core/schemas/builders/set/index.ts b/seed/ts-express/license/core/schemas/builders/set/index.ts new file mode 100644 index 00000000000..f3310e8bdad --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/set/index.ts @@ -0,0 +1 @@ +export { set } from "./set"; diff --git a/seed/ts-express/license/core/schemas/builders/set/set.ts b/seed/ts-express/license/core/schemas/builders/set/set.ts new file mode 100644 index 00000000000..e9e6bb7e539 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/set/set.ts @@ -0,0 +1,43 @@ +import { BaseSchema, Schema, SchemaType } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { list } from "../list"; +import { getSchemaUtils } from "../schema-utils"; + +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/license/core/schemas/builders/undiscriminated-union/index.ts b/seed/ts-express/license/core/schemas/builders/undiscriminated-union/index.ts new file mode 100644 index 00000000000..75b71cb3565 --- /dev/null +++ b/seed/ts-express/license/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/license/core/schemas/builders/undiscriminated-union/types.ts b/seed/ts-express/license/core/schemas/builders/undiscriminated-union/types.ts new file mode 100644 index 00000000000..43e7108a060 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/undiscriminated-union/types.ts @@ -0,0 +1,10 @@ +import { 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/license/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts b/seed/ts-express/license/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts new file mode 100644 index 00000000000..21ed3df0f40 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts @@ -0,0 +1,60 @@ +import { BaseSchema, MaybeValid, Schema, SchemaOptions, SchemaType, ValidationError } from "../../Schema"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils"; +import { 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/license/core/schemas/builders/union/discriminant.ts b/seed/ts-express/license/core/schemas/builders/union/discriminant.ts new file mode 100644 index 00000000000..55065bc8946 --- /dev/null +++ b/seed/ts-express/license/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/license/core/schemas/builders/union/index.ts b/seed/ts-express/license/core/schemas/builders/union/index.ts new file mode 100644 index 00000000000..85fc008a2d8 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/union/index.ts @@ -0,0 +1,10 @@ +export { discriminant } from "./discriminant"; +export type { Discriminant } from "./discriminant"; +export type { + inferParsedDiscriminant, + inferParsedUnion, + inferRawDiscriminant, + inferRawUnion, + UnionSubtypes, +} from "./types"; +export { union } from "./union"; diff --git a/seed/ts-express/license/core/schemas/builders/union/types.ts b/seed/ts-express/license/core/schemas/builders/union/types.ts new file mode 100644 index 00000000000..6f82c868b2d --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/union/types.ts @@ -0,0 +1,26 @@ +import { inferParsedObject, inferRawObject, ObjectSchema } from "../object"; +import { 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/license/core/schemas/builders/union/union.ts b/seed/ts-express/license/core/schemas/builders/union/union.ts new file mode 100644 index 00000000000..ab61475a572 --- /dev/null +++ b/seed/ts-express/license/core/schemas/builders/union/union.ts @@ -0,0 +1,170 @@ +import { BaseSchema, 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"; +import { ObjectSchema } from "../object"; +import { getObjectLikeUtils, ObjectLikeSchema } from "../object-like"; +import { getSchemaUtils } from "../schema-utils"; +import { Discriminant } from "./discriminant"; +import { 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/license/core/schemas/index.ts b/seed/ts-express/license/core/schemas/index.ts new file mode 100644 index 00000000000..5429d8b43eb --- /dev/null +++ b/seed/ts-express/license/core/schemas/index.ts @@ -0,0 +1,2 @@ +export * from "./builders"; +export type { inferParsed, inferRaw, Schema, SchemaOptions } from "./Schema"; diff --git a/seed/ts-express/license/core/schemas/utils/MaybePromise.ts b/seed/ts-express/license/core/schemas/utils/MaybePromise.ts new file mode 100644 index 00000000000..9cd354b3418 --- /dev/null +++ b/seed/ts-express/license/core/schemas/utils/MaybePromise.ts @@ -0,0 +1 @@ +export type MaybePromise = T | Promise; diff --git a/seed/ts-express/license/core/schemas/utils/addQuestionMarksToNullableProperties.ts b/seed/ts-express/license/core/schemas/utils/addQuestionMarksToNullableProperties.ts new file mode 100644 index 00000000000..4111d703cd0 --- /dev/null +++ b/seed/ts-express/license/core/schemas/utils/addQuestionMarksToNullableProperties.ts @@ -0,0 +1,15 @@ +export type addQuestionMarksToNullableProperties = { + [K in OptionalKeys]?: T[K]; +} & Pick>; + +export type OptionalKeys = { + [K in keyof T]-?: undefined extends T[K] + ? K + : null extends T[K] + ? K + : 1 extends (any extends T[K] ? 0 : 1) + ? never + : K; +}[keyof T]; + +export type RequiredKeys = Exclude>; diff --git a/seed/ts-express/license/core/schemas/utils/createIdentitySchemaCreator.ts b/seed/ts-express/license/core/schemas/utils/createIdentitySchemaCreator.ts new file mode 100644 index 00000000000..de107cf5ee1 --- /dev/null +++ b/seed/ts-express/license/core/schemas/utils/createIdentitySchemaCreator.ts @@ -0,0 +1,21 @@ +import { getSchemaUtils } from "../builders/schema-utils"; +import { 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/license/core/schemas/utils/entries.ts b/seed/ts-express/license/core/schemas/utils/entries.ts new file mode 100644 index 00000000000..e122952137d --- /dev/null +++ b/seed/ts-express/license/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/license/core/schemas/utils/filterObject.ts b/seed/ts-express/license/core/schemas/utils/filterObject.ts new file mode 100644 index 00000000000..2c25a3455bc --- /dev/null +++ b/seed/ts-express/license/core/schemas/utils/filterObject.ts @@ -0,0 +1,10 @@ +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; + } + return acc; + // eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter + }, {} as Pick); +} diff --git a/seed/ts-express/license/core/schemas/utils/getErrorMessageForIncorrectType.ts b/seed/ts-express/license/core/schemas/utils/getErrorMessageForIncorrectType.ts new file mode 100644 index 00000000000..1a5c31027ce --- /dev/null +++ b/seed/ts-express/license/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/license/core/schemas/utils/isPlainObject.ts b/seed/ts-express/license/core/schemas/utils/isPlainObject.ts new file mode 100644 index 00000000000..db82a722c35 --- /dev/null +++ b/seed/ts-express/license/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/license/core/schemas/utils/keys.ts b/seed/ts-express/license/core/schemas/utils/keys.ts new file mode 100644 index 00000000000..01867098287 --- /dev/null +++ b/seed/ts-express/license/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/license/core/schemas/utils/maybeSkipValidation.ts b/seed/ts-express/license/core/schemas/utils/maybeSkipValidation.ts new file mode 100644 index 00000000000..86c07abf2b4 --- /dev/null +++ b/seed/ts-express/license/core/schemas/utils/maybeSkipValidation.ts @@ -0,0 +1,38 @@ +import { 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) { + // eslint-disable-next-line no-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/license/core/schemas/utils/partition.ts b/seed/ts-express/license/core/schemas/utils/partition.ts new file mode 100644 index 00000000000..f58d6f3d35f --- /dev/null +++ b/seed/ts-express/license/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/license/errors/SeedLicenseError.ts b/seed/ts-express/license/errors/SeedLicenseError.ts new file mode 100644 index 00000000000..f984b98863c --- /dev/null +++ b/seed/ts-express/license/errors/SeedLicenseError.ts @@ -0,0 +1,14 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import express from "express"; + +export abstract class SeedLicenseError extends Error { + constructor(public readonly errorName?: string) { + super(); + Object.setPrototypeOf(this, SeedLicenseError.prototype); + } + + public abstract send(res: express.Response): Promise; +} diff --git a/seed/ts-express/license/errors/index.ts b/seed/ts-express/license/errors/index.ts new file mode 100644 index 00000000000..b2eec75ee33 --- /dev/null +++ b/seed/ts-express/license/errors/index.ts @@ -0,0 +1 @@ +export { SeedLicenseError } from "./SeedLicenseError"; diff --git a/seed/ts-express/license/index.ts b/seed/ts-express/license/index.ts new file mode 100644 index 00000000000..5f2df6529a3 --- /dev/null +++ b/seed/ts-express/license/index.ts @@ -0,0 +1,3 @@ +export * as SeedLicense from "./api"; +export { register } from "./register"; +export { SeedLicenseError } from "./errors"; diff --git a/seed/ts-express/license/register.ts b/seed/ts-express/license/register.ts new file mode 100644 index 00000000000..2f4a0441f51 --- /dev/null +++ b/seed/ts-express/license/register.ts @@ -0,0 +1,15 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import express from "express"; +import { RootService } from "./api/service/RootService"; + +export function register( + expressApp: express.Express | express.Router, + services: { + _root: RootService; + } +): void { + (expressApp as any).use("/", services._root.toRouter()); +} diff --git a/seed/ts-express/license/serialization/index.ts b/seed/ts-express/license/serialization/index.ts new file mode 100644 index 00000000000..eea524d6557 --- /dev/null +++ b/seed/ts-express/license/serialization/index.ts @@ -0,0 +1 @@ +export * from "./types"; diff --git a/seed/ts-express/license/serialization/types/Type.ts b/seed/ts-express/license/serialization/types/Type.ts new file mode 100644 index 00000000000..dcea84426af --- /dev/null +++ b/seed/ts-express/license/serialization/types/Type.ts @@ -0,0 +1,17 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as serializers from "../index"; +import * as SeedLicense from "../../api/index"; +import * as core from "../../core"; + +export const Type: core.serialization.ObjectSchema = core.serialization.object({ + name: core.serialization.string(), +}); + +export declare namespace Type { + interface Raw { + name: string; + } +} diff --git a/seed/ts-express/license/serialization/types/index.ts b/seed/ts-express/license/serialization/types/index.ts new file mode 100644 index 00000000000..3edf389a5d5 --- /dev/null +++ b/seed/ts-express/license/serialization/types/index.ts @@ -0,0 +1 @@ +export * from "./Type"; diff --git a/seed/ts-express/license/snippet-templates.json b/seed/ts-express/license/snippet-templates.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/ts-express/license/snippet.json b/seed/ts-express/license/snippet.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/.mock/definition/endpoints/content-type.yml b/seed/ts-sdk/exhaustive/allow-extra-fields/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/reference.md b/seed/ts-sdk/exhaustive/allow-extra-fields/reference.md index bd6d817d278..626ea450013 100644 --- a/seed/ts-sdk/exhaustive/allow-extra-fields/reference.md +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/reference.md @@ -359,6 +359,136 @@ await client.endpoints.container.getAndReturnOptional({ +## Endpoints ContentType + +

client.endpoints.contentType.postJsonPatchContentType({ ...params }) -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentType.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.endpoints.contentType.postJsonPatchContentWithCharsetType({ ...params }) -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentType.RequestOptions` + +
+
+
+
+ +
+
+
+ ## Endpoints Enum
client.endpoints.enum.getAndReturnEnum({ ...params }) -> SeedExhaustive.WeatherReport diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/resolved-snippet-templates.md b/seed/ts-sdk/exhaustive/allow-extra-fields/resolved-snippet-templates.md index 167ce502ce3..03fcc43186c 100644 --- a/seed/ts-sdk/exhaustive/allow-extra-fields/resolved-snippet-templates.md +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/resolved-snippet-templates.md @@ -112,6 +112,62 @@ await client.endpoints.container.getAndReturnOptional({ ``` +```typescript +import { SeedExhaustiveClient } from "@fern/exhaustive"; + +const client = new SeedExhaustiveClient({ + environment: "YOUR_BASE_URL", + token: "YOUR_TOKEN", +}); +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); + +``` + + +```typescript +import { SeedExhaustiveClient } from "@fern/exhaustive"; + +const client = new SeedExhaustiveClient({ + environment: "YOUR_BASE_URL", + token: "YOUR_TOKEN", +}); +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); + +``` + + ```typescript import { SeedExhaustiveClient } from "@fern/exhaustive"; diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/snippet-templates.json b/seed/ts-sdk/exhaustive/allow-extra-fields/snippet-templates.json index 2abba309172..6d46a90fcf6 100644 --- a/seed/ts-sdk/exhaustive/allow-extra-fields/snippet-templates.json +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/snippet-templates.json @@ -836,6 +836,706 @@ "type": "v1" } }, + { + "sdk": { + "package": "@fern/exhaustive", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { SeedExhaustiveClient } from \"@fern/exhaustive\";" + ], + "templateString": "const client = new SeedExhaustiveClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "token: $FERN_INPUT", + "isOptional": false, + "templateInputs": [ + { + "location": "AUTH", + "path": "token", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.endpoints.contentType.postJsonPatchContentType(\n\t$FERN_INPUT\n)", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{\n\t\t$FERN_INPUT\n\t}", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "string: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "string", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "integer: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "integer", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "long: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "long", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "double: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "double", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bool: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bool", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "datetime: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "datetime", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "date: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "date", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "uuid: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "uuid", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "base64: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "base64", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "list: [\n\t\t\t\t$FERN_INPUT\n\t\t\t]", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "list", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "set: new Set([\n\t\t\t\t$FERN_INPUT\n\t\t\t])", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "set", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "map: {\n\t\t\t\t$FERN_INPUT\n\t\t\t}", + "delimiter": ",\n\t\t\t\t", + "keyValueSeparator": ": ", + "keyTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "valueTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "map", + "type": "payload" + }, + "type": "dict" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bigint: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bigint", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "v1" + } + }, + { + "sdk": { + "package": "@fern/exhaustive", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { SeedExhaustiveClient } from \"@fern/exhaustive\";" + ], + "templateString": "const client = new SeedExhaustiveClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "token: $FERN_INPUT", + "isOptional": false, + "templateInputs": [ + { + "location": "AUTH", + "path": "token", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.endpoints.contentType.postJsonPatchContentWithCharsetType(\n\t$FERN_INPUT\n)", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{\n\t\t$FERN_INPUT\n\t}", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "string: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "string", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "integer: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "integer", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "long: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "long", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "double: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "double", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bool: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bool", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "datetime: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "datetime", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "date: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "date", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "uuid: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "uuid", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "base64: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "base64", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "list: [\n\t\t\t\t$FERN_INPUT\n\t\t\t]", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "list", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "set: new Set([\n\t\t\t\t$FERN_INPUT\n\t\t\t])", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "set", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "map: {\n\t\t\t\t$FERN_INPUT\n\t\t\t}", + "delimiter": ",\n\t\t\t\t", + "keyValueSeparator": ": ", + "keyTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "valueTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "map", + "type": "payload" + }, + "type": "dict" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bigint: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bigint", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "v1" + } + }, { "sdk": { "package": "@fern/exhaustive", diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/snippet.json b/seed/ts-sdk/exhaustive/allow-extra-fields/snippet.json index 3fc62172e0c..b7be73b97c3 100644 --- a/seed/ts-sdk/exhaustive/allow-extra-fields/snippet.json +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/snippet.json @@ -77,6 +77,28 @@ "client": "import { SeedExhaustiveClient, SeedExhaustive } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnOptional({\n string: \"string\"\n});\n" } }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: \"2024-01-15T09:30:00Z\",\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentWithCharsetType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: \"2024-01-15T09:30:00Z\",\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, { "id": { "path": "/enum", diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/client/Client.ts b/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/client/Client.ts index 77d5a57856b..fecb304d6bb 100644 --- a/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/client/Client.ts +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/client/Client.ts @@ -4,6 +4,7 @@ import * as core from "../../../../core"; import { Container } from "../resources/container/client/Client"; +import { ContentType } from "../resources/contentType/client/Client"; import { Enum } from "../resources/enum/client/Client"; import { HttpMethods } from "../resources/httpMethods/client/Client"; import { Object_ } from "../resources/object/client/Client"; @@ -36,6 +37,12 @@ export class Endpoints { return (this._container ??= new Container(this._options)); } + protected _contentType: ContentType | undefined; + + public get contentType(): ContentType { + return (this._contentType ??= new ContentType(this._options)); + } + protected _enum: Enum | undefined; public get enum(): Enum { diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/contentType/client/Client.ts b/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/contentType/client/Client.ts new file mode 100644 index 00000000000..e7394018a51 --- /dev/null +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/contentType/client/Client.ts @@ -0,0 +1,190 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as core from "../../../../../../core"; +import * as SeedExhaustive from "../../../../../index"; +import * as serializers from "../../../../../../serialization/index"; +import urlJoin from "url-join"; +import * as errors from "../../../../../../errors/index"; + +export declare namespace ContentType { + interface Options { + environment: core.Supplier; + token?: core.Supplier; + } + + interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + } +} + +export class ContentType { + constructor(protected readonly _options: ContentType.Options) {} + + /** + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {ContentType.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: "2024-01-15T09:30:00Z", + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public async postJsonPatchContentType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentType.RequestOptions + ): Promise { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/foo/bar"), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json-patch+json", + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.SeedExhaustiveTimeoutError(); + case "unknown": + throw new errors.SeedExhaustiveError({ + message: _response.error.errorMessage, + }); + } + } + + /** + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {ContentType.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: "2024-01-15T09:30:00Z", + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public async postJsonPatchContentWithCharsetType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentType.RequestOptions + ): Promise { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/foo/baz"), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json-patch+json; charset=utf-8", + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.SeedExhaustiveTimeoutError(); + case "unknown": + throw new errors.SeedExhaustiveError({ + message: _response.error.errorMessage, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + const bearer = await core.Supplier.get(this._options.token); + if (bearer != null) { + return `Bearer ${bearer}`; + } + + return undefined; + } +} diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/contentType/client/index.ts b/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/contentType/client/index.ts new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/contentType/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/contentType/index.ts b/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/contentType/index.ts new file mode 100644 index 00000000000..5ec76921e18 --- /dev/null +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/contentType/index.ts @@ -0,0 +1 @@ +export * from "./client"; diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/index.ts b/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/index.ts index b5b5b656b2b..b82087be453 100644 --- a/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/index.ts +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/index.ts @@ -1,4 +1,5 @@ export * as container from "./container"; +export * as contentType from "./contentType"; export * as enum_ from "./enum"; export * as httpMethods from "./httpMethods"; export * as object from "./object"; diff --git a/seed/ts-sdk/exhaustive/bigint/.mock/definition/endpoints/content-type.yml b/seed/ts-sdk/exhaustive/bigint/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ts-sdk/exhaustive/bigint/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ts-sdk/exhaustive/bigint/reference.md b/seed/ts-sdk/exhaustive/bigint/reference.md index 322a95d3cdd..5b3cef80e05 100644 --- a/seed/ts-sdk/exhaustive/bigint/reference.md +++ b/seed/ts-sdk/exhaustive/bigint/reference.md @@ -359,6 +359,136 @@ await client.endpoints.container.getAndReturnOptional({
+## Endpoints ContentType + +
client.endpoints.contentType.postJsonPatchContentType({ ...params }) -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentType.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.endpoints.contentType.postJsonPatchContentWithCharsetType({ ...params }) -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentType.RequestOptions` + +
+
+
+
+ +
+
+
+ ## Endpoints Enum
client.endpoints.enum.getAndReturnEnum({ ...params }) -> SeedExhaustive.WeatherReport diff --git a/seed/ts-sdk/exhaustive/bigint/resolved-snippet-templates.md b/seed/ts-sdk/exhaustive/bigint/resolved-snippet-templates.md index 167ce502ce3..03fcc43186c 100644 --- a/seed/ts-sdk/exhaustive/bigint/resolved-snippet-templates.md +++ b/seed/ts-sdk/exhaustive/bigint/resolved-snippet-templates.md @@ -112,6 +112,62 @@ await client.endpoints.container.getAndReturnOptional({ ``` +```typescript +import { SeedExhaustiveClient } from "@fern/exhaustive"; + +const client = new SeedExhaustiveClient({ + environment: "YOUR_BASE_URL", + token: "YOUR_TOKEN", +}); +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); + +``` + + +```typescript +import { SeedExhaustiveClient } from "@fern/exhaustive"; + +const client = new SeedExhaustiveClient({ + environment: "YOUR_BASE_URL", + token: "YOUR_TOKEN", +}); +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); + +``` + + ```typescript import { SeedExhaustiveClient } from "@fern/exhaustive"; diff --git a/seed/ts-sdk/exhaustive/bigint/snippet-templates.json b/seed/ts-sdk/exhaustive/bigint/snippet-templates.json index 2abba309172..6d46a90fcf6 100644 --- a/seed/ts-sdk/exhaustive/bigint/snippet-templates.json +++ b/seed/ts-sdk/exhaustive/bigint/snippet-templates.json @@ -836,6 +836,706 @@ "type": "v1" } }, + { + "sdk": { + "package": "@fern/exhaustive", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { SeedExhaustiveClient } from \"@fern/exhaustive\";" + ], + "templateString": "const client = new SeedExhaustiveClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "token: $FERN_INPUT", + "isOptional": false, + "templateInputs": [ + { + "location": "AUTH", + "path": "token", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.endpoints.contentType.postJsonPatchContentType(\n\t$FERN_INPUT\n)", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{\n\t\t$FERN_INPUT\n\t}", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "string: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "string", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "integer: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "integer", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "long: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "long", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "double: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "double", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bool: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bool", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "datetime: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "datetime", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "date: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "date", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "uuid: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "uuid", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "base64: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "base64", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "list: [\n\t\t\t\t$FERN_INPUT\n\t\t\t]", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "list", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "set: new Set([\n\t\t\t\t$FERN_INPUT\n\t\t\t])", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "set", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "map: {\n\t\t\t\t$FERN_INPUT\n\t\t\t}", + "delimiter": ",\n\t\t\t\t", + "keyValueSeparator": ": ", + "keyTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "valueTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "map", + "type": "payload" + }, + "type": "dict" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bigint: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bigint", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "v1" + } + }, + { + "sdk": { + "package": "@fern/exhaustive", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { SeedExhaustiveClient } from \"@fern/exhaustive\";" + ], + "templateString": "const client = new SeedExhaustiveClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "token: $FERN_INPUT", + "isOptional": false, + "templateInputs": [ + { + "location": "AUTH", + "path": "token", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.endpoints.contentType.postJsonPatchContentWithCharsetType(\n\t$FERN_INPUT\n)", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{\n\t\t$FERN_INPUT\n\t}", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "string: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "string", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "integer: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "integer", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "long: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "long", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "double: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "double", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bool: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bool", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "datetime: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "datetime", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "date: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "date", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "uuid: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "uuid", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "base64: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "base64", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "list: [\n\t\t\t\t$FERN_INPUT\n\t\t\t]", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "list", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "set: new Set([\n\t\t\t\t$FERN_INPUT\n\t\t\t])", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "set", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "map: {\n\t\t\t\t$FERN_INPUT\n\t\t\t}", + "delimiter": ",\n\t\t\t\t", + "keyValueSeparator": ": ", + "keyTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "valueTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "map", + "type": "payload" + }, + "type": "dict" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bigint: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bigint", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "v1" + } + }, { "sdk": { "package": "@fern/exhaustive", diff --git a/seed/ts-sdk/exhaustive/bigint/snippet.json b/seed/ts-sdk/exhaustive/bigint/snippet.json index 3fc62172e0c..b7be73b97c3 100644 --- a/seed/ts-sdk/exhaustive/bigint/snippet.json +++ b/seed/ts-sdk/exhaustive/bigint/snippet.json @@ -77,6 +77,28 @@ "client": "import { SeedExhaustiveClient, SeedExhaustive } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnOptional({\n string: \"string\"\n});\n" } }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: \"2024-01-15T09:30:00Z\",\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentWithCharsetType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: \"2024-01-15T09:30:00Z\",\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, { "id": { "path": "/enum", diff --git a/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/client/Client.ts b/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/client/Client.ts index 77d5a57856b..fecb304d6bb 100644 --- a/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/client/Client.ts +++ b/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/client/Client.ts @@ -4,6 +4,7 @@ import * as core from "../../../../core"; import { Container } from "../resources/container/client/Client"; +import { ContentType } from "../resources/contentType/client/Client"; import { Enum } from "../resources/enum/client/Client"; import { HttpMethods } from "../resources/httpMethods/client/Client"; import { Object_ } from "../resources/object/client/Client"; @@ -36,6 +37,12 @@ export class Endpoints { return (this._container ??= new Container(this._options)); } + protected _contentType: ContentType | undefined; + + public get contentType(): ContentType { + return (this._contentType ??= new ContentType(this._options)); + } + protected _enum: Enum | undefined; public get enum(): Enum { diff --git a/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/contentType/client/Client.ts b/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/contentType/client/Client.ts new file mode 100644 index 00000000000..8da6ef22957 --- /dev/null +++ b/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/contentType/client/Client.ts @@ -0,0 +1,182 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as core from "../../../../../../core"; +import * as SeedExhaustive from "../../../../../index"; +import * as serializers from "../../../../../../serialization/index"; +import urlJoin from "url-join"; +import * as errors from "../../../../../../errors/index"; + +export declare namespace ContentType { + interface Options { + environment: core.Supplier; + token?: core.Supplier; + } + + interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + } +} + +export class ContentType { + constructor(protected readonly _options: ContentType.Options) {} + + /** + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {ContentType.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: "2024-01-15T09:30:00Z", + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public async postJsonPatchContentType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentType.RequestOptions + ): Promise { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/foo/bar"), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json-patch+json", + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { unrecognizedObjectKeys: "strip" }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.SeedExhaustiveTimeoutError(); + case "unknown": + throw new errors.SeedExhaustiveError({ + message: _response.error.errorMessage, + }); + } + } + + /** + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {ContentType.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: "2024-01-15T09:30:00Z", + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public async postJsonPatchContentWithCharsetType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentType.RequestOptions + ): Promise { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/foo/baz"), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json-patch+json; charset=utf-8", + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { unrecognizedObjectKeys: "strip" }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.SeedExhaustiveTimeoutError(); + case "unknown": + throw new errors.SeedExhaustiveError({ + message: _response.error.errorMessage, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + const bearer = await core.Supplier.get(this._options.token); + if (bearer != null) { + return `Bearer ${bearer}`; + } + + return undefined; + } +} diff --git a/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/contentType/client/index.ts b/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/contentType/client/index.ts new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/contentType/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/contentType/index.ts b/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/contentType/index.ts new file mode 100644 index 00000000000..5ec76921e18 --- /dev/null +++ b/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/contentType/index.ts @@ -0,0 +1 @@ +export * from "./client"; diff --git a/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/index.ts b/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/index.ts index b5b5b656b2b..b82087be453 100644 --- a/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/index.ts +++ b/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/index.ts @@ -1,4 +1,5 @@ export * as container from "./container"; +export * as contentType from "./contentType"; export * as enum_ from "./enum"; export * as httpMethods from "./httpMethods"; export * as object from "./object"; diff --git a/seed/ts-sdk/exhaustive/bundle/.mock/definition/endpoints/content-type.yml b/seed/ts-sdk/exhaustive/bundle/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ts-sdk/exhaustive/bundle/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ts-sdk/exhaustive/bundle/reference.md b/seed/ts-sdk/exhaustive/bundle/reference.md index 73b1019a1eb..94ed70194a8 100644 --- a/seed/ts-sdk/exhaustive/bundle/reference.md +++ b/seed/ts-sdk/exhaustive/bundle/reference.md @@ -359,6 +359,136 @@ await client.endpoints.container.getAndReturnOptional({
+## Endpoints ContentType + +
client.endpoints.contentType.postJsonPatchContentType({ ...params }) -> core.APIResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Fiddle.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentType.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.endpoints.contentType.postJsonPatchContentWithCharsetType({ ...params }) -> core.APIResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Fiddle.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentType.RequestOptions` + +
+
+
+
+ +
+
+
+ ## Endpoints Enum
client.endpoints.enum.getAndReturnEnum({ ...params }) -> core.APIResponse diff --git a/seed/ts-sdk/exhaustive/bundle/resolved-snippet-templates.md b/seed/ts-sdk/exhaustive/bundle/resolved-snippet-templates.md index fdf7bca7e9b..187ccd04cff 100644 --- a/seed/ts-sdk/exhaustive/bundle/resolved-snippet-templates.md +++ b/seed/ts-sdk/exhaustive/bundle/resolved-snippet-templates.md @@ -112,6 +112,62 @@ await client.endpoints.container.getAndReturnOptional({ ``` +```typescript +import { FiddleClient } from "@fern/exhaustive"; + +const client = new FiddleClient({ + environment: "YOUR_BASE_URL", + token: "YOUR_TOKEN", +}); +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); + +``` + + +```typescript +import { FiddleClient } from "@fern/exhaustive"; + +const client = new FiddleClient({ + environment: "YOUR_BASE_URL", + token: "YOUR_TOKEN", +}); +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); + +``` + + ```typescript import { FiddleClient } from "@fern/exhaustive"; diff --git a/seed/ts-sdk/exhaustive/bundle/snippet-templates.json b/seed/ts-sdk/exhaustive/bundle/snippet-templates.json index 55bb9b7b047..71a155d938b 100644 --- a/seed/ts-sdk/exhaustive/bundle/snippet-templates.json +++ b/seed/ts-sdk/exhaustive/bundle/snippet-templates.json @@ -836,6 +836,706 @@ "type": "v1" } }, + { + "sdk": { + "package": "@fern/exhaustive", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { FiddleClient } from \"@fern/exhaustive\";" + ], + "templateString": "const client = new FiddleClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "token: $FERN_INPUT", + "isOptional": false, + "templateInputs": [ + { + "location": "AUTH", + "path": "token", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.endpoints.contentType.postJsonPatchContentType(\n\t$FERN_INPUT\n)", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{\n\t\t$FERN_INPUT\n\t}", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "string: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "string", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "integer: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "integer", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "long: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "long", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "double: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "double", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bool: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bool", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "datetime: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "datetime", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "date: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "date", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "uuid: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "uuid", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "base64: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "base64", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "list: [\n\t\t\t\t$FERN_INPUT\n\t\t\t]", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "list", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "set: new Set([\n\t\t\t\t$FERN_INPUT\n\t\t\t])", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "set", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "map: {\n\t\t\t\t$FERN_INPUT\n\t\t\t}", + "delimiter": ",\n\t\t\t\t", + "keyValueSeparator": ": ", + "keyTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "valueTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "map", + "type": "payload" + }, + "type": "dict" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bigint: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bigint", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "v1" + } + }, + { + "sdk": { + "package": "@fern/exhaustive", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { FiddleClient } from \"@fern/exhaustive\";" + ], + "templateString": "const client = new FiddleClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "token: $FERN_INPUT", + "isOptional": false, + "templateInputs": [ + { + "location": "AUTH", + "path": "token", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.endpoints.contentType.postJsonPatchContentWithCharsetType(\n\t$FERN_INPUT\n)", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{\n\t\t$FERN_INPUT\n\t}", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "string: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "string", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "integer: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "integer", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "long: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "long", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "double: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "double", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bool: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bool", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "datetime: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "datetime", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "date: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "date", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "uuid: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "uuid", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "base64: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "base64", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "list: [\n\t\t\t\t$FERN_INPUT\n\t\t\t]", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "list", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "set: new Set([\n\t\t\t\t$FERN_INPUT\n\t\t\t])", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "set", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "map: {\n\t\t\t\t$FERN_INPUT\n\t\t\t}", + "delimiter": ",\n\t\t\t\t", + "keyValueSeparator": ": ", + "keyTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "valueTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "map", + "type": "payload" + }, + "type": "dict" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bigint: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bigint", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "v1" + } + }, { "sdk": { "package": "@fern/exhaustive", diff --git a/seed/ts-sdk/exhaustive/bundle/snippet.json b/seed/ts-sdk/exhaustive/bundle/snippet.json index e49cdea27ff..fc73f8e3e24 100644 --- a/seed/ts-sdk/exhaustive/bundle/snippet.json +++ b/seed/ts-sdk/exhaustive/bundle/snippet.json @@ -77,6 +77,28 @@ "client": "import { FiddleClient } from \"@fern/exhaustive\";\n\nconst client = new FiddleClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnOptional({\n string: \"string\"\n});\n" } }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "type": "typescript", + "client": "import { FiddleClient } from \"@fern/exhaustive\";\n\nconst client = new FiddleClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: \"2024-01-15T09:30:00Z\",\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "type": "typescript", + "client": "import { FiddleClient } from \"@fern/exhaustive\";\n\nconst client = new FiddleClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentWithCharsetType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: \"2024-01-15T09:30:00Z\",\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, { "id": { "path": "/enum", diff --git a/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/client/Client.ts b/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/client/Client.ts index 77d5a57856b..fecb304d6bb 100644 --- a/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/client/Client.ts +++ b/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/client/Client.ts @@ -4,6 +4,7 @@ import * as core from "../../../../core"; import { Container } from "../resources/container/client/Client"; +import { ContentType } from "../resources/contentType/client/Client"; import { Enum } from "../resources/enum/client/Client"; import { HttpMethods } from "../resources/httpMethods/client/Client"; import { Object_ } from "../resources/object/client/Client"; @@ -36,6 +37,12 @@ export class Endpoints { return (this._container ??= new Container(this._options)); } + protected _contentType: ContentType | undefined; + + public get contentType(): ContentType { + return (this._contentType ??= new ContentType(this._options)); + } + protected _enum: Enum | undefined; public get enum(): Enum { diff --git a/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/contentType/client/Client.ts b/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/contentType/client/Client.ts new file mode 100644 index 00000000000..1c1872141fd --- /dev/null +++ b/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/contentType/client/Client.ts @@ -0,0 +1,155 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as core from "../../../../../../core"; +import * as Fiddle from "../../../../../index"; +import * as serializers from "../../../../../../serialization/index"; +import urlJoin from "url-join"; + +export declare namespace ContentType { + interface Options { + environment: core.Supplier; + token?: core.Supplier; + } + + interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + } +} + +export class ContentType { + constructor(protected readonly _options: ContentType.Options) {} + + /** + * @param {Fiddle.types.ObjectWithOptionalField} request + * @param {ContentType.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: "2024-01-15T09:30:00Z", + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public async postJsonPatchContentType( + request: Fiddle.types.ObjectWithOptionalField, + requestOptions?: ContentType.RequestOptions + ): Promise> { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/foo/bar"), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json-patch+json", + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { unrecognizedObjectKeys: "strip" }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { + ok: true, + body: undefined, + }; + } + + return { + ok: false, + error: Fiddle.endpoints.contentType.postJsonPatchContentType.Error._unknown(_response.error), + }; + } + + /** + * @param {Fiddle.types.ObjectWithOptionalField} request + * @param {ContentType.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: "2024-01-15T09:30:00Z", + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public async postJsonPatchContentWithCharsetType( + request: Fiddle.types.ObjectWithOptionalField, + requestOptions?: ContentType.RequestOptions + ): Promise> { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/foo/baz"), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json-patch+json; charset=utf-8", + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { unrecognizedObjectKeys: "strip" }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { + ok: true, + body: undefined, + }; + } + + return { + ok: false, + error: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._unknown(_response.error), + }; + } + + protected async _getAuthorizationHeader(): Promise { + const bearer = await core.Supplier.get(this._options.token); + if (bearer != null) { + return `Bearer ${bearer}`; + } + + return undefined; + } +} diff --git a/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/contentType/client/index.ts b/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/contentType/client/index.ts new file mode 100644 index 00000000000..cd74d615ee4 --- /dev/null +++ b/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/contentType/client/index.ts @@ -0,0 +1,2 @@ +export * as postJsonPatchContentType from "./postJsonPatchContentType"; +export * as postJsonPatchContentWithCharsetType from "./postJsonPatchContentWithCharsetType"; diff --git a/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentType.ts b/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentType.ts new file mode 100644 index 00000000000..c474809d2c2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentType.ts @@ -0,0 +1,52 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Fiddle from "../../../../../index"; +import * as core from "../../../../../../core"; + +export type Error = Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Unknown; + +export declare namespace Error { + interface _Unknown extends _Utils { + statusCode: void; + content: core.Fetcher.Error; + } + + interface _Utils { + _visit: <_Result>( + visitor: Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Visitor<_Result> + ) => _Result; + } + + interface _Visitor<_Result> { + _other: (value: core.Fetcher.Error) => _Result; + } +} + +export const Error = { + _unknown: ( + fetcherError: core.Fetcher.Error + ): Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Unknown => { + return { + statusCode: undefined, + content: fetcherError, + _visit: function <_Result>( + this: Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Unknown, + visitor: Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Visitor<_Result> + ) { + return Fiddle.endpoints.contentType.postJsonPatchContentType.Error._visit(this, visitor); + }, + }; + }, + + _visit: <_Result>( + value: Fiddle.endpoints.contentType.postJsonPatchContentType.Error, + visitor: Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Visitor<_Result> + ): _Result => { + switch (value.statusCode) { + default: + return visitor._other(value as any); + } + }, +} as const; diff --git a/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentWithCharsetType.ts b/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentWithCharsetType.ts new file mode 100644 index 00000000000..69cbf6e95a0 --- /dev/null +++ b/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentWithCharsetType.ts @@ -0,0 +1,52 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Fiddle from "../../../../../index"; +import * as core from "../../../../../../core"; + +export type Error = Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Unknown; + +export declare namespace Error { + interface _Unknown extends _Utils { + statusCode: void; + content: core.Fetcher.Error; + } + + interface _Utils { + _visit: <_Result>( + visitor: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Visitor<_Result> + ) => _Result; + } + + interface _Visitor<_Result> { + _other: (value: core.Fetcher.Error) => _Result; + } +} + +export const Error = { + _unknown: ( + fetcherError: core.Fetcher.Error + ): Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Unknown => { + return { + statusCode: undefined, + content: fetcherError, + _visit: function <_Result>( + this: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Unknown, + visitor: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Visitor<_Result> + ) { + return Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._visit(this, visitor); + }, + }; + }, + + _visit: <_Result>( + value: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error, + visitor: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Visitor<_Result> + ): _Result => { + switch (value.statusCode) { + default: + return visitor._other(value as any); + } + }, +} as const; diff --git a/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/contentType/index.ts b/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/contentType/index.ts new file mode 100644 index 00000000000..5ec76921e18 --- /dev/null +++ b/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/contentType/index.ts @@ -0,0 +1 @@ +export * from "./client"; diff --git a/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/index.ts b/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/index.ts index b5b5b656b2b..b82087be453 100644 --- a/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/index.ts +++ b/seed/ts-sdk/exhaustive/bundle/src/api/resources/endpoints/resources/index.ts @@ -1,4 +1,5 @@ export * as container from "./container"; +export * as contentType from "./contentType"; export * as enum_ from "./enum"; export * as httpMethods from "./httpMethods"; export * as object from "./object"; diff --git a/seed/ts-sdk/exhaustive/custom-package-json/.mock/definition/endpoints/content-type.yml b/seed/ts-sdk/exhaustive/custom-package-json/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ts-sdk/exhaustive/custom-package-json/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ts-sdk/exhaustive/custom-package-json/reference.md b/seed/ts-sdk/exhaustive/custom-package-json/reference.md index 73b1019a1eb..94ed70194a8 100644 --- a/seed/ts-sdk/exhaustive/custom-package-json/reference.md +++ b/seed/ts-sdk/exhaustive/custom-package-json/reference.md @@ -359,6 +359,136 @@ await client.endpoints.container.getAndReturnOptional({
+## Endpoints ContentType + +
client.endpoints.contentType.postJsonPatchContentType({ ...params }) -> core.APIResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Fiddle.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentType.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.endpoints.contentType.postJsonPatchContentWithCharsetType({ ...params }) -> core.APIResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Fiddle.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentType.RequestOptions` + +
+
+
+
+ +
+
+
+ ## Endpoints Enum
client.endpoints.enum.getAndReturnEnum({ ...params }) -> core.APIResponse diff --git a/seed/ts-sdk/exhaustive/custom-package-json/resolved-snippet-templates.md b/seed/ts-sdk/exhaustive/custom-package-json/resolved-snippet-templates.md index fdf7bca7e9b..187ccd04cff 100644 --- a/seed/ts-sdk/exhaustive/custom-package-json/resolved-snippet-templates.md +++ b/seed/ts-sdk/exhaustive/custom-package-json/resolved-snippet-templates.md @@ -112,6 +112,62 @@ await client.endpoints.container.getAndReturnOptional({ ``` +```typescript +import { FiddleClient } from "@fern/exhaustive"; + +const client = new FiddleClient({ + environment: "YOUR_BASE_URL", + token: "YOUR_TOKEN", +}); +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); + +``` + + +```typescript +import { FiddleClient } from "@fern/exhaustive"; + +const client = new FiddleClient({ + environment: "YOUR_BASE_URL", + token: "YOUR_TOKEN", +}); +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); + +``` + + ```typescript import { FiddleClient } from "@fern/exhaustive"; diff --git a/seed/ts-sdk/exhaustive/custom-package-json/snippet-templates.json b/seed/ts-sdk/exhaustive/custom-package-json/snippet-templates.json index 55bb9b7b047..71a155d938b 100644 --- a/seed/ts-sdk/exhaustive/custom-package-json/snippet-templates.json +++ b/seed/ts-sdk/exhaustive/custom-package-json/snippet-templates.json @@ -836,6 +836,706 @@ "type": "v1" } }, + { + "sdk": { + "package": "@fern/exhaustive", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { FiddleClient } from \"@fern/exhaustive\";" + ], + "templateString": "const client = new FiddleClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "token: $FERN_INPUT", + "isOptional": false, + "templateInputs": [ + { + "location": "AUTH", + "path": "token", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.endpoints.contentType.postJsonPatchContentType(\n\t$FERN_INPUT\n)", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{\n\t\t$FERN_INPUT\n\t}", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "string: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "string", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "integer: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "integer", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "long: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "long", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "double: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "double", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bool: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bool", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "datetime: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "datetime", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "date: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "date", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "uuid: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "uuid", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "base64: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "base64", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "list: [\n\t\t\t\t$FERN_INPUT\n\t\t\t]", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "list", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "set: new Set([\n\t\t\t\t$FERN_INPUT\n\t\t\t])", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "set", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "map: {\n\t\t\t\t$FERN_INPUT\n\t\t\t}", + "delimiter": ",\n\t\t\t\t", + "keyValueSeparator": ": ", + "keyTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "valueTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "map", + "type": "payload" + }, + "type": "dict" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bigint: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bigint", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "v1" + } + }, + { + "sdk": { + "package": "@fern/exhaustive", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { FiddleClient } from \"@fern/exhaustive\";" + ], + "templateString": "const client = new FiddleClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "token: $FERN_INPUT", + "isOptional": false, + "templateInputs": [ + { + "location": "AUTH", + "path": "token", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.endpoints.contentType.postJsonPatchContentWithCharsetType(\n\t$FERN_INPUT\n)", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{\n\t\t$FERN_INPUT\n\t}", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "string: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "string", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "integer: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "integer", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "long: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "long", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "double: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "double", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bool: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bool", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "datetime: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "datetime", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "date: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "date", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "uuid: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "uuid", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "base64: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "base64", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "list: [\n\t\t\t\t$FERN_INPUT\n\t\t\t]", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "list", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "set: new Set([\n\t\t\t\t$FERN_INPUT\n\t\t\t])", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "set", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "map: {\n\t\t\t\t$FERN_INPUT\n\t\t\t}", + "delimiter": ",\n\t\t\t\t", + "keyValueSeparator": ": ", + "keyTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "valueTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "map", + "type": "payload" + }, + "type": "dict" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bigint: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bigint", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "v1" + } + }, { "sdk": { "package": "@fern/exhaustive", diff --git a/seed/ts-sdk/exhaustive/custom-package-json/snippet.json b/seed/ts-sdk/exhaustive/custom-package-json/snippet.json index e49cdea27ff..fc73f8e3e24 100644 --- a/seed/ts-sdk/exhaustive/custom-package-json/snippet.json +++ b/seed/ts-sdk/exhaustive/custom-package-json/snippet.json @@ -77,6 +77,28 @@ "client": "import { FiddleClient } from \"@fern/exhaustive\";\n\nconst client = new FiddleClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnOptional({\n string: \"string\"\n});\n" } }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "type": "typescript", + "client": "import { FiddleClient } from \"@fern/exhaustive\";\n\nconst client = new FiddleClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: \"2024-01-15T09:30:00Z\",\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "type": "typescript", + "client": "import { FiddleClient } from \"@fern/exhaustive\";\n\nconst client = new FiddleClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentWithCharsetType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: \"2024-01-15T09:30:00Z\",\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, { "id": { "path": "/enum", diff --git a/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/client/Client.ts b/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/client/Client.ts index 77d5a57856b..fecb304d6bb 100644 --- a/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/client/Client.ts +++ b/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/client/Client.ts @@ -4,6 +4,7 @@ import * as core from "../../../../core"; import { Container } from "../resources/container/client/Client"; +import { ContentType } from "../resources/contentType/client/Client"; import { Enum } from "../resources/enum/client/Client"; import { HttpMethods } from "../resources/httpMethods/client/Client"; import { Object_ } from "../resources/object/client/Client"; @@ -36,6 +37,12 @@ export class Endpoints { return (this._container ??= new Container(this._options)); } + protected _contentType: ContentType | undefined; + + public get contentType(): ContentType { + return (this._contentType ??= new ContentType(this._options)); + } + protected _enum: Enum | undefined; public get enum(): Enum { diff --git a/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/contentType/client/Client.ts b/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/contentType/client/Client.ts new file mode 100644 index 00000000000..1c1872141fd --- /dev/null +++ b/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/contentType/client/Client.ts @@ -0,0 +1,155 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as core from "../../../../../../core"; +import * as Fiddle from "../../../../../index"; +import * as serializers from "../../../../../../serialization/index"; +import urlJoin from "url-join"; + +export declare namespace ContentType { + interface Options { + environment: core.Supplier; + token?: core.Supplier; + } + + interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + } +} + +export class ContentType { + constructor(protected readonly _options: ContentType.Options) {} + + /** + * @param {Fiddle.types.ObjectWithOptionalField} request + * @param {ContentType.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: "2024-01-15T09:30:00Z", + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public async postJsonPatchContentType( + request: Fiddle.types.ObjectWithOptionalField, + requestOptions?: ContentType.RequestOptions + ): Promise> { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/foo/bar"), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json-patch+json", + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { unrecognizedObjectKeys: "strip" }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { + ok: true, + body: undefined, + }; + } + + return { + ok: false, + error: Fiddle.endpoints.contentType.postJsonPatchContentType.Error._unknown(_response.error), + }; + } + + /** + * @param {Fiddle.types.ObjectWithOptionalField} request + * @param {ContentType.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: "2024-01-15T09:30:00Z", + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public async postJsonPatchContentWithCharsetType( + request: Fiddle.types.ObjectWithOptionalField, + requestOptions?: ContentType.RequestOptions + ): Promise> { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/foo/baz"), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json-patch+json; charset=utf-8", + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { unrecognizedObjectKeys: "strip" }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { + ok: true, + body: undefined, + }; + } + + return { + ok: false, + error: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._unknown(_response.error), + }; + } + + protected async _getAuthorizationHeader(): Promise { + const bearer = await core.Supplier.get(this._options.token); + if (bearer != null) { + return `Bearer ${bearer}`; + } + + return undefined; + } +} diff --git a/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/contentType/client/index.ts b/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/contentType/client/index.ts new file mode 100644 index 00000000000..cd74d615ee4 --- /dev/null +++ b/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/contentType/client/index.ts @@ -0,0 +1,2 @@ +export * as postJsonPatchContentType from "./postJsonPatchContentType"; +export * as postJsonPatchContentWithCharsetType from "./postJsonPatchContentWithCharsetType"; diff --git a/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentType.ts b/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentType.ts new file mode 100644 index 00000000000..c474809d2c2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentType.ts @@ -0,0 +1,52 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Fiddle from "../../../../../index"; +import * as core from "../../../../../../core"; + +export type Error = Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Unknown; + +export declare namespace Error { + interface _Unknown extends _Utils { + statusCode: void; + content: core.Fetcher.Error; + } + + interface _Utils { + _visit: <_Result>( + visitor: Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Visitor<_Result> + ) => _Result; + } + + interface _Visitor<_Result> { + _other: (value: core.Fetcher.Error) => _Result; + } +} + +export const Error = { + _unknown: ( + fetcherError: core.Fetcher.Error + ): Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Unknown => { + return { + statusCode: undefined, + content: fetcherError, + _visit: function <_Result>( + this: Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Unknown, + visitor: Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Visitor<_Result> + ) { + return Fiddle.endpoints.contentType.postJsonPatchContentType.Error._visit(this, visitor); + }, + }; + }, + + _visit: <_Result>( + value: Fiddle.endpoints.contentType.postJsonPatchContentType.Error, + visitor: Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Visitor<_Result> + ): _Result => { + switch (value.statusCode) { + default: + return visitor._other(value as any); + } + }, +} as const; diff --git a/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentWithCharsetType.ts b/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentWithCharsetType.ts new file mode 100644 index 00000000000..69cbf6e95a0 --- /dev/null +++ b/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentWithCharsetType.ts @@ -0,0 +1,52 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Fiddle from "../../../../../index"; +import * as core from "../../../../../../core"; + +export type Error = Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Unknown; + +export declare namespace Error { + interface _Unknown extends _Utils { + statusCode: void; + content: core.Fetcher.Error; + } + + interface _Utils { + _visit: <_Result>( + visitor: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Visitor<_Result> + ) => _Result; + } + + interface _Visitor<_Result> { + _other: (value: core.Fetcher.Error) => _Result; + } +} + +export const Error = { + _unknown: ( + fetcherError: core.Fetcher.Error + ): Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Unknown => { + return { + statusCode: undefined, + content: fetcherError, + _visit: function <_Result>( + this: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Unknown, + visitor: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Visitor<_Result> + ) { + return Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._visit(this, visitor); + }, + }; + }, + + _visit: <_Result>( + value: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error, + visitor: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Visitor<_Result> + ): _Result => { + switch (value.statusCode) { + default: + return visitor._other(value as any); + } + }, +} as const; diff --git a/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/contentType/index.ts b/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/contentType/index.ts new file mode 100644 index 00000000000..5ec76921e18 --- /dev/null +++ b/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/contentType/index.ts @@ -0,0 +1 @@ +export * from "./client"; diff --git a/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/index.ts b/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/index.ts index b5b5b656b2b..b82087be453 100644 --- a/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/index.ts +++ b/seed/ts-sdk/exhaustive/custom-package-json/src/api/resources/endpoints/resources/index.ts @@ -1,4 +1,5 @@ export * as container from "./container"; +export * as contentType from "./contentType"; export * as enum_ from "./enum"; export * as httpMethods from "./httpMethods"; export * as object from "./object"; diff --git a/seed/ts-sdk/exhaustive/dev-dependencies/.mock/definition/endpoints/content-type.yml b/seed/ts-sdk/exhaustive/dev-dependencies/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ts-sdk/exhaustive/dev-dependencies/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ts-sdk/exhaustive/dev-dependencies/reference.md b/seed/ts-sdk/exhaustive/dev-dependencies/reference.md index 73b1019a1eb..94ed70194a8 100644 --- a/seed/ts-sdk/exhaustive/dev-dependencies/reference.md +++ b/seed/ts-sdk/exhaustive/dev-dependencies/reference.md @@ -359,6 +359,136 @@ await client.endpoints.container.getAndReturnOptional({
+## Endpoints ContentType + +
client.endpoints.contentType.postJsonPatchContentType({ ...params }) -> core.APIResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Fiddle.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentType.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.endpoints.contentType.postJsonPatchContentWithCharsetType({ ...params }) -> core.APIResponse +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `Fiddle.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentType.RequestOptions` + +
+
+
+
+ +
+
+
+ ## Endpoints Enum
client.endpoints.enum.getAndReturnEnum({ ...params }) -> core.APIResponse diff --git a/seed/ts-sdk/exhaustive/dev-dependencies/resolved-snippet-templates.md b/seed/ts-sdk/exhaustive/dev-dependencies/resolved-snippet-templates.md index fdf7bca7e9b..187ccd04cff 100644 --- a/seed/ts-sdk/exhaustive/dev-dependencies/resolved-snippet-templates.md +++ b/seed/ts-sdk/exhaustive/dev-dependencies/resolved-snippet-templates.md @@ -112,6 +112,62 @@ await client.endpoints.container.getAndReturnOptional({ ``` +```typescript +import { FiddleClient } from "@fern/exhaustive"; + +const client = new FiddleClient({ + environment: "YOUR_BASE_URL", + token: "YOUR_TOKEN", +}); +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); + +``` + + +```typescript +import { FiddleClient } from "@fern/exhaustive"; + +const client = new FiddleClient({ + environment: "YOUR_BASE_URL", + token: "YOUR_TOKEN", +}); +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); + +``` + + ```typescript import { FiddleClient } from "@fern/exhaustive"; diff --git a/seed/ts-sdk/exhaustive/dev-dependencies/snippet-templates.json b/seed/ts-sdk/exhaustive/dev-dependencies/snippet-templates.json index 55bb9b7b047..71a155d938b 100644 --- a/seed/ts-sdk/exhaustive/dev-dependencies/snippet-templates.json +++ b/seed/ts-sdk/exhaustive/dev-dependencies/snippet-templates.json @@ -836,6 +836,706 @@ "type": "v1" } }, + { + "sdk": { + "package": "@fern/exhaustive", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { FiddleClient } from \"@fern/exhaustive\";" + ], + "templateString": "const client = new FiddleClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "token: $FERN_INPUT", + "isOptional": false, + "templateInputs": [ + { + "location": "AUTH", + "path": "token", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.endpoints.contentType.postJsonPatchContentType(\n\t$FERN_INPUT\n)", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{\n\t\t$FERN_INPUT\n\t}", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "string: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "string", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "integer: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "integer", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "long: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "long", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "double: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "double", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bool: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bool", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "datetime: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "datetime", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "date: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "date", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "uuid: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "uuid", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "base64: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "base64", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "list: [\n\t\t\t\t$FERN_INPUT\n\t\t\t]", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "list", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "set: new Set([\n\t\t\t\t$FERN_INPUT\n\t\t\t])", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "set", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "map: {\n\t\t\t\t$FERN_INPUT\n\t\t\t}", + "delimiter": ",\n\t\t\t\t", + "keyValueSeparator": ": ", + "keyTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "valueTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "map", + "type": "payload" + }, + "type": "dict" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bigint: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bigint", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "v1" + } + }, + { + "sdk": { + "package": "@fern/exhaustive", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { FiddleClient } from \"@fern/exhaustive\";" + ], + "templateString": "const client = new FiddleClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "token: $FERN_INPUT", + "isOptional": false, + "templateInputs": [ + { + "location": "AUTH", + "path": "token", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.endpoints.contentType.postJsonPatchContentWithCharsetType(\n\t$FERN_INPUT\n)", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{\n\t\t$FERN_INPUT\n\t}", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "string: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "string", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "integer: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "integer", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "long: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "long", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "double: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "double", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bool: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bool", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "datetime: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "datetime", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "date: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "date", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "uuid: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "uuid", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "base64: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "base64", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "list: [\n\t\t\t\t$FERN_INPUT\n\t\t\t]", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "list", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "set: new Set([\n\t\t\t\t$FERN_INPUT\n\t\t\t])", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "set", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "map: {\n\t\t\t\t$FERN_INPUT\n\t\t\t}", + "delimiter": ",\n\t\t\t\t", + "keyValueSeparator": ": ", + "keyTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "valueTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "map", + "type": "payload" + }, + "type": "dict" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bigint: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bigint", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "v1" + } + }, { "sdk": { "package": "@fern/exhaustive", diff --git a/seed/ts-sdk/exhaustive/dev-dependencies/snippet.json b/seed/ts-sdk/exhaustive/dev-dependencies/snippet.json index e49cdea27ff..fc73f8e3e24 100644 --- a/seed/ts-sdk/exhaustive/dev-dependencies/snippet.json +++ b/seed/ts-sdk/exhaustive/dev-dependencies/snippet.json @@ -77,6 +77,28 @@ "client": "import { FiddleClient } from \"@fern/exhaustive\";\n\nconst client = new FiddleClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnOptional({\n string: \"string\"\n});\n" } }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "type": "typescript", + "client": "import { FiddleClient } from \"@fern/exhaustive\";\n\nconst client = new FiddleClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: \"2024-01-15T09:30:00Z\",\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "type": "typescript", + "client": "import { FiddleClient } from \"@fern/exhaustive\";\n\nconst client = new FiddleClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentWithCharsetType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: \"2024-01-15T09:30:00Z\",\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, { "id": { "path": "/enum", diff --git a/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/client/Client.ts b/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/client/Client.ts index 77d5a57856b..fecb304d6bb 100644 --- a/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/client/Client.ts +++ b/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/client/Client.ts @@ -4,6 +4,7 @@ import * as core from "../../../../core"; import { Container } from "../resources/container/client/Client"; +import { ContentType } from "../resources/contentType/client/Client"; import { Enum } from "../resources/enum/client/Client"; import { HttpMethods } from "../resources/httpMethods/client/Client"; import { Object_ } from "../resources/object/client/Client"; @@ -36,6 +37,12 @@ export class Endpoints { return (this._container ??= new Container(this._options)); } + protected _contentType: ContentType | undefined; + + public get contentType(): ContentType { + return (this._contentType ??= new ContentType(this._options)); + } + protected _enum: Enum | undefined; public get enum(): Enum { diff --git a/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/contentType/client/Client.ts b/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/contentType/client/Client.ts new file mode 100644 index 00000000000..1c1872141fd --- /dev/null +++ b/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/contentType/client/Client.ts @@ -0,0 +1,155 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as core from "../../../../../../core"; +import * as Fiddle from "../../../../../index"; +import * as serializers from "../../../../../../serialization/index"; +import urlJoin from "url-join"; + +export declare namespace ContentType { + interface Options { + environment: core.Supplier; + token?: core.Supplier; + } + + interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + } +} + +export class ContentType { + constructor(protected readonly _options: ContentType.Options) {} + + /** + * @param {Fiddle.types.ObjectWithOptionalField} request + * @param {ContentType.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: "2024-01-15T09:30:00Z", + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public async postJsonPatchContentType( + request: Fiddle.types.ObjectWithOptionalField, + requestOptions?: ContentType.RequestOptions + ): Promise> { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/foo/bar"), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json-patch+json", + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { unrecognizedObjectKeys: "strip" }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { + ok: true, + body: undefined, + }; + } + + return { + ok: false, + error: Fiddle.endpoints.contentType.postJsonPatchContentType.Error._unknown(_response.error), + }; + } + + /** + * @param {Fiddle.types.ObjectWithOptionalField} request + * @param {ContentType.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: "2024-01-15T09:30:00Z", + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public async postJsonPatchContentWithCharsetType( + request: Fiddle.types.ObjectWithOptionalField, + requestOptions?: ContentType.RequestOptions + ): Promise> { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/foo/baz"), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json-patch+json; charset=utf-8", + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { unrecognizedObjectKeys: "strip" }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return { + ok: true, + body: undefined, + }; + } + + return { + ok: false, + error: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._unknown(_response.error), + }; + } + + protected async _getAuthorizationHeader(): Promise { + const bearer = await core.Supplier.get(this._options.token); + if (bearer != null) { + return `Bearer ${bearer}`; + } + + return undefined; + } +} diff --git a/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/contentType/client/index.ts b/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/contentType/client/index.ts new file mode 100644 index 00000000000..cd74d615ee4 --- /dev/null +++ b/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/contentType/client/index.ts @@ -0,0 +1,2 @@ +export * as postJsonPatchContentType from "./postJsonPatchContentType"; +export * as postJsonPatchContentWithCharsetType from "./postJsonPatchContentWithCharsetType"; diff --git a/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentType.ts b/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentType.ts new file mode 100644 index 00000000000..c474809d2c2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentType.ts @@ -0,0 +1,52 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Fiddle from "../../../../../index"; +import * as core from "../../../../../../core"; + +export type Error = Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Unknown; + +export declare namespace Error { + interface _Unknown extends _Utils { + statusCode: void; + content: core.Fetcher.Error; + } + + interface _Utils { + _visit: <_Result>( + visitor: Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Visitor<_Result> + ) => _Result; + } + + interface _Visitor<_Result> { + _other: (value: core.Fetcher.Error) => _Result; + } +} + +export const Error = { + _unknown: ( + fetcherError: core.Fetcher.Error + ): Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Unknown => { + return { + statusCode: undefined, + content: fetcherError, + _visit: function <_Result>( + this: Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Unknown, + visitor: Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Visitor<_Result> + ) { + return Fiddle.endpoints.contentType.postJsonPatchContentType.Error._visit(this, visitor); + }, + }; + }, + + _visit: <_Result>( + value: Fiddle.endpoints.contentType.postJsonPatchContentType.Error, + visitor: Fiddle.endpoints.contentType.postJsonPatchContentType.Error._Visitor<_Result> + ): _Result => { + switch (value.statusCode) { + default: + return visitor._other(value as any); + } + }, +} as const; diff --git a/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentWithCharsetType.ts b/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentWithCharsetType.ts new file mode 100644 index 00000000000..69cbf6e95a0 --- /dev/null +++ b/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/contentType/client/postJsonPatchContentWithCharsetType.ts @@ -0,0 +1,52 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as Fiddle from "../../../../../index"; +import * as core from "../../../../../../core"; + +export type Error = Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Unknown; + +export declare namespace Error { + interface _Unknown extends _Utils { + statusCode: void; + content: core.Fetcher.Error; + } + + interface _Utils { + _visit: <_Result>( + visitor: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Visitor<_Result> + ) => _Result; + } + + interface _Visitor<_Result> { + _other: (value: core.Fetcher.Error) => _Result; + } +} + +export const Error = { + _unknown: ( + fetcherError: core.Fetcher.Error + ): Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Unknown => { + return { + statusCode: undefined, + content: fetcherError, + _visit: function <_Result>( + this: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Unknown, + visitor: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Visitor<_Result> + ) { + return Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._visit(this, visitor); + }, + }; + }, + + _visit: <_Result>( + value: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error, + visitor: Fiddle.endpoints.contentType.postJsonPatchContentWithCharsetType.Error._Visitor<_Result> + ): _Result => { + switch (value.statusCode) { + default: + return visitor._other(value as any); + } + }, +} as const; diff --git a/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/contentType/index.ts b/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/contentType/index.ts new file mode 100644 index 00000000000..5ec76921e18 --- /dev/null +++ b/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/contentType/index.ts @@ -0,0 +1 @@ +export * from "./client"; diff --git a/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/index.ts b/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/index.ts index b5b5b656b2b..b82087be453 100644 --- a/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/index.ts +++ b/seed/ts-sdk/exhaustive/dev-dependencies/src/api/resources/endpoints/resources/index.ts @@ -1,4 +1,5 @@ export * as container from "./container"; +export * as contentType from "./contentType"; export * as enum_ from "./enum"; export * as httpMethods from "./httpMethods"; export * as object from "./object"; diff --git a/seed/ts-sdk/exhaustive/jsr/.mock/definition/endpoints/content-type.yml b/seed/ts-sdk/exhaustive/jsr/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ts-sdk/exhaustive/jsr/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ts-sdk/exhaustive/jsr/reference.md b/seed/ts-sdk/exhaustive/jsr/reference.md index bd6d817d278..626ea450013 100644 --- a/seed/ts-sdk/exhaustive/jsr/reference.md +++ b/seed/ts-sdk/exhaustive/jsr/reference.md @@ -359,6 +359,136 @@ await client.endpoints.container.getAndReturnOptional({
+## Endpoints ContentType + +
client.endpoints.contentType.postJsonPatchContentType({ ...params }) -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentType.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.endpoints.contentType.postJsonPatchContentWithCharsetType({ ...params }) -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentType.RequestOptions` + +
+
+
+
+ +
+
+
+ ## Endpoints Enum
client.endpoints.enum.getAndReturnEnum({ ...params }) -> SeedExhaustive.WeatherReport diff --git a/seed/ts-sdk/exhaustive/jsr/resolved-snippet-templates.md b/seed/ts-sdk/exhaustive/jsr/resolved-snippet-templates.md index 167ce502ce3..03fcc43186c 100644 --- a/seed/ts-sdk/exhaustive/jsr/resolved-snippet-templates.md +++ b/seed/ts-sdk/exhaustive/jsr/resolved-snippet-templates.md @@ -112,6 +112,62 @@ await client.endpoints.container.getAndReturnOptional({ ``` +```typescript +import { SeedExhaustiveClient } from "@fern/exhaustive"; + +const client = new SeedExhaustiveClient({ + environment: "YOUR_BASE_URL", + token: "YOUR_TOKEN", +}); +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); + +``` + + +```typescript +import { SeedExhaustiveClient } from "@fern/exhaustive"; + +const client = new SeedExhaustiveClient({ + environment: "YOUR_BASE_URL", + token: "YOUR_TOKEN", +}); +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); + +``` + + ```typescript import { SeedExhaustiveClient } from "@fern/exhaustive"; diff --git a/seed/ts-sdk/exhaustive/jsr/snippet-templates.json b/seed/ts-sdk/exhaustive/jsr/snippet-templates.json index 2abba309172..6d46a90fcf6 100644 --- a/seed/ts-sdk/exhaustive/jsr/snippet-templates.json +++ b/seed/ts-sdk/exhaustive/jsr/snippet-templates.json @@ -836,6 +836,706 @@ "type": "v1" } }, + { + "sdk": { + "package": "@fern/exhaustive", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { SeedExhaustiveClient } from \"@fern/exhaustive\";" + ], + "templateString": "const client = new SeedExhaustiveClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "token: $FERN_INPUT", + "isOptional": false, + "templateInputs": [ + { + "location": "AUTH", + "path": "token", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.endpoints.contentType.postJsonPatchContentType(\n\t$FERN_INPUT\n)", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{\n\t\t$FERN_INPUT\n\t}", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "string: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "string", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "integer: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "integer", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "long: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "long", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "double: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "double", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bool: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bool", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "datetime: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "datetime", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "date: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "date", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "uuid: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "uuid", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "base64: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "base64", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "list: [\n\t\t\t\t$FERN_INPUT\n\t\t\t]", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "list", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "set: new Set([\n\t\t\t\t$FERN_INPUT\n\t\t\t])", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "set", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "map: {\n\t\t\t\t$FERN_INPUT\n\t\t\t}", + "delimiter": ",\n\t\t\t\t", + "keyValueSeparator": ": ", + "keyTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "valueTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "map", + "type": "payload" + }, + "type": "dict" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bigint: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bigint", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "v1" + } + }, + { + "sdk": { + "package": "@fern/exhaustive", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { SeedExhaustiveClient } from \"@fern/exhaustive\";" + ], + "templateString": "const client = new SeedExhaustiveClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "token: $FERN_INPUT", + "isOptional": false, + "templateInputs": [ + { + "location": "AUTH", + "path": "token", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.endpoints.contentType.postJsonPatchContentWithCharsetType(\n\t$FERN_INPUT\n)", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{\n\t\t$FERN_INPUT\n\t}", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "string: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "string", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "integer: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "integer", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "long: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "long", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "double: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "double", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bool: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bool", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "datetime: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "datetime", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "date: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "date", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "uuid: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "uuid", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "base64: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "base64", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "list: [\n\t\t\t\t$FERN_INPUT\n\t\t\t]", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "list", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "set: new Set([\n\t\t\t\t$FERN_INPUT\n\t\t\t])", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "set", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "map: {\n\t\t\t\t$FERN_INPUT\n\t\t\t}", + "delimiter": ",\n\t\t\t\t", + "keyValueSeparator": ": ", + "keyTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "valueTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "map", + "type": "payload" + }, + "type": "dict" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bigint: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bigint", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "v1" + } + }, { "sdk": { "package": "@fern/exhaustive", diff --git a/seed/ts-sdk/exhaustive/jsr/snippet.json b/seed/ts-sdk/exhaustive/jsr/snippet.json index 3fc62172e0c..b7be73b97c3 100644 --- a/seed/ts-sdk/exhaustive/jsr/snippet.json +++ b/seed/ts-sdk/exhaustive/jsr/snippet.json @@ -77,6 +77,28 @@ "client": "import { SeedExhaustiveClient, SeedExhaustive } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnOptional({\n string: \"string\"\n});\n" } }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: \"2024-01-15T09:30:00Z\",\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentWithCharsetType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: \"2024-01-15T09:30:00Z\",\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, { "id": { "path": "/enum", diff --git a/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/client/Client.ts b/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/client/Client.ts index 77d5a57856b..fecb304d6bb 100644 --- a/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/client/Client.ts +++ b/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/client/Client.ts @@ -4,6 +4,7 @@ import * as core from "../../../../core"; import { Container } from "../resources/container/client/Client"; +import { ContentType } from "../resources/contentType/client/Client"; import { Enum } from "../resources/enum/client/Client"; import { HttpMethods } from "../resources/httpMethods/client/Client"; import { Object_ } from "../resources/object/client/Client"; @@ -36,6 +37,12 @@ export class Endpoints { return (this._container ??= new Container(this._options)); } + protected _contentType: ContentType | undefined; + + public get contentType(): ContentType { + return (this._contentType ??= new ContentType(this._options)); + } + protected _enum: Enum | undefined; public get enum(): Enum { diff --git a/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/resources/contentType/client/Client.ts b/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/resources/contentType/client/Client.ts new file mode 100644 index 00000000000..8da6ef22957 --- /dev/null +++ b/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/resources/contentType/client/Client.ts @@ -0,0 +1,182 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as core from "../../../../../../core"; +import * as SeedExhaustive from "../../../../../index"; +import * as serializers from "../../../../../../serialization/index"; +import urlJoin from "url-join"; +import * as errors from "../../../../../../errors/index"; + +export declare namespace ContentType { + interface Options { + environment: core.Supplier; + token?: core.Supplier; + } + + interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + } +} + +export class ContentType { + constructor(protected readonly _options: ContentType.Options) {} + + /** + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {ContentType.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: "2024-01-15T09:30:00Z", + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public async postJsonPatchContentType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentType.RequestOptions + ): Promise { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/foo/bar"), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json-patch+json", + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { unrecognizedObjectKeys: "strip" }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.SeedExhaustiveTimeoutError(); + case "unknown": + throw new errors.SeedExhaustiveError({ + message: _response.error.errorMessage, + }); + } + } + + /** + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {ContentType.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: "2024-01-15T09:30:00Z", + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public async postJsonPatchContentWithCharsetType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentType.RequestOptions + ): Promise { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/foo/baz"), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json-patch+json; charset=utf-8", + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { unrecognizedObjectKeys: "strip" }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.SeedExhaustiveTimeoutError(); + case "unknown": + throw new errors.SeedExhaustiveError({ + message: _response.error.errorMessage, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + const bearer = await core.Supplier.get(this._options.token); + if (bearer != null) { + return `Bearer ${bearer}`; + } + + return undefined; + } +} diff --git a/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/resources/contentType/client/index.ts b/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/resources/contentType/client/index.ts new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/resources/contentType/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/resources/contentType/index.ts b/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/resources/contentType/index.ts new file mode 100644 index 00000000000..5ec76921e18 --- /dev/null +++ b/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/resources/contentType/index.ts @@ -0,0 +1 @@ +export * from "./client"; diff --git a/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/resources/index.ts b/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/resources/index.ts index b5b5b656b2b..b82087be453 100644 --- a/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/resources/index.ts +++ b/seed/ts-sdk/exhaustive/jsr/src/api/resources/endpoints/resources/index.ts @@ -1,4 +1,5 @@ export * as container from "./container"; +export * as contentType from "./contentType"; export * as enum_ from "./enum"; export * as httpMethods from "./httpMethods"; export * as object from "./object"; diff --git a/seed/ts-sdk/exhaustive/no-custom-config/.mock/definition/endpoints/content-type.yml b/seed/ts-sdk/exhaustive/no-custom-config/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ts-sdk/exhaustive/no-custom-config/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ts-sdk/exhaustive/no-custom-config/reference.md b/seed/ts-sdk/exhaustive/no-custom-config/reference.md index bd6d817d278..626ea450013 100644 --- a/seed/ts-sdk/exhaustive/no-custom-config/reference.md +++ b/seed/ts-sdk/exhaustive/no-custom-config/reference.md @@ -359,6 +359,136 @@ await client.endpoints.container.getAndReturnOptional({
+## Endpoints ContentType + +
client.endpoints.contentType.postJsonPatchContentType({ ...params }) -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentType.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.endpoints.contentType.postJsonPatchContentWithCharsetType({ ...params }) -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentType.RequestOptions` + +
+
+
+
+ +
+
+
+ ## Endpoints Enum
client.endpoints.enum.getAndReturnEnum({ ...params }) -> SeedExhaustive.WeatherReport diff --git a/seed/ts-sdk/exhaustive/no-custom-config/resolved-snippet-templates.md b/seed/ts-sdk/exhaustive/no-custom-config/resolved-snippet-templates.md index 167ce502ce3..03fcc43186c 100644 --- a/seed/ts-sdk/exhaustive/no-custom-config/resolved-snippet-templates.md +++ b/seed/ts-sdk/exhaustive/no-custom-config/resolved-snippet-templates.md @@ -112,6 +112,62 @@ await client.endpoints.container.getAndReturnOptional({ ``` +```typescript +import { SeedExhaustiveClient } from "@fern/exhaustive"; + +const client = new SeedExhaustiveClient({ + environment: "YOUR_BASE_URL", + token: "YOUR_TOKEN", +}); +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); + +``` + + +```typescript +import { SeedExhaustiveClient } from "@fern/exhaustive"; + +const client = new SeedExhaustiveClient({ + environment: "YOUR_BASE_URL", + token: "YOUR_TOKEN", +}); +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); + +``` + + ```typescript import { SeedExhaustiveClient } from "@fern/exhaustive"; diff --git a/seed/ts-sdk/exhaustive/no-custom-config/snippet-templates.json b/seed/ts-sdk/exhaustive/no-custom-config/snippet-templates.json index 2abba309172..6d46a90fcf6 100644 --- a/seed/ts-sdk/exhaustive/no-custom-config/snippet-templates.json +++ b/seed/ts-sdk/exhaustive/no-custom-config/snippet-templates.json @@ -836,6 +836,706 @@ "type": "v1" } }, + { + "sdk": { + "package": "@fern/exhaustive", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { SeedExhaustiveClient } from \"@fern/exhaustive\";" + ], + "templateString": "const client = new SeedExhaustiveClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "token: $FERN_INPUT", + "isOptional": false, + "templateInputs": [ + { + "location": "AUTH", + "path": "token", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.endpoints.contentType.postJsonPatchContentType(\n\t$FERN_INPUT\n)", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{\n\t\t$FERN_INPUT\n\t}", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "string: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "string", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "integer: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "integer", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "long: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "long", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "double: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "double", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bool: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bool", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "datetime: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "datetime", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "date: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "date", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "uuid: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "uuid", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "base64: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "base64", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "list: [\n\t\t\t\t$FERN_INPUT\n\t\t\t]", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "list", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "set: new Set([\n\t\t\t\t$FERN_INPUT\n\t\t\t])", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "set", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "map: {\n\t\t\t\t$FERN_INPUT\n\t\t\t}", + "delimiter": ",\n\t\t\t\t", + "keyValueSeparator": ": ", + "keyTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "valueTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "map", + "type": "payload" + }, + "type": "dict" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bigint: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bigint", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "v1" + } + }, + { + "sdk": { + "package": "@fern/exhaustive", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { SeedExhaustiveClient } from \"@fern/exhaustive\";" + ], + "templateString": "const client = new SeedExhaustiveClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "token: $FERN_INPUT", + "isOptional": false, + "templateInputs": [ + { + "location": "AUTH", + "path": "token", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.endpoints.contentType.postJsonPatchContentWithCharsetType(\n\t$FERN_INPUT\n)", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{\n\t\t$FERN_INPUT\n\t}", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "string: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "string", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "integer: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "integer", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "long: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "long", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "double: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "double", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bool: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bool", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "datetime: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "datetime", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "date: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "date", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "uuid: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "uuid", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "base64: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "base64", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "list: [\n\t\t\t\t$FERN_INPUT\n\t\t\t]", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "list", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "set: new Set([\n\t\t\t\t$FERN_INPUT\n\t\t\t])", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "set", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "map: {\n\t\t\t\t$FERN_INPUT\n\t\t\t}", + "delimiter": ",\n\t\t\t\t", + "keyValueSeparator": ": ", + "keyTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "valueTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "map", + "type": "payload" + }, + "type": "dict" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bigint: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bigint", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "v1" + } + }, { "sdk": { "package": "@fern/exhaustive", diff --git a/seed/ts-sdk/exhaustive/no-custom-config/snippet.json b/seed/ts-sdk/exhaustive/no-custom-config/snippet.json index 3fc62172e0c..b7be73b97c3 100644 --- a/seed/ts-sdk/exhaustive/no-custom-config/snippet.json +++ b/seed/ts-sdk/exhaustive/no-custom-config/snippet.json @@ -77,6 +77,28 @@ "client": "import { SeedExhaustiveClient, SeedExhaustive } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnOptional({\n string: \"string\"\n});\n" } }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: \"2024-01-15T09:30:00Z\",\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentWithCharsetType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: \"2024-01-15T09:30:00Z\",\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, { "id": { "path": "/enum", diff --git a/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/client/Client.ts b/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/client/Client.ts index 77d5a57856b..fecb304d6bb 100644 --- a/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/client/Client.ts +++ b/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/client/Client.ts @@ -4,6 +4,7 @@ import * as core from "../../../../core"; import { Container } from "../resources/container/client/Client"; +import { ContentType } from "../resources/contentType/client/Client"; import { Enum } from "../resources/enum/client/Client"; import { HttpMethods } from "../resources/httpMethods/client/Client"; import { Object_ } from "../resources/object/client/Client"; @@ -36,6 +37,12 @@ export class Endpoints { return (this._container ??= new Container(this._options)); } + protected _contentType: ContentType | undefined; + + public get contentType(): ContentType { + return (this._contentType ??= new ContentType(this._options)); + } + protected _enum: Enum | undefined; public get enum(): Enum { diff --git a/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/contentType/client/Client.ts b/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/contentType/client/Client.ts new file mode 100644 index 00000000000..8da6ef22957 --- /dev/null +++ b/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/contentType/client/Client.ts @@ -0,0 +1,182 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as core from "../../../../../../core"; +import * as SeedExhaustive from "../../../../../index"; +import * as serializers from "../../../../../../serialization/index"; +import urlJoin from "url-join"; +import * as errors from "../../../../../../errors/index"; + +export declare namespace ContentType { + interface Options { + environment: core.Supplier; + token?: core.Supplier; + } + + interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + } +} + +export class ContentType { + constructor(protected readonly _options: ContentType.Options) {} + + /** + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {ContentType.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: "2024-01-15T09:30:00Z", + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public async postJsonPatchContentType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentType.RequestOptions + ): Promise { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/foo/bar"), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json-patch+json", + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { unrecognizedObjectKeys: "strip" }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.SeedExhaustiveTimeoutError(); + case "unknown": + throw new errors.SeedExhaustiveError({ + message: _response.error.errorMessage, + }); + } + } + + /** + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {ContentType.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: "2024-01-15T09:30:00Z", + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public async postJsonPatchContentWithCharsetType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentType.RequestOptions + ): Promise { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/foo/baz"), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json-patch+json; charset=utf-8", + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { unrecognizedObjectKeys: "strip" }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.SeedExhaustiveTimeoutError(); + case "unknown": + throw new errors.SeedExhaustiveError({ + message: _response.error.errorMessage, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + const bearer = await core.Supplier.get(this._options.token); + if (bearer != null) { + return `Bearer ${bearer}`; + } + + return undefined; + } +} diff --git a/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/contentType/client/index.ts b/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/contentType/client/index.ts new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/contentType/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/contentType/index.ts b/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/contentType/index.ts new file mode 100644 index 00000000000..5ec76921e18 --- /dev/null +++ b/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/contentType/index.ts @@ -0,0 +1 @@ +export * from "./client"; diff --git a/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/index.ts b/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/index.ts index b5b5b656b2b..b82087be453 100644 --- a/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/index.ts +++ b/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/index.ts @@ -1,4 +1,5 @@ export * as container from "./container"; +export * as contentType from "./contentType"; export * as enum_ from "./enum"; export * as httpMethods from "./httpMethods"; export * as object from "./object"; diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/.mock/definition/endpoints/content-type.yml b/seed/ts-sdk/exhaustive/retain-original-casing/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ts-sdk/exhaustive/retain-original-casing/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/reference.md b/seed/ts-sdk/exhaustive/retain-original-casing/reference.md index 2eb64256e95..30aef507b8a 100644 --- a/seed/ts-sdk/exhaustive/retain-original-casing/reference.md +++ b/seed/ts-sdk/exhaustive/retain-original-casing/reference.md @@ -359,6 +359,136 @@ await client.endpoints.container.getAndReturnOptional({
+## Endpoints ContentType + +
client.endpoints.contentType.postJsonPatchContentType({ ...params }) -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentType.RequestOptions` + +
+
+
+
+ +
+
+
+ +
client.endpoints.contentType.postJsonPatchContentWithCharsetType({ ...params }) -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `SeedExhaustive.ObjectWithOptionalField` + +
+
+ +
+
+ +**requestOptions:** `ContentType.RequestOptions` + +
+
+
+
+ +
+
+
+ ## Endpoints Enum
client.endpoints.enum.getAndReturnEnum({ ...params }) -> SeedExhaustive.WeatherReport diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/resolved-snippet-templates.md b/seed/ts-sdk/exhaustive/retain-original-casing/resolved-snippet-templates.md index 97b0e47ebd7..c9da1596e10 100644 --- a/seed/ts-sdk/exhaustive/retain-original-casing/resolved-snippet-templates.md +++ b/seed/ts-sdk/exhaustive/retain-original-casing/resolved-snippet-templates.md @@ -112,6 +112,62 @@ await client.endpoints.container.getAndReturnOptional({ ``` +```typescript +import { SeedExhaustiveClient } from "@fern/exhaustive"; + +const client = new SeedExhaustiveClient({ + environment: "YOUR_BASE_URL", + token: "YOUR_TOKEN", +}); +await client.endpoints.contentType.postJsonPatchContentType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); + +``` + + +```typescript +import { SeedExhaustiveClient } from "@fern/exhaustive"; + +const client = new SeedExhaustiveClient({ + environment: "YOUR_BASE_URL", + token: "YOUR_TOKEN", +}); +await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: "2024-01-15T09:30:00Z", + date: "2023-01-15", + uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + base64: "SGVsbG8gd29ybGQh", + list: ["list", "list"], + set: new Set(["set"]), + map: { + 1: "map", + }, + bigint: "1000000", +}); + +``` + + ```typescript import { SeedExhaustiveClient } from "@fern/exhaustive"; diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/snippet-templates.json b/seed/ts-sdk/exhaustive/retain-original-casing/snippet-templates.json index ed8d70f0df1..6cd32a5ce78 100644 --- a/seed/ts-sdk/exhaustive/retain-original-casing/snippet-templates.json +++ b/seed/ts-sdk/exhaustive/retain-original-casing/snippet-templates.json @@ -836,6 +836,706 @@ "type": "v1" } }, + { + "sdk": { + "package": "@fern/exhaustive", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/foo/bar", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { SeedExhaustiveClient } from \"@fern/exhaustive\";" + ], + "templateString": "const client = new SeedExhaustiveClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "token: $FERN_INPUT", + "isOptional": false, + "templateInputs": [ + { + "location": "AUTH", + "path": "token", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.endpoints.contentType.postJsonPatchContentType(\n\t$FERN_INPUT\n)", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{\n\t\t$FERN_INPUT\n\t}", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "string: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "string", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "integer: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "integer", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "long: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "long", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "double: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "double", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bool: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bool", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "datetime: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "datetime", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "date: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "date", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "uuid: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "uuid", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "base64: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "base64", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "list: [\n\t\t\t\t$FERN_INPUT\n\t\t\t]", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "list", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "set: new Set([\n\t\t\t\t$FERN_INPUT\n\t\t\t])", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "set", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "map: {\n\t\t\t\t$FERN_INPUT\n\t\t\t}", + "delimiter": ",\n\t\t\t\t", + "keyValueSeparator": ": ", + "keyTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "valueTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "map", + "type": "payload" + }, + "type": "dict" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bigint: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bigint", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "v1" + } + }, + { + "sdk": { + "package": "@fern/exhaustive", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/foo/baz", + "method": "POST", + "identifierOverride": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { SeedExhaustiveClient } from \"@fern/exhaustive\";" + ], + "templateString": "const client = new SeedExhaustiveClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "token: $FERN_INPUT", + "isOptional": false, + "templateInputs": [ + { + "location": "AUTH", + "path": "token", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.endpoints.contentType.postJsonPatchContentWithCharsetType(\n\t$FERN_INPUT\n)", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{\n\t\t$FERN_INPUT\n\t}", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "inputDelimiter": ",\n\t\t", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "string: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "string", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "integer: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "integer", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "long: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "long", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "double: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "double", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bool: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bool", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "datetime: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "datetime", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "date: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "date", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "uuid: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "uuid", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "base64: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "base64", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "list: [\n\t\t\t\t$FERN_INPUT\n\t\t\t]", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "list", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "set: new Set([\n\t\t\t\t$FERN_INPUT\n\t\t\t])", + "delimiter": ",\n\t\t\t\t", + "innerTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "set", + "type": "payload" + }, + "type": "iterable" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "isOptional": true, + "containerTemplateString": "map: {\n\t\t\t\t$FERN_INPUT\n\t\t\t}", + "delimiter": ",\n\t\t\t\t", + "keyValueSeparator": ": ", + "keyTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "valueTemplate": { + "imports": [], + "templateString": "$FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "RELATIVE", + "type": "payload" + } + ], + "type": "generic" + }, + "templateInput": { + "location": "BODY", + "path": "map", + "type": "payload" + }, + "type": "dict" + }, + "type": "template" + }, + { + "value": { + "imports": [], + "templateString": "bigint: $FERN_INPUT", + "isOptional": true, + "templateInputs": [ + { + "location": "BODY", + "path": "bigint", + "type": "payload" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "v1" + } + }, { "sdk": { "package": "@fern/exhaustive", diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/snippet.json b/seed/ts-sdk/exhaustive/retain-original-casing/snippet.json index 2880bf20038..a63b634ce14 100644 --- a/seed/ts-sdk/exhaustive/retain-original-casing/snippet.json +++ b/seed/ts-sdk/exhaustive/retain-original-casing/snippet.json @@ -77,6 +77,28 @@ "client": "import { SeedExhaustiveClient, SeedExhaustive } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.container.getAndReturnOptional({\n string: \"string\"\n});\n" } }, + { + "id": { + "path": "/foo/bar", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentType" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: \"2024-01-15T09:30:00Z\",\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, + { + "id": { + "path": "/foo/baz", + "method": "POST", + "identifier_override": "endpoint_endpoints/content-type.postJsonPatchContentWithCharsetType" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.contentType.postJsonPatchContentWithCharsetType({\n string: \"string\",\n integer: 1,\n long: 1000000,\n double: 1.1,\n bool: true,\n datetime: \"2024-01-15T09:30:00Z\",\n date: \"2023-01-15\",\n uuid: \"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\",\n base64: \"SGVsbG8gd29ybGQh\",\n list: [\"list\", \"list\"],\n set: new Set([\"set\"]),\n map: {\n 1: \"map\"\n },\n bigint: \"1000000\"\n});\n" + } + }, { "id": { "path": "/enum", diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/client/Client.ts b/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/client/Client.ts index 77d5a57856b..fecb304d6bb 100644 --- a/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/client/Client.ts +++ b/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/client/Client.ts @@ -4,6 +4,7 @@ import * as core from "../../../../core"; import { Container } from "../resources/container/client/Client"; +import { ContentType } from "../resources/contentType/client/Client"; import { Enum } from "../resources/enum/client/Client"; import { HttpMethods } from "../resources/httpMethods/client/Client"; import { Object_ } from "../resources/object/client/Client"; @@ -36,6 +37,12 @@ export class Endpoints { return (this._container ??= new Container(this._options)); } + protected _contentType: ContentType | undefined; + + public get contentType(): ContentType { + return (this._contentType ??= new ContentType(this._options)); + } + protected _enum: Enum | undefined; public get enum(): Enum { diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/contentType/client/Client.ts b/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/contentType/client/Client.ts new file mode 100644 index 00000000000..8da6ef22957 --- /dev/null +++ b/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/contentType/client/Client.ts @@ -0,0 +1,182 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as core from "../../../../../../core"; +import * as SeedExhaustive from "../../../../../index"; +import * as serializers from "../../../../../../serialization/index"; +import urlJoin from "url-join"; +import * as errors from "../../../../../../errors/index"; + +export declare namespace ContentType { + interface Options { + environment: core.Supplier; + token?: core.Supplier; + } + + interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + } +} + +export class ContentType { + constructor(protected readonly _options: ContentType.Options) {} + + /** + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {ContentType.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: "2024-01-15T09:30:00Z", + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public async postJsonPatchContentType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentType.RequestOptions + ): Promise { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/foo/bar"), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json-patch+json", + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { unrecognizedObjectKeys: "strip" }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.SeedExhaustiveTimeoutError(); + case "unknown": + throw new errors.SeedExhaustiveError({ + message: _response.error.errorMessage, + }); + } + } + + /** + * @param {SeedExhaustive.types.ObjectWithOptionalField} request + * @param {ContentType.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.endpoints.contentType.postJsonPatchContentWithCharsetType({ + * string: "string", + * integer: 1, + * long: 1000000, + * double: 1.1, + * bool: true, + * datetime: "2024-01-15T09:30:00Z", + * date: "2023-01-15", + * uuid: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + * base64: "SGVsbG8gd29ybGQh", + * list: ["list", "list"], + * set: new Set(["set"]), + * map: { + * 1: "map" + * }, + * bigint: "1000000" + * }) + */ + public async postJsonPatchContentWithCharsetType( + request: SeedExhaustive.types.ObjectWithOptionalField, + requestOptions?: ContentType.RequestOptions + ): Promise { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/foo/baz"), + method: "POST", + headers: { + Authorization: await this._getAuthorizationHeader(), + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/exhaustive", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/exhaustive/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json-patch+json; charset=utf-8", + requestType: "json", + body: serializers.types.ObjectWithOptionalField.jsonOrThrow(request, { unrecognizedObjectKeys: "strip" }), + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.SeedExhaustiveTimeoutError(); + case "unknown": + throw new errors.SeedExhaustiveError({ + message: _response.error.errorMessage, + }); + } + } + + protected async _getAuthorizationHeader(): Promise { + const bearer = await core.Supplier.get(this._options.token); + if (bearer != null) { + return `Bearer ${bearer}`; + } + + return undefined; + } +} diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/contentType/client/index.ts b/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/contentType/client/index.ts new file mode 100644 index 00000000000..cb0ff5c3b54 --- /dev/null +++ b/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/contentType/client/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/contentType/index.ts b/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/contentType/index.ts new file mode 100644 index 00000000000..5ec76921e18 --- /dev/null +++ b/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/contentType/index.ts @@ -0,0 +1 @@ +export * from "./client"; diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/index.ts b/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/index.ts index b5b5b656b2b..b82087be453 100644 --- a/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/index.ts +++ b/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/index.ts @@ -1,4 +1,5 @@ export * as container from "./container"; +export * as contentType from "./contentType"; export * as enum_ from "./enum"; export * as httpMethods from "./httpMethods"; export * as object from "./object"; diff --git a/seed/ts-sdk/exhaustive/with-audiences/.mock/definition/endpoints/content-type.yml b/seed/ts-sdk/exhaustive/with-audiences/.mock/definition/endpoints/content-type.yml new file mode 100644 index 00000000000..7c54e39fa5a --- /dev/null +++ b/seed/ts-sdk/exhaustive/with-audiences/.mock/definition/endpoints/content-type.yml @@ -0,0 +1,19 @@ +imports: + objects: ../types/object.yml + +service: + auth: true + base-path: /foo + endpoints: + postJsonPatchContentType: + path: /bar + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json + postJsonPatchContentWithCharsetType: + path: /baz + method: POST + request: + body: objects.ObjectWithOptionalField + content-type: application/json-patch+json; charset=utf-8 diff --git a/seed/ts-sdk/grpc-proto-exhaustive/.mock/generators.yml b/seed/ts-sdk/grpc-proto-exhaustive/.mock/generators.yml index c23323621f2..972ed6d7b73 100644 --- a/seed/ts-sdk/grpc-proto-exhaustive/.mock/generators.yml +++ b/seed/ts-sdk/grpc-proto-exhaustive/.mock/generators.yml @@ -1,7 +1,6 @@ api: - - path: openapi/openapi.yml - - proto: - root: proto - target: proto/data/v1/data.proto - overrides: overrides.yml - local-generation: true + - proto: + root: proto + target: proto/data/v1/data.proto + overrides: overrides.yml + local-generation: true \ No newline at end of file diff --git a/seed/ts-sdk/grpc-proto-exhaustive/.mock/openapi/openapi.yml b/seed/ts-sdk/grpc-proto-exhaustive/.mock/openapi/openapi.yml deleted file mode 100644 index ebc23143df3..00000000000 --- a/seed/ts-sdk/grpc-proto-exhaustive/.mock/openapi/openapi.yml +++ /dev/null @@ -1,33 +0,0 @@ -openapi: 3.0.3 -info: - title: Test API - version: 1.0.0 -servers: - - url: https://localhost -tags: - - name: dataservice -paths: - /foo: - post: - tag: dataservice - x-fern-sdk-group-name: - - dataservice - x-fern-sdk-method-name: foo - security: - - ApiKeyAuth: [] - operationId: foo - responses: - "200": - content: - application/json: - schema: - type: object - -security: - - ApiKeyAuth: [] -components: - securitySchemes: - ApiKeyAuth: - type: apiKey - in: header - name: X-API-Key diff --git a/seed/ts-sdk/grpc-proto-exhaustive/README.md b/seed/ts-sdk/grpc-proto-exhaustive/README.md index 6afc2becf4d..1d744ab0d4b 100644 --- a/seed/ts-sdk/grpc-proto-exhaustive/README.md +++ b/seed/ts-sdk/grpc-proto-exhaustive/README.md @@ -20,10 +20,17 @@ A full reference for this library is available [here](./reference.md). Instantiate and use the client with the following: ```typescript -import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; - -const client = new SeedApiClient({ apiKey: "YOUR_API_KEY" }); -await client.dataservice.foo(); +import { SeedApiClient, SeedApi } from "@fern/grpc-proto-exhaustive"; + +const client = new SeedApiClient({ environment: "YOUR_BASE_URL" }); +await client.dataservice.upload({ + columns: [ + { + id: "id", + values: [1.1], + }, + ], +}); ``` ## Request And Response Types @@ -48,7 +55,7 @@ will be thrown. import { SeedApiError } from "@fern/grpc-proto-exhaustive"; try { - await client.dataservice.foo(...); + await client.dataservice.upload(...); } catch (err) { if (err instanceof SeedApiError) { console.log(err.statusCode); @@ -75,7 +82,7 @@ A request is deemed retriable when any of the following HTTP status codes is ret Use the `maxRetries` request option to configure this behavior. ```typescript -const response = await client.dataservice.foo(..., { +const response = await client.dataservice.upload(..., { maxRetries: 0 // override maxRetries at the request level }); ``` @@ -85,7 +92,7 @@ const response = await client.dataservice.foo(..., { The SDK defaults to a 60 second timeout. Use the `timeoutInSeconds` option to configure this behavior. ```typescript -const response = await client.dataservice.foo(..., { +const response = await client.dataservice.upload(..., { timeoutInSeconds: 30 // override timeout to 30s }); ``` @@ -96,7 +103,7 @@ The SDK allows users to abort requests at any point by passing in an abort signa ```typescript const controller = new AbortController(); -const response = await client.dataservice.foo(..., { +const response = await client.dataservice.upload(..., { abortSignal: controller.signal }); controller.abort(); // aborts the request diff --git a/seed/ts-sdk/grpc-proto-exhaustive/reference.md b/seed/ts-sdk/grpc-proto-exhaustive/reference.md index 74f0b1a937e..3c3c3ce64ee 100644 --- a/seed/ts-sdk/grpc-proto-exhaustive/reference.md +++ b/seed/ts-sdk/grpc-proto-exhaustive/reference.md @@ -2,46 +2,6 @@ ## DataService -
client.dataservice.foo() -> Record -
-
- -#### 🔌 Usage - -
-
- -
-
- -```typescript -await client.dataservice.foo(); -``` - -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**requestOptions:** `Dataservice.RequestOptions` - -
-
-
-
- -
-
-
-
client.dataservice.upload({ ...params }) -> SeedApi.UploadResponse
diff --git a/seed/ts-sdk/grpc-proto-exhaustive/resolved-snippet-templates.md b/seed/ts-sdk/grpc-proto-exhaustive/resolved-snippet-templates.md index d8072715ed0..0b164384dfa 100644 --- a/seed/ts-sdk/grpc-proto-exhaustive/resolved-snippet-templates.md +++ b/seed/ts-sdk/grpc-proto-exhaustive/resolved-snippet-templates.md @@ -1,25 +1,7 @@ ```typescript import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; -const client = new SeedApiClient({ apiKey: "YOUR_AUTHORIZATION" }); -await client.dataservice.foo(); - -``` - - -```typescript -import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; - -const client = new SeedApiClient({ apiKey: "YOUR_AUTHORIZATION" }); -await client.dataservice.foo(); - -``` - - -```typescript -import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; - -const client = new SeedApiClient({ apiKey: "YOUR_AUTHORIZATION" }); +const client = new SeedApiClient({ environment: "YOUR_BASE_URL" }); await client.dataservice.upload({ columns: [ { @@ -34,7 +16,7 @@ await client.dataservice.upload({ ```typescript import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; -const client = new SeedApiClient({ apiKey: "YOUR_AUTHORIZATION" }); +const client = new SeedApiClient({ environment: "YOUR_BASE_URL" }); await client.dataservice.upload({ columns: [ { @@ -53,7 +35,7 @@ await client.dataservice.upload({ ```typescript import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; -const client = new SeedApiClient({ apiKey: "YOUR_AUTHORIZATION" }); +const client = new SeedApiClient({ environment: "YOUR_BASE_URL" }); undefined; ``` @@ -62,7 +44,7 @@ undefined; ```typescript import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; -const client = new SeedApiClient({ apiKey: "YOUR_AUTHORIZATION" }); +const client = new SeedApiClient({ environment: "YOUR_BASE_URL" }); await client.dataservice.delete({ ids: ["ids", "ids"], deleteAll: true, @@ -75,7 +57,7 @@ await client.dataservice.delete({ ```typescript import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; -const client = new SeedApiClient({ apiKey: "YOUR_AUTHORIZATION" }); +const client = new SeedApiClient({ environment: "YOUR_BASE_URL" }); undefined; ``` @@ -84,7 +66,7 @@ undefined; ```typescript import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; -const client = new SeedApiClient({ apiKey: "YOUR_AUTHORIZATION" }); +const client = new SeedApiClient({ environment: "YOUR_BASE_URL" }); undefined; ``` @@ -93,7 +75,7 @@ undefined; ```typescript import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; -const client = new SeedApiClient({ apiKey: "YOUR_AUTHORIZATION" }); +const client = new SeedApiClient({ environment: "YOUR_BASE_URL" }); undefined; ``` @@ -102,7 +84,7 @@ undefined; ```typescript import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; -const client = new SeedApiClient({ apiKey: "YOUR_AUTHORIZATION" }); +const client = new SeedApiClient({ environment: "YOUR_BASE_URL" }); await client.dataservice.fetch({ ids: "ids", namespace: "namespace", @@ -114,7 +96,7 @@ await client.dataservice.fetch({ ```typescript import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; -const client = new SeedApiClient({ apiKey: "YOUR_AUTHORIZATION" }); +const client = new SeedApiClient({ environment: "YOUR_BASE_URL" }); undefined; ``` @@ -123,7 +105,7 @@ undefined; ```typescript import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; -const client = new SeedApiClient({ apiKey: "YOUR_AUTHORIZATION" }); +const client = new SeedApiClient({ environment: "YOUR_BASE_URL" }); await client.dataservice.list({ prefix: "prefix", limit: 1, @@ -137,7 +119,7 @@ await client.dataservice.list({ ```typescript import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; -const client = new SeedApiClient({ apiKey: "YOUR_AUTHORIZATION" }); +const client = new SeedApiClient({ environment: "YOUR_BASE_URL" }); await client.dataservice.query({ topK: 1, }); @@ -148,7 +130,7 @@ await client.dataservice.query({ ```typescript import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; -const client = new SeedApiClient({ apiKey: "YOUR_AUTHORIZATION" }); +const client = new SeedApiClient({ environment: "YOUR_BASE_URL" }); await client.dataservice.query({ namespace: "namespace", topK: 1, @@ -186,7 +168,7 @@ await client.dataservice.query({ ```typescript import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; -const client = new SeedApiClient({ apiKey: "YOUR_AUTHORIZATION" }); +const client = new SeedApiClient({ environment: "YOUR_BASE_URL" }); await client.dataservice.update({ id: "id", }); @@ -197,7 +179,7 @@ await client.dataservice.update({ ```typescript import { SeedApiClient } from "@fern/grpc-proto-exhaustive"; -const client = new SeedApiClient({ apiKey: "YOUR_AUTHORIZATION" }); +const client = new SeedApiClient({ environment: "YOUR_BASE_URL" }); await client.dataservice.update({ id: "id", values: [1.1, 1.1], diff --git a/seed/ts-sdk/grpc-proto-exhaustive/snippet-templates.json b/seed/ts-sdk/grpc-proto-exhaustive/snippet-templates.json index e27fdcb4151..b8618245ee0 100644 --- a/seed/ts-sdk/grpc-proto-exhaustive/snippet-templates.json +++ b/seed/ts-sdk/grpc-proto-exhaustive/snippet-templates.json @@ -1,65 +1,4 @@ [ - { - "sdk": { - "package": "@fern/grpc-proto-exhaustive", - "version": "0.0.1", - "type": "typescript" - }, - "endpointId": { - "path": "/foo", - "method": "POST", - "identifierOverride": "endpoint_dataservice.foo" - }, - "snippetTemplate": { - "clientInstantiation": { - "imports": [ - "import { SeedApiClient } from \"@fern/grpc-proto-exhaustive\";" - ], - "templateString": "const client = new SeedApiClient($FERN_INPUT);", - "isOptional": false, - "inputDelimiter": ",", - "templateInputs": [ - { - "value": { - "imports": [], - "templateString": "{ $FERN_INPUT }", - "isOptional": true, - "templateInputs": [ - { - "value": { - "imports": [], - "templateString": "apiKey: $FERN_INPUT", - "isOptional": false, - "templateInputs": [ - { - "location": "AUTH", - "path": "Authorization", - "type": "payload" - } - ], - "type": "generic" - }, - "type": "template" - } - ], - "type": "generic" - }, - "type": "template" - } - ], - "type": "generic" - }, - "functionInvocation": { - "imports": [], - "templateString": "await client.dataservice.foo()", - "isOptional": false, - "inputDelimiter": ",\n\t", - "templateInputs": [], - "type": "generic" - }, - "type": "v1" - } - }, { "sdk": { "package": "@fern/grpc-proto-exhaustive", @@ -89,15 +28,9 @@ { "value": { "imports": [], - "templateString": "apiKey: $FERN_INPUT", + "templateString": "environment: \"YOUR_BASE_URL\"", "isOptional": false, - "templateInputs": [ - { - "location": "AUTH", - "path": "Authorization", - "type": "payload" - } - ], + "templateInputs": [], "type": "generic" }, "type": "template" @@ -489,15 +422,9 @@ { "value": { "imports": [], - "templateString": "apiKey: $FERN_INPUT", + "templateString": "environment: \"YOUR_BASE_URL\"", "isOptional": false, - "templateInputs": [ - { - "location": "AUTH", - "path": "Authorization", - "type": "payload" - } - ], + "templateInputs": [], "type": "generic" }, "type": "template" @@ -799,15 +726,9 @@ { "value": { "imports": [], - "templateString": "apiKey: $FERN_INPUT", + "templateString": "environment: \"YOUR_BASE_URL\"", "isOptional": false, - "templateInputs": [ - { - "location": "AUTH", - "path": "Authorization", - "type": "payload" - } - ], + "templateInputs": [], "type": "generic" }, "type": "template" @@ -1050,15 +971,9 @@ { "value": { "imports": [], - "templateString": "apiKey: $FERN_INPUT", + "templateString": "environment: \"YOUR_BASE_URL\"", "isOptional": false, - "templateInputs": [ - { - "location": "AUTH", - "path": "Authorization", - "type": "payload" - } - ], + "templateInputs": [], "type": "generic" }, "type": "template" @@ -1156,15 +1071,9 @@ { "value": { "imports": [], - "templateString": "apiKey: $FERN_INPUT", + "templateString": "environment: \"YOUR_BASE_URL\"", "isOptional": false, - "templateInputs": [ - { - "location": "AUTH", - "path": "Authorization", - "type": "payload" - } - ], + "templateInputs": [], "type": "generic" }, "type": "template" @@ -1294,15 +1203,9 @@ { "value": { "imports": [], - "templateString": "apiKey: $FERN_INPUT", + "templateString": "environment: \"YOUR_BASE_URL\"", "isOptional": false, - "templateInputs": [ - { - "location": "AUTH", - "path": "Authorization", - "type": "payload" - } - ], + "templateInputs": [], "type": "generic" }, "type": "template" @@ -2044,15 +1947,9 @@ { "value": { "imports": [], - "templateString": "apiKey: $FERN_INPUT", + "templateString": "environment: \"YOUR_BASE_URL\"", "isOptional": false, - "templateInputs": [ - { - "location": "AUTH", - "path": "Authorization", - "type": "payload" - } - ], + "templateInputs": [], "type": "generic" }, "type": "template" diff --git a/seed/ts-sdk/grpc-proto-exhaustive/snippet.json b/seed/ts-sdk/grpc-proto-exhaustive/snippet.json index 6b90c262f8b..fb9f9f4d68b 100644 --- a/seed/ts-sdk/grpc-proto-exhaustive/snippet.json +++ b/seed/ts-sdk/grpc-proto-exhaustive/snippet.json @@ -1,16 +1,5 @@ { "endpoints": [ - { - "id": { - "path": "/foo", - "method": "POST", - "identifier_override": "endpoint_dataservice.foo" - }, - "snippet": { - "type": "typescript", - "client": "import { SeedApiClient } from \"@fern/grpc-proto-exhaustive\";\n\nconst client = new SeedApiClient({ apiKey: \"YOUR_API_KEY\" });\nawait client.dataservice.foo();\n" - } - }, { "id": { "path": "/data", @@ -19,7 +8,7 @@ }, "snippet": { "type": "typescript", - "client": "import { SeedApiClient, SeedApi } from \"@fern/grpc-proto-exhaustive\";\n\nconst client = new SeedApiClient({ apiKey: \"YOUR_API_KEY\" });\nawait client.dataservice.upload({\n columns: [{\n id: \"id\",\n values: [1.1]\n }]\n});\n" + "client": "import { SeedApiClient, SeedApi } from \"@fern/grpc-proto-exhaustive\";\n\nconst client = new SeedApiClient({ environment: \"YOUR_BASE_URL\" });\nawait client.dataservice.upload({\n columns: [{\n id: \"id\",\n values: [1.1]\n }]\n});\n" } }, { @@ -30,7 +19,7 @@ }, "snippet": { "type": "typescript", - "client": "import { SeedApiClient, SeedApi } from \"@fern/grpc-proto-exhaustive\";\n\nconst client = new SeedApiClient({ apiKey: \"YOUR_API_KEY\" });\nawait client.dataservice.delete();\n" + "client": "import { SeedApiClient, SeedApi } from \"@fern/grpc-proto-exhaustive\";\n\nconst client = new SeedApiClient({ environment: \"YOUR_BASE_URL\" });\nawait client.dataservice.delete();\n" } }, { @@ -41,7 +30,7 @@ }, "snippet": { "type": "typescript", - "client": "import { SeedApiClient, SeedApi } from \"@fern/grpc-proto-exhaustive\";\n\nconst client = new SeedApiClient({ apiKey: \"YOUR_API_KEY\" });\nawait client.dataservice.describe();\n" + "client": "import { SeedApiClient, SeedApi } from \"@fern/grpc-proto-exhaustive\";\n\nconst client = new SeedApiClient({ environment: \"YOUR_BASE_URL\" });\nawait client.dataservice.describe();\n" } }, { @@ -52,7 +41,7 @@ }, "snippet": { "type": "typescript", - "client": "import { SeedApiClient, SeedApi } from \"@fern/grpc-proto-exhaustive\";\n\nconst client = new SeedApiClient({ apiKey: \"YOUR_API_KEY\" });\nawait client.dataservice.fetch();\n" + "client": "import { SeedApiClient, SeedApi } from \"@fern/grpc-proto-exhaustive\";\n\nconst client = new SeedApiClient({ environment: \"YOUR_BASE_URL\" });\nawait client.dataservice.fetch();\n" } }, { @@ -63,7 +52,7 @@ }, "snippet": { "type": "typescript", - "client": "import { SeedApiClient, SeedApi } from \"@fern/grpc-proto-exhaustive\";\n\nconst client = new SeedApiClient({ apiKey: \"YOUR_API_KEY\" });\nawait client.dataservice.list();\n" + "client": "import { SeedApiClient, SeedApi } from \"@fern/grpc-proto-exhaustive\";\n\nconst client = new SeedApiClient({ environment: \"YOUR_BASE_URL\" });\nawait client.dataservice.list();\n" } }, { @@ -74,7 +63,7 @@ }, "snippet": { "type": "typescript", - "client": "import { SeedApiClient, SeedApi } from \"@fern/grpc-proto-exhaustive\";\n\nconst client = new SeedApiClient({ apiKey: \"YOUR_API_KEY\" });\nawait client.dataservice.query({\n topK: 1\n});\n" + "client": "import { SeedApiClient, SeedApi } from \"@fern/grpc-proto-exhaustive\";\n\nconst client = new SeedApiClient({ environment: \"YOUR_BASE_URL\" });\nawait client.dataservice.query({\n topK: 1\n});\n" } }, { @@ -85,7 +74,7 @@ }, "snippet": { "type": "typescript", - "client": "import { SeedApiClient, SeedApi } from \"@fern/grpc-proto-exhaustive\";\n\nconst client = new SeedApiClient({ apiKey: \"YOUR_API_KEY\" });\nawait client.dataservice.update({\n id: \"id\"\n});\n" + "client": "import { SeedApiClient, SeedApi } from \"@fern/grpc-proto-exhaustive\";\n\nconst client = new SeedApiClient({ environment: \"YOUR_BASE_URL\" });\nawait client.dataservice.update({\n id: \"id\"\n});\n" } } ], diff --git a/seed/ts-sdk/grpc-proto-exhaustive/src/Client.ts b/seed/ts-sdk/grpc-proto-exhaustive/src/Client.ts index f0ef236f969..5e7e40b02de 100644 --- a/seed/ts-sdk/grpc-proto-exhaustive/src/Client.ts +++ b/seed/ts-sdk/grpc-proto-exhaustive/src/Client.ts @@ -2,14 +2,12 @@ * This file was auto-generated by Fern from our API Definition. */ -import * as environments from "./environments"; import * as core from "./core"; import { Dataservice } from "./api/resources/dataservice/client/Client"; export declare namespace SeedApiClient { interface Options { - environment?: core.Supplier; - apiKey?: core.Supplier; + environment: core.Supplier; } interface RequestOptions { @@ -23,7 +21,7 @@ export declare namespace SeedApiClient { } export class SeedApiClient { - constructor(protected readonly _options: SeedApiClient.Options = {}) {} + constructor(protected readonly _options: SeedApiClient.Options) {} protected _dataservice: Dataservice | undefined; diff --git a/seed/ts-sdk/grpc-proto-exhaustive/src/api/resources/dataservice/client/Client.ts b/seed/ts-sdk/grpc-proto-exhaustive/src/api/resources/dataservice/client/Client.ts index f16218f86c9..353ee106b3a 100644 --- a/seed/ts-sdk/grpc-proto-exhaustive/src/api/resources/dataservice/client/Client.ts +++ b/seed/ts-sdk/grpc-proto-exhaustive/src/api/resources/dataservice/client/Client.ts @@ -2,17 +2,15 @@ * This file was auto-generated by Fern from our API Definition. */ -import * as environments from "../../../../environments"; import * as core from "../../../../core"; -import urlJoin from "url-join"; +import * as SeedApi from "../../../index"; import * as serializers from "../../../../serialization/index"; +import urlJoin from "url-join"; import * as errors from "../../../../errors/index"; -import * as SeedApi from "../../../index"; export declare namespace Dataservice { interface Options { - environment?: core.Supplier; - apiKey?: core.Supplier; + environment: core.Supplier; } interface RequestOptions { @@ -26,66 +24,7 @@ export declare namespace Dataservice { } export class Dataservice { - constructor(protected readonly _options: Dataservice.Options = {}) {} - - /** - * @param {Dataservice.RequestOptions} requestOptions - Request-specific configuration. - * - * @example - * await client.dataservice.foo() - */ - public async foo(requestOptions?: Dataservice.RequestOptions): Promise> { - const _response = await core.fetcher({ - url: urlJoin( - (await core.Supplier.get(this._options.environment)) ?? environments.SeedApiEnvironment.Default, - "foo" - ), - method: "POST", - headers: { - "X-Fern-Language": "JavaScript", - "X-Fern-SDK-Name": "@fern/grpc-proto-exhaustive", - "X-Fern-SDK-Version": "0.0.1", - "User-Agent": "@fern/grpc-proto-exhaustive/0.0.1", - "X-Fern-Runtime": core.RUNTIME.type, - "X-Fern-Runtime-Version": core.RUNTIME.version, - ...(await this._getCustomAuthorizationHeaders()), - }, - contentType: "application/json", - requestType: "json", - timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, - maxRetries: requestOptions?.maxRetries, - abortSignal: requestOptions?.abortSignal, - }); - if (_response.ok) { - return serializers.dataservice.foo.Response.parseOrThrow(_response.body, { - unrecognizedObjectKeys: "passthrough", - allowUnrecognizedUnionMembers: true, - allowUnrecognizedEnumValues: true, - breadcrumbsPrefix: ["response"], - }); - } - - if (_response.error.reason === "status-code") { - throw new errors.SeedApiError({ - statusCode: _response.error.statusCode, - body: _response.error.body, - }); - } - - switch (_response.error.reason) { - case "non-json": - throw new errors.SeedApiError({ - statusCode: _response.error.statusCode, - body: _response.error.rawBody, - }); - case "timeout": - throw new errors.SeedApiTimeoutError(); - case "unknown": - throw new errors.SeedApiError({ - message: _response.error.errorMessage, - }); - } - } + constructor(protected readonly _options: Dataservice.Options) {} /** * @param {SeedApi.UploadRequest} request @@ -104,10 +43,7 @@ export class Dataservice { requestOptions?: Dataservice.RequestOptions ): Promise { const _response = await core.fetcher({ - url: urlJoin( - (await core.Supplier.get(this._options.environment)) ?? environments.SeedApiEnvironment.Default, - "data" - ), + url: urlJoin(await core.Supplier.get(this._options.environment), "data"), method: "POST", headers: { "X-Fern-Language": "JavaScript", @@ -116,7 +52,6 @@ export class Dataservice { "User-Agent": "@fern/grpc-proto-exhaustive/0.0.1", "X-Fern-Runtime": core.RUNTIME.type, "X-Fern-Runtime-Version": core.RUNTIME.version, - ...(await this._getCustomAuthorizationHeaders()), }, contentType: "application/json", requestType: "json", @@ -168,10 +103,7 @@ export class Dataservice { requestOptions?: Dataservice.RequestOptions ): Promise { const _response = await core.fetcher({ - url: urlJoin( - (await core.Supplier.get(this._options.environment)) ?? environments.SeedApiEnvironment.Default, - "data/delete" - ), + url: urlJoin(await core.Supplier.get(this._options.environment), "data/delete"), method: "POST", headers: { "X-Fern-Language": "JavaScript", @@ -180,7 +112,6 @@ export class Dataservice { "User-Agent": "@fern/grpc-proto-exhaustive/0.0.1", "X-Fern-Runtime": core.RUNTIME.type, "X-Fern-Runtime-Version": core.RUNTIME.version, - ...(await this._getCustomAuthorizationHeaders()), }, contentType: "application/json", requestType: "json", @@ -232,10 +163,7 @@ export class Dataservice { requestOptions?: Dataservice.RequestOptions ): Promise { const _response = await core.fetcher({ - url: urlJoin( - (await core.Supplier.get(this._options.environment)) ?? environments.SeedApiEnvironment.Default, - "data/describe" - ), + url: urlJoin(await core.Supplier.get(this._options.environment), "data/describe"), method: "POST", headers: { "X-Fern-Language": "JavaScript", @@ -244,7 +172,6 @@ export class Dataservice { "User-Agent": "@fern/grpc-proto-exhaustive/0.0.1", "X-Fern-Runtime": core.RUNTIME.type, "X-Fern-Runtime-Version": core.RUNTIME.version, - ...(await this._getCustomAuthorizationHeaders()), }, contentType: "application/json", requestType: "json", @@ -310,10 +237,7 @@ export class Dataservice { } const _response = await core.fetcher({ - url: urlJoin( - (await core.Supplier.get(this._options.environment)) ?? environments.SeedApiEnvironment.Default, - "data/fetch" - ), + url: urlJoin(await core.Supplier.get(this._options.environment), "data/fetch"), method: "GET", headers: { "X-Fern-Language": "JavaScript", @@ -322,7 +246,6 @@ export class Dataservice { "User-Agent": "@fern/grpc-proto-exhaustive/0.0.1", "X-Fern-Runtime": core.RUNTIME.type, "X-Fern-Runtime-Version": core.RUNTIME.version, - ...(await this._getCustomAuthorizationHeaders()), }, contentType: "application/json", queryParameters: _queryParams, @@ -392,10 +315,7 @@ export class Dataservice { } const _response = await core.fetcher({ - url: urlJoin( - (await core.Supplier.get(this._options.environment)) ?? environments.SeedApiEnvironment.Default, - "data/list" - ), + url: urlJoin(await core.Supplier.get(this._options.environment), "data/list"), method: "GET", headers: { "X-Fern-Language": "JavaScript", @@ -404,7 +324,6 @@ export class Dataservice { "User-Agent": "@fern/grpc-proto-exhaustive/0.0.1", "X-Fern-Runtime": core.RUNTIME.type, "X-Fern-Runtime-Version": core.RUNTIME.version, - ...(await this._getCustomAuthorizationHeaders()), }, contentType: "application/json", queryParameters: _queryParams, @@ -458,10 +377,7 @@ export class Dataservice { requestOptions?: Dataservice.RequestOptions ): Promise { const _response = await core.fetcher({ - url: urlJoin( - (await core.Supplier.get(this._options.environment)) ?? environments.SeedApiEnvironment.Default, - "data/query" - ), + url: urlJoin(await core.Supplier.get(this._options.environment), "data/query"), method: "POST", headers: { "X-Fern-Language": "JavaScript", @@ -470,7 +386,6 @@ export class Dataservice { "User-Agent": "@fern/grpc-proto-exhaustive/0.0.1", "X-Fern-Runtime": core.RUNTIME.type, "X-Fern-Runtime-Version": core.RUNTIME.version, - ...(await this._getCustomAuthorizationHeaders()), }, contentType: "application/json", requestType: "json", @@ -524,10 +439,7 @@ export class Dataservice { requestOptions?: Dataservice.RequestOptions ): Promise { const _response = await core.fetcher({ - url: urlJoin( - (await core.Supplier.get(this._options.environment)) ?? environments.SeedApiEnvironment.Default, - "data/update" - ), + url: urlJoin(await core.Supplier.get(this._options.environment), "data/update"), method: "POST", headers: { "X-Fern-Language": "JavaScript", @@ -536,7 +448,6 @@ export class Dataservice { "User-Agent": "@fern/grpc-proto-exhaustive/0.0.1", "X-Fern-Runtime": core.RUNTIME.type, "X-Fern-Runtime-Version": core.RUNTIME.version, - ...(await this._getCustomAuthorizationHeaders()), }, contentType: "application/json", requestType: "json", @@ -575,9 +486,4 @@ export class Dataservice { }); } } - - protected async _getCustomAuthorizationHeaders() { - const apiKeyValue = await core.Supplier.get(this._options.apiKey); - return { "X-API-Key": apiKeyValue }; - } } diff --git a/seed/ts-sdk/grpc-proto-exhaustive/src/environments.ts b/seed/ts-sdk/grpc-proto-exhaustive/src/environments.ts deleted file mode 100644 index 0cec2791589..00000000000 --- a/seed/ts-sdk/grpc-proto-exhaustive/src/environments.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ - -export const SeedApiEnvironment = { - Default: "https://localhost", -} as const; - -export type SeedApiEnvironment = typeof SeedApiEnvironment.Default; diff --git a/seed/ts-sdk/grpc-proto-exhaustive/src/index.ts b/seed/ts-sdk/grpc-proto-exhaustive/src/index.ts index 9157dadc454..bed9941cdaa 100644 --- a/seed/ts-sdk/grpc-proto-exhaustive/src/index.ts +++ b/seed/ts-sdk/grpc-proto-exhaustive/src/index.ts @@ -1,4 +1,3 @@ export * as SeedApi from "./api"; export { SeedApiClient } from "./Client"; -export { SeedApiEnvironment } from "./environments"; export { SeedApiError, SeedApiTimeoutError } from "./errors"; diff --git a/seed/ts-sdk/grpc-proto-exhaustive/src/serialization/resources/dataservice/client/foo.ts b/seed/ts-sdk/grpc-proto-exhaustive/src/serialization/resources/dataservice/client/foo.ts deleted file mode 100644 index 3bdfc81c307..00000000000 --- a/seed/ts-sdk/grpc-proto-exhaustive/src/serialization/resources/dataservice/client/foo.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ - -import * as serializers from "../../../index"; -import * as core from "../../../../core"; - -export const Response: core.serialization.Schema< - serializers.dataservice.foo.Response.Raw, - Record -> = core.serialization.record(core.serialization.string(), core.serialization.unknown()); - -export declare namespace Response { - type Raw = Record; -} diff --git a/seed/ts-sdk/grpc-proto-exhaustive/src/serialization/resources/dataservice/client/index.ts b/seed/ts-sdk/grpc-proto-exhaustive/src/serialization/resources/dataservice/client/index.ts index 734c128303e..415726b7fea 100644 --- a/seed/ts-sdk/grpc-proto-exhaustive/src/serialization/resources/dataservice/client/index.ts +++ b/seed/ts-sdk/grpc-proto-exhaustive/src/serialization/resources/dataservice/client/index.ts @@ -1,2 +1 @@ -export * as foo from "./foo"; export * from "./requests"; diff --git a/seed/ts-sdk/license/.github/workflows/ci.yml b/seed/ts-sdk/license/.github/workflows/ci.yml new file mode 100644 index 00000000000..b64a6cbbb4a --- /dev/null +++ b/seed/ts-sdk/license/.github/workflows/ci.yml @@ -0,0 +1,57 @@ +name: ci + +on: [push] + +jobs: + compile: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Set up node + uses: actions/setup-node@v3 + + - name: Compile + run: yarn && yarn build + + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Set up node + uses: actions/setup-node@v3 + + - name: Compile + run: yarn && yarn 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@v3 + - name: Set up node + uses: actions/setup-node@v3 + - name: Install dependencies + run: yarn install + - name: Build + run: yarn build + + - name: Publish to npm + run: | + npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN} + if [[ ${GITHUB_REF} == *alpha* ]]; then + npm publish --access public --tag alpha + elif [[ ${GITHUB_REF} == *beta* ]]; then + npm publish --access public --tag beta + else + npm publish --access public + fi + env: + NPM_TOKEN: ${{ secrets. }} \ No newline at end of file diff --git a/seed/ts-sdk/license/.gitignore b/seed/ts-sdk/license/.gitignore new file mode 100644 index 00000000000..72271e049c0 --- /dev/null +++ b/seed/ts-sdk/license/.gitignore @@ -0,0 +1,3 @@ +node_modules +.DS_Store +/dist \ No newline at end of file diff --git a/seed/ts-sdk/license/.mock/definition/__package__.yml b/seed/ts-sdk/license/.mock/definition/__package__.yml new file mode 100644 index 00000000000..b1e4d4a878f --- /dev/null +++ b/seed/ts-sdk/license/.mock/definition/__package__.yml @@ -0,0 +1,13 @@ +types: + Type: + docs: A simple type with just a name. + properties: + name: string + +service: + auth: false + base-path: / + endpoints: + get: + path: "/" + method: GET diff --git a/seed/ts-sdk/license/.mock/definition/api.yml b/seed/ts-sdk/license/.mock/definition/api.yml new file mode 100644 index 00000000000..5523ff1f181 --- /dev/null +++ b/seed/ts-sdk/license/.mock/definition/api.yml @@ -0,0 +1 @@ +name: license diff --git a/seed/ts-sdk/license/.mock/fern.config.json b/seed/ts-sdk/license/.mock/fern.config.json new file mode 100644 index 00000000000..4c8e54ac313 --- /dev/null +++ b/seed/ts-sdk/license/.mock/fern.config.json @@ -0,0 +1 @@ +{"organization": "fern-test", "version": "*"} \ No newline at end of file diff --git a/seed/ts-sdk/license/.mock/generators.yml b/seed/ts-sdk/license/.mock/generators.yml new file mode 100644 index 00000000000..0967ef424bc --- /dev/null +++ b/seed/ts-sdk/license/.mock/generators.yml @@ -0,0 +1 @@ +{} diff --git a/seed/ts-sdk/license/.npmignore b/seed/ts-sdk/license/.npmignore new file mode 100644 index 00000000000..6db0876c41c --- /dev/null +++ b/seed/ts-sdk/license/.npmignore @@ -0,0 +1,9 @@ +node_modules +src +tests +.gitignore +.github +.fernignore +.prettierrc.yml +tsconfig.json +yarn.lock \ No newline at end of file diff --git a/seed/ts-sdk/license/.prettierrc.yml b/seed/ts-sdk/license/.prettierrc.yml new file mode 100644 index 00000000000..0c06786bf53 --- /dev/null +++ b/seed/ts-sdk/license/.prettierrc.yml @@ -0,0 +1,2 @@ +tabWidth: 4 +printWidth: 120 diff --git a/seed/ts-sdk/license/README.md b/seed/ts-sdk/license/README.md new file mode 100644 index 00000000000..d41dc63ca13 --- /dev/null +++ b/seed/ts-sdk/license/README.md @@ -0,0 +1,126 @@ +# Seed TypeScript 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%2FTypeScript) +[![npm shield](https://img.shields.io/npm/v/@fern/license)](https://www.npmjs.com/package/@fern/license) + +The Seed TypeScript library provides convenient access to the Seed API from TypeScript. + +## Installation + +```sh +npm i -s @fern/license +``` + +## Reference + +A full reference for this library is available [here](./reference.md). + +## Usage + +Instantiate and use the client with the following: + +```typescript +import { SeedLicenseClient } from "@fern/license"; + +const client = new SeedLicenseClient({ environment: "YOUR_BASE_URL" }); +await client.get(); +``` + +## Exception Handling + +When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error +will be thrown. + +```typescript +import { SeedLicenseError } from "@fern/license"; + +try { + await client.get(...); +} catch (err) { + if (err instanceof SeedLicenseError) { + console.log(err.statusCode); + console.log(err.message); + console.log(err.body); + } +} +``` + +## Advanced + +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retriable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). + +A request is deemed retriable 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. + +```typescript +const response = await client.get(..., { + maxRetries: 0 // override maxRetries at the request level +}); +``` + +### Timeouts + +The SDK defaults to a 60 second timeout. Use the `timeoutInSeconds` option to configure this behavior. + +```typescript +const response = await client.get(..., { + timeoutInSeconds: 30 // override timeout to 30s +}); +``` + +### Aborting Requests + +The SDK allows users to abort requests at any point by passing in an abort signal. + +```typescript +const controller = new AbortController(); +const response = await client.get(..., { + abortSignal: controller.signal +}); +controller.abort(); // aborts the request +``` + +### Runtime Compatibility + +The SDK defaults to `node-fetch` but will use the global fetch client if present. The SDK works in the following +runtimes: + +- Node.js 18+ +- Vercel +- Cloudflare Workers +- Deno v1.25+ +- Bun 1.0+ +- React Native + +### Customizing Fetch Client + +The SDK provides a way for your to customize the underlying HTTP client / Fetch function. If you're running in an +unsupported environment, this provides a way for you to break glass and ensure the SDK works. + +```typescript +import { SeedLicenseClient } from "@fern/license"; + +const client = new SeedLicenseClient({ + ... + fetcher: // provide your implementation here +}); +``` + +## 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/ts-sdk/license/jest.config.js b/seed/ts-sdk/license/jest.config.js new file mode 100644 index 00000000000..35d6e65bf93 --- /dev/null +++ b/seed/ts-sdk/license/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('jest').Config} */ +module.exports = { + preset: "ts-jest", + testEnvironment: "node", +}; diff --git a/seed/ts-sdk/license/package.json b/seed/ts-sdk/license/package.json new file mode 100644 index 00000000000..e50916e2e68 --- /dev/null +++ b/seed/ts-sdk/license/package.json @@ -0,0 +1,43 @@ +{ + "name": "@fern/license", + "version": "0.0.1", + "private": false, + "repository": "https://github.com/license/fern", + "main": "./index.js", + "types": "./index.d.ts", + "scripts": { + "format": "prettier . --write --ignore-unknown", + "build": "tsc", + "prepack": "cp -rv dist/. .", + "test": "jest" + }, + "dependencies": { + "url-join": "4.0.1", + "form-data": "^4.0.0", + "formdata-node": "^6.0.3", + "node-fetch": "2.7.0", + "qs": "6.11.2", + "readable-stream": "^4.5.2" + }, + "devDependencies": { + "@types/url-join": "4.0.1", + "@types/qs": "6.9.8", + "@types/node-fetch": "2.6.9", + "@types/readable-stream": "^4.0.15", + "fetch-mock-jest": "^1.5.1", + "webpack": "^5.94.0", + "ts-loader": "^9.3.1", + "jest": "29.7.0", + "@types/jest": "29.5.5", + "ts-jest": "29.1.1", + "jest-environment-jsdom": "29.7.0", + "@types/node": "17.0.33", + "prettier": "2.7.1", + "typescript": "4.6.4" + }, + "browser": { + "fs": false, + "os": false, + "path": false + } +} diff --git a/seed/ts-sdk/license/reference.md b/seed/ts-sdk/license/reference.md new file mode 100644 index 00000000000..3d32945f15a --- /dev/null +++ b/seed/ts-sdk/license/reference.md @@ -0,0 +1,43 @@ +# Reference + +
client.get() -> void +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.get(); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**requestOptions:** `SeedLicenseClient.RequestOptions` + +
+
+
+
+ +
+
+
+ +## diff --git a/seed/ts-sdk/license/resolved-snippet-templates.md b/seed/ts-sdk/license/resolved-snippet-templates.md new file mode 100644 index 00000000000..30bd988c3c2 --- /dev/null +++ b/seed/ts-sdk/license/resolved-snippet-templates.md @@ -0,0 +1,9 @@ +```typescript +import { SeedLicenseClient } from "@fern/license"; + +const client = new SeedLicenseClient({ environment: "YOUR_BASE_URL" }); +await client.get(); + +``` + + diff --git a/seed/ts-sdk/license/snippet-templates.json b/seed/ts-sdk/license/snippet-templates.json new file mode 100644 index 00000000000..c7230fa1052 --- /dev/null +++ b/seed/ts-sdk/license/snippet-templates.json @@ -0,0 +1,57 @@ +[ + { + "sdk": { + "package": "@fern/license", + "version": "0.0.1", + "type": "typescript" + }, + "endpointId": { + "path": "/", + "method": "GET", + "identifierOverride": "endpoint_.get" + }, + "snippetTemplate": { + "clientInstantiation": { + "imports": [ + "import { SeedLicenseClient } from \"@fern/license\";" + ], + "templateString": "const client = new SeedLicenseClient($FERN_INPUT);", + "isOptional": false, + "inputDelimiter": ",", + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "{ $FERN_INPUT }", + "isOptional": true, + "templateInputs": [ + { + "value": { + "imports": [], + "templateString": "environment: \"YOUR_BASE_URL\"", + "isOptional": false, + "templateInputs": [], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "type": "template" + } + ], + "type": "generic" + }, + "functionInvocation": { + "imports": [], + "templateString": "await client.get()", + "isOptional": false, + "inputDelimiter": ",\n\t", + "templateInputs": [], + "type": "generic" + }, + "type": "v1" + } + } +] \ No newline at end of file diff --git a/seed/ts-sdk/license/snippet.json b/seed/ts-sdk/license/snippet.json new file mode 100644 index 00000000000..c4cd7776115 --- /dev/null +++ b/seed/ts-sdk/license/snippet.json @@ -0,0 +1,16 @@ +{ + "endpoints": [ + { + "id": { + "path": "/", + "method": "GET", + "identifier_override": "endpoint_.get" + }, + "snippet": { + "type": "typescript", + "client": "import { SeedLicenseClient } from \"@fern/license\";\n\nconst client = new SeedLicenseClient({ environment: \"YOUR_BASE_URL\" });\nawait client.get();\n" + } + } + ], + "types": {} +} \ No newline at end of file diff --git a/seed/ts-sdk/license/src/Client.ts b/seed/ts-sdk/license/src/Client.ts new file mode 100644 index 00000000000..00480dd802e --- /dev/null +++ b/seed/ts-sdk/license/src/Client.ts @@ -0,0 +1,76 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as core from "./core"; +import urlJoin from "url-join"; +import * as errors from "./errors/index"; + +export declare namespace SeedLicenseClient { + interface Options { + environment: core.Supplier; + } + + interface RequestOptions { + /** The maximum time to wait for a response in seconds. */ + timeoutInSeconds?: number; + /** The number of times to retry the request. Defaults to 2. */ + maxRetries?: number; + /** A hook to abort the request. */ + abortSignal?: AbortSignal; + } +} + +export class SeedLicenseClient { + constructor(protected readonly _options: SeedLicenseClient.Options) {} + + /** + * @param {SeedLicenseClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * await client.get() + */ + public async get(requestOptions?: SeedLicenseClient.RequestOptions): Promise { + const _response = await core.fetcher({ + url: urlJoin(await core.Supplier.get(this._options.environment), "/"), + method: "GET", + headers: { + "X-Fern-Language": "JavaScript", + "X-Fern-SDK-Name": "@fern/license", + "X-Fern-SDK-Version": "0.0.1", + "User-Agent": "@fern/license/0.0.1", + "X-Fern-Runtime": core.RUNTIME.type, + "X-Fern-Runtime-Version": core.RUNTIME.version, + }, + contentType: "application/json", + requestType: "json", + timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, + maxRetries: requestOptions?.maxRetries, + abortSignal: requestOptions?.abortSignal, + }); + if (_response.ok) { + return; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedLicenseError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + }); + } + + switch (_response.error.reason) { + case "non-json": + throw new errors.SeedLicenseError({ + statusCode: _response.error.statusCode, + body: _response.error.rawBody, + }); + case "timeout": + throw new errors.SeedLicenseTimeoutError(); + case "unknown": + throw new errors.SeedLicenseError({ + message: _response.error.errorMessage, + }); + } + } +} diff --git a/seed/ts-sdk/license/src/api/index.ts b/seed/ts-sdk/license/src/api/index.ts new file mode 100644 index 00000000000..eea524d6557 --- /dev/null +++ b/seed/ts-sdk/license/src/api/index.ts @@ -0,0 +1 @@ +export * from "./types"; diff --git a/seed/ts-sdk/license/src/api/types/Type.ts b/seed/ts-sdk/license/src/api/types/Type.ts new file mode 100644 index 00000000000..cf5fb80f4d8 --- /dev/null +++ b/seed/ts-sdk/license/src/api/types/Type.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +/** + * A simple type with just a name. + */ +export interface Type { + name: string; +} diff --git a/seed/ts-sdk/license/src/api/types/index.ts b/seed/ts-sdk/license/src/api/types/index.ts new file mode 100644 index 00000000000..3edf389a5d5 --- /dev/null +++ b/seed/ts-sdk/license/src/api/types/index.ts @@ -0,0 +1 @@ +export * from "./Type"; diff --git a/seed/ts-sdk/license/src/core/fetcher/APIResponse.ts b/seed/ts-sdk/license/src/core/fetcher/APIResponse.ts new file mode 100644 index 00000000000..3664d09e168 --- /dev/null +++ b/seed/ts-sdk/license/src/core/fetcher/APIResponse.ts @@ -0,0 +1,12 @@ +export type APIResponse = SuccessfulResponse | FailedResponse; + +export interface SuccessfulResponse { + ok: true; + body: T; + headers?: Record; +} + +export interface FailedResponse { + ok: false; + error: T; +} diff --git a/seed/ts-sdk/license/src/core/fetcher/Fetcher.ts b/seed/ts-sdk/license/src/core/fetcher/Fetcher.ts new file mode 100644 index 00000000000..b8f23717b69 --- /dev/null +++ b/seed/ts-sdk/license/src/core/fetcher/Fetcher.ts @@ -0,0 +1,143 @@ +import { APIResponse } from "./APIResponse"; +import { createRequestUrl } from "./createRequestUrl"; +import { getFetchFn } from "./getFetchFn"; +import { getRequestBody } from "./getRequestBody"; +import { getResponseBody } from "./getResponseBody"; +import { makeRequest } from "./makeRequest"; +import { requestWithRetries } from "./requestWithRetries"; + +export type FetchFunction = (args: Fetcher.Args) => Promise>; + +export declare namespace Fetcher { + export interface Args { + url: string; + method: string; + contentType?: string; + headers?: Record; + queryParameters?: Record; + body?: unknown; + timeoutMs?: number; + maxRetries?: number; + withCredentials?: boolean; + abortSignal?: AbortSignal; + requestType?: "json" | "file" | "bytes"; + responseType?: "json" | "blob" | "sse" | "streaming" | "text" | "arrayBuffer"; + duplex?: "half"; + } + + export type Error = FailedStatusCodeError | NonJsonError | TimeoutError | UnknownError; + + export interface FailedStatusCodeError { + reason: "status-code"; + statusCode: number; + body: unknown; + } + + export interface NonJsonError { + reason: "non-json"; + statusCode: number; + rawBody: string; + } + + export interface TimeoutError { + reason: "timeout"; + } + + export interface UnknownError { + reason: "unknown"; + errorMessage: string; + } +} + +export async function fetcherImpl(args: Fetcher.Args): Promise> { + const headers: Record = {}; + if (args.body !== undefined && args.contentType != null) { + headers["Content-Type"] = args.contentType; + } + + if (args.headers != null) { + for (const [key, value] of Object.entries(args.headers)) { + if (value != null) { + headers[key] = value; + } + } + } + + const url = createRequestUrl(args.url, args.queryParameters); + let requestBody: BodyInit | undefined = await getRequestBody({ + body: args.body, + type: args.requestType === "json" ? "json" : "other", + }); + const fetchFn = await getFetchFn(); + + try { + const response = await requestWithRetries( + async () => + makeRequest( + fetchFn, + url, + args.method, + headers, + requestBody, + args.timeoutMs, + args.abortSignal, + args.withCredentials, + args.duplex + ), + args.maxRetries + ); + let responseBody = await getResponseBody(response, args.responseType); + + if (response.status >= 200 && response.status < 400) { + return { + ok: true, + body: responseBody as R, + headers: response.headers, + }; + } else { + return { + ok: false, + error: { + reason: "status-code", + statusCode: response.status, + body: responseBody, + }, + }; + } + } catch (error) { + if (args.abortSignal != null && args.abortSignal.aborted) { + return { + ok: false, + error: { + reason: "unknown", + errorMessage: "The user aborted a request", + }, + }; + } else if (error instanceof Error && error.name === "AbortError") { + return { + ok: false, + error: { + reason: "timeout", + }, + }; + } else if (error instanceof Error) { + return { + ok: false, + error: { + reason: "unknown", + errorMessage: error.message, + }, + }; + } + + return { + ok: false, + error: { + reason: "unknown", + errorMessage: JSON.stringify(error), + }, + }; + } +} + +export const fetcher: FetchFunction = fetcherImpl; diff --git a/seed/ts-sdk/license/src/core/fetcher/Supplier.ts b/seed/ts-sdk/license/src/core/fetcher/Supplier.ts new file mode 100644 index 00000000000..867c931c02f --- /dev/null +++ b/seed/ts-sdk/license/src/core/fetcher/Supplier.ts @@ -0,0 +1,11 @@ +export type Supplier = T | Promise | (() => T | Promise); + +export const Supplier = { + get: async (supplier: Supplier): Promise => { + if (typeof supplier === "function") { + return (supplier as () => T)(); + } else { + return supplier; + } + }, +}; diff --git a/seed/ts-sdk/license/src/core/fetcher/createRequestUrl.ts b/seed/ts-sdk/license/src/core/fetcher/createRequestUrl.ts new file mode 100644 index 00000000000..9288a99bb22 --- /dev/null +++ b/seed/ts-sdk/license/src/core/fetcher/createRequestUrl.ts @@ -0,0 +1,10 @@ +import qs from "qs"; + +export function createRequestUrl( + baseUrl: string, + queryParameters?: Record +): string { + return Object.keys(queryParameters ?? {}).length > 0 + ? `${baseUrl}?${qs.stringify(queryParameters, { arrayFormat: "repeat" })}` + : baseUrl; +} diff --git a/seed/ts-sdk/license/src/core/fetcher/getFetchFn.ts b/seed/ts-sdk/license/src/core/fetcher/getFetchFn.ts new file mode 100644 index 00000000000..9fd9bfc42bd --- /dev/null +++ b/seed/ts-sdk/license/src/core/fetcher/getFetchFn.ts @@ -0,0 +1,25 @@ +import { RUNTIME } from "../runtime"; + +/** + * Returns a fetch function based on the runtime + */ +export async function getFetchFn(): Promise { + // In Node.js 18+ environments, use native fetch + if (RUNTIME.type === "node" && RUNTIME.parsedVersion != null && RUNTIME.parsedVersion >= 18) { + return fetch; + } + + // In Node.js 18 or lower environments, the SDK always uses`node-fetch`. + if (RUNTIME.type === "node") { + return (await import("node-fetch")).default as any; + } + + // Otherwise the SDK uses global fetch if available, + // and falls back to node-fetch. + if (typeof fetch == "function") { + return fetch; + } + + // Defaults to node `node-fetch` if global fetch isn't available + return (await import("node-fetch")).default as any; +} diff --git a/seed/ts-sdk/license/src/core/fetcher/getHeader.ts b/seed/ts-sdk/license/src/core/fetcher/getHeader.ts new file mode 100644 index 00000000000..50f922b0e87 --- /dev/null +++ b/seed/ts-sdk/license/src/core/fetcher/getHeader.ts @@ -0,0 +1,8 @@ +export function getHeader(headers: Record, header: string): string | undefined { + for (const [headerKey, headerValue] of Object.entries(headers)) { + if (headerKey.toLowerCase() === header.toLowerCase()) { + return headerValue; + } + } + return undefined; +} diff --git a/seed/ts-sdk/license/src/core/fetcher/getRequestBody.ts b/seed/ts-sdk/license/src/core/fetcher/getRequestBody.ts new file mode 100644 index 00000000000..1138414b1c2 --- /dev/null +++ b/seed/ts-sdk/license/src/core/fetcher/getRequestBody.ts @@ -0,0 +1,14 @@ +export declare namespace GetRequestBody { + interface Args { + body: unknown; + type: "json" | "file" | "bytes" | "other"; + } +} + +export async function getRequestBody({ body, type }: GetRequestBody.Args): Promise { + if (type.includes("json")) { + return JSON.stringify(body); + } else { + return body as BodyInit; + } +} diff --git a/seed/ts-sdk/license/src/core/fetcher/getResponseBody.ts b/seed/ts-sdk/license/src/core/fetcher/getResponseBody.ts new file mode 100644 index 00000000000..d046e6ea275 --- /dev/null +++ b/seed/ts-sdk/license/src/core/fetcher/getResponseBody.ts @@ -0,0 +1,34 @@ +import { chooseStreamWrapper } from "./stream-wrappers/chooseStreamWrapper"; + +export async function getResponseBody(response: Response, responseType?: string): Promise { + if (response.body != null && responseType === "blob") { + return await response.blob(); + } else if (response.body != null && responseType === "arrayBuffer") { + return await response.arrayBuffer(); + } else if (response.body != null && responseType === "sse") { + return response.body; + } else if (response.body != null && responseType === "streaming") { + return chooseStreamWrapper(response.body); + } else if (response.body != null && responseType === "text") { + return await response.text(); + } else { + const text = await response.text(); + if (text.length > 0) { + try { + let responseBody = JSON.parse(text); + return responseBody; + } catch (err) { + return { + ok: false, + error: { + reason: "non-json", + statusCode: response.status, + rawBody: text, + }, + }; + } + } else { + return undefined; + } + } +} diff --git a/seed/ts-sdk/license/src/core/fetcher/index.ts b/seed/ts-sdk/license/src/core/fetcher/index.ts new file mode 100644 index 00000000000..2d658ca48f9 --- /dev/null +++ b/seed/ts-sdk/license/src/core/fetcher/index.ts @@ -0,0 +1,5 @@ +export type { APIResponse } from "./APIResponse"; +export { fetcher } from "./Fetcher"; +export type { Fetcher, FetchFunction } from "./Fetcher"; +export { getHeader } from "./getHeader"; +export { Supplier } from "./Supplier"; diff --git a/seed/ts-sdk/license/src/core/fetcher/makeRequest.ts b/seed/ts-sdk/license/src/core/fetcher/makeRequest.ts new file mode 100644 index 00000000000..8fb4bace466 --- /dev/null +++ b/seed/ts-sdk/license/src/core/fetcher/makeRequest.ts @@ -0,0 +1,44 @@ +import { anySignal, getTimeoutSignal } from "./signals"; + +export const makeRequest = async ( + fetchFn: (url: string, init: RequestInit) => Promise, + url: string, + method: string, + headers: Record, + requestBody: BodyInit | undefined, + timeoutMs?: number, + abortSignal?: AbortSignal, + withCredentials?: boolean, + duplex?: "half" +): Promise => { + const signals: AbortSignal[] = []; + + // Add timeout signal + let timeoutAbortId: NodeJS.Timeout | undefined = undefined; + if (timeoutMs != null) { + const { signal, abortId } = getTimeoutSignal(timeoutMs); + timeoutAbortId = abortId; + signals.push(signal); + } + + // Add arbitrary signal + if (abortSignal != null) { + signals.push(abortSignal); + } + let newSignals = anySignal(signals); + const response = await fetchFn(url, { + method: method, + headers, + body: requestBody, + signal: newSignals, + credentials: withCredentials ? "include" : undefined, + // @ts-ignore + duplex, + }); + + if (timeoutAbortId != null) { + clearTimeout(timeoutAbortId); + } + + return response; +}; diff --git a/seed/ts-sdk/license/src/core/fetcher/requestWithRetries.ts b/seed/ts-sdk/license/src/core/fetcher/requestWithRetries.ts new file mode 100644 index 00000000000..8d5af9d5a82 --- /dev/null +++ b/seed/ts-sdk/license/src/core/fetcher/requestWithRetries.ts @@ -0,0 +1,33 @@ +const INITIAL_RETRY_DELAY = 1000; // in milliseconds +const MAX_RETRY_DELAY = 60000; // in milliseconds +const DEFAULT_MAX_RETRIES = 2; +const JITTER_FACTOR = 0.2; // 20% random jitter + +function addJitter(delay: number): number { + // Generate a random value between -JITTER_FACTOR and +JITTER_FACTOR + const jitterMultiplier = 1 + (Math.random() * 2 - 1) * JITTER_FACTOR; + return delay * jitterMultiplier; +} + +export async function requestWithRetries( + requestFn: () => Promise, + maxRetries: number = DEFAULT_MAX_RETRIES +): Promise { + let response: Response = await requestFn(); + + for (let i = 0; i < maxRetries; ++i) { + if ([408, 409, 429].includes(response.status) || response.status >= 500) { + // Calculate base delay using exponential backoff (in milliseconds) + const baseDelay = Math.min(INITIAL_RETRY_DELAY * Math.pow(2, i), MAX_RETRY_DELAY); + + // Add jitter to the delay + const delayWithJitter = addJitter(baseDelay); + + await new Promise((resolve) => setTimeout(resolve, delayWithJitter)); + response = await requestFn(); + } else { + break; + } + } + return response!; +} diff --git a/seed/ts-sdk/license/src/core/fetcher/signals.ts b/seed/ts-sdk/license/src/core/fetcher/signals.ts new file mode 100644 index 00000000000..6c124ff7985 --- /dev/null +++ b/seed/ts-sdk/license/src/core/fetcher/signals.ts @@ -0,0 +1,38 @@ +const TIMEOUT = "timeout"; + +export function getTimeoutSignal(timeoutMs: number): { signal: AbortSignal; abortId: NodeJS.Timeout } { + const controller = new AbortController(); + const abortId = setTimeout(() => controller.abort(TIMEOUT), timeoutMs); + return { signal: controller.signal, abortId }; +} + +/** + * Returns an abort signal that is getting aborted when + * at least one of the specified abort signals is aborted. + * + * Requires at least node.js 18. + */ +export function anySignal(...args: AbortSignal[] | [AbortSignal[]]): AbortSignal { + // Allowing signals to be passed either as array + // of signals or as multiple arguments. + const signals = (args.length === 1 && Array.isArray(args[0]) ? args[0] : args); + + const controller = new AbortController(); + + for (const signal of signals) { + if (signal.aborted) { + // Exiting early if one of the signals + // is already aborted. + controller.abort((signal as any)?.reason); + break; + } + + // Listening for signals and removing the listeners + // when at least one symbol is aborted. + signal.addEventListener("abort", () => controller.abort((signal as any)?.reason), { + signal: controller.signal, + }); + } + + return controller.signal; +} diff --git a/seed/ts-sdk/license/src/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts b/seed/ts-sdk/license/src/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts new file mode 100644 index 00000000000..4d7b7d52e8f --- /dev/null +++ b/seed/ts-sdk/license/src/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper.ts @@ -0,0 +1,256 @@ +import type { Writable } from "readable-stream"; +import { EventCallback, StreamWrapper } from "./chooseStreamWrapper"; + +export class Node18UniversalStreamWrapper + implements + StreamWrapper | Writable | WritableStream, ReadFormat> +{ + private readableStream: ReadableStream; + private reader: ReadableStreamDefaultReader; + private events: Record; + private paused: boolean; + private resumeCallback: ((value?: unknown) => void) | null; + private encoding: string | null; + + constructor(readableStream: ReadableStream) { + this.readableStream = readableStream; + this.reader = this.readableStream.getReader(); + this.events = { + data: [], + end: [], + error: [], + readable: [], + close: [], + pause: [], + resume: [], + }; + this.paused = false; + this.resumeCallback = null; + this.encoding = null; + } + + public on(event: string, callback: EventCallback): void { + this.events[event]?.push(callback); + } + + public off(event: string, callback: EventCallback): void { + this.events[event] = this.events[event]?.filter((cb) => cb !== callback); + } + + public pipe( + dest: Node18UniversalStreamWrapper | Writable | WritableStream + ): Node18UniversalStreamWrapper | Writable | WritableStream { + this.on("data", async (chunk) => { + if (dest instanceof Node18UniversalStreamWrapper) { + dest._write(chunk); + } else if (dest instanceof WritableStream) { + const writer = dest.getWriter(); + writer.write(chunk).then(() => writer.releaseLock()); + } else { + dest.write(chunk); + } + }); + + this.on("end", async () => { + if (dest instanceof Node18UniversalStreamWrapper) { + dest._end(); + } else if (dest instanceof WritableStream) { + const writer = dest.getWriter(); + writer.close(); + } else { + dest.end(); + } + }); + + this.on("error", async (error) => { + if (dest instanceof Node18UniversalStreamWrapper) { + dest._error(error); + } else if (dest instanceof WritableStream) { + const writer = dest.getWriter(); + writer.abort(error); + } else { + dest.destroy(error); + } + }); + + this._startReading(); + + return dest; + } + + public pipeTo( + dest: Node18UniversalStreamWrapper | Writable | WritableStream + ): Node18UniversalStreamWrapper | Writable | WritableStream { + return this.pipe(dest); + } + + public unpipe(dest: Node18UniversalStreamWrapper | Writable | WritableStream): void { + this.off("data", async (chunk) => { + if (dest instanceof Node18UniversalStreamWrapper) { + dest._write(chunk); + } else if (dest instanceof WritableStream) { + const writer = dest.getWriter(); + writer.write(chunk).then(() => writer.releaseLock()); + } else { + dest.write(chunk); + } + }); + + this.off("end", async () => { + if (dest instanceof Node18UniversalStreamWrapper) { + dest._end(); + } else if (dest instanceof WritableStream) { + const writer = dest.getWriter(); + writer.close(); + } else { + dest.end(); + } + }); + + this.off("error", async (error) => { + if (dest instanceof Node18UniversalStreamWrapper) { + dest._error(error); + } else if (dest instanceof WritableStream) { + const writer = dest.getWriter(); + writer.abort(error); + } else { + dest.destroy(error); + } + }); + } + + public destroy(error?: Error): void { + this.reader + .cancel(error) + .then(() => { + this._emit("close"); + }) + .catch((err) => { + this._emit("error", err); + }); + } + + public pause(): void { + this.paused = true; + this._emit("pause"); + } + + public resume(): void { + if (this.paused) { + this.paused = false; + this._emit("resume"); + if (this.resumeCallback) { + this.resumeCallback(); + this.resumeCallback = null; + } + } + } + + public get isPaused(): boolean { + return this.paused; + } + + public async read(): Promise { + if (this.paused) { + await new Promise((resolve) => { + this.resumeCallback = resolve; + }); + } + const { done, value } = await this.reader.read(); + + if (done) { + return undefined; + } + return value; + } + + public setEncoding(encoding: string): void { + this.encoding = encoding; + } + + public async text(): Promise { + const chunks: ReadFormat[] = []; + + while (true) { + const { done, value } = await this.reader.read(); + if (done) { + break; + } + if (value) { + chunks.push(value); + } + } + + const decoder = new TextDecoder(this.encoding || "utf-8"); + return decoder.decode(await new Blob(chunks).arrayBuffer()); + } + + public async json(): Promise { + const text = await this.text(); + return JSON.parse(text); + } + + private _write(chunk: ReadFormat): void { + this._emit("data", chunk); + } + + private _end(): void { + this._emit("end"); + } + + private _error(error: any): void { + this._emit("error", error); + } + + private _emit(event: string, data?: any): void { + if (this.events[event]) { + for (const callback of this.events[event] || []) { + callback(data); + } + } + } + + private async _startReading(): Promise { + try { + this._emit("readable"); + while (true) { + if (this.paused) { + await new Promise((resolve) => { + this.resumeCallback = resolve; + }); + } + const { done, value } = await this.reader.read(); + if (done) { + this._emit("end"); + this._emit("close"); + break; + } + if (value) { + this._emit("data", value); + } + } + } catch (error) { + this._emit("error", error); + } + } + + [Symbol.asyncIterator](): AsyncIterableIterator { + return { + next: async () => { + if (this.paused) { + await new Promise((resolve) => { + this.resumeCallback = resolve; + }); + } + const { done, value } = await this.reader.read(); + if (done) { + return { done: true, value: undefined }; + } + return { done: false, value }; + }, + [Symbol.asyncIterator]() { + return this; + }, + }; + } +} diff --git a/seed/ts-sdk/license/src/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts b/seed/ts-sdk/license/src/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts new file mode 100644 index 00000000000..ba5f7276750 --- /dev/null +++ b/seed/ts-sdk/license/src/core/fetcher/stream-wrappers/NodePre18StreamWrapper.ts @@ -0,0 +1,106 @@ +import type { Readable, Writable } from "readable-stream"; +import { EventCallback, StreamWrapper } from "./chooseStreamWrapper"; + +export class NodePre18StreamWrapper implements StreamWrapper { + private readableStream: Readable; + private encoding: string | undefined; + + constructor(readableStream: Readable) { + this.readableStream = readableStream; + } + + public on(event: string, callback: EventCallback): void { + this.readableStream.on(event, callback); + } + + public off(event: string, callback: EventCallback): void { + this.readableStream.off(event, callback); + } + + public pipe(dest: Writable): Writable { + this.readableStream.pipe(dest); + return dest; + } + + public pipeTo(dest: Writable): Writable { + return this.pipe(dest); + } + + public unpipe(dest?: Writable): void { + if (dest) { + this.readableStream.unpipe(dest); + } else { + this.readableStream.unpipe(); + } + } + + public destroy(error?: Error): void { + this.readableStream.destroy(error); + } + + public pause(): void { + this.readableStream.pause(); + } + + public resume(): void { + this.readableStream.resume(); + } + + public get isPaused(): boolean { + return this.readableStream.isPaused(); + } + + public async read(): Promise { + return new Promise((resolve, reject) => { + const chunk = this.readableStream.read(); + if (chunk) { + resolve(chunk); + } else { + this.readableStream.once("readable", () => { + const chunk = this.readableStream.read(); + resolve(chunk); + }); + this.readableStream.once("error", reject); + } + }); + } + + public setEncoding(encoding?: string): void { + this.readableStream.setEncoding(encoding as BufferEncoding); + this.encoding = encoding; + } + + public async text(): Promise { + const chunks: Uint8Array[] = []; + const encoder = new TextEncoder(); + this.readableStream.setEncoding((this.encoding || "utf-8") as BufferEncoding); + + for await (const chunk of this.readableStream) { + chunks.push(encoder.encode(chunk)); + } + + const decoder = new TextDecoder(this.encoding || "utf-8"); + return decoder.decode(Buffer.concat(chunks)); + } + + public async json(): Promise { + const text = await this.text(); + return JSON.parse(text); + } + + public [Symbol.asyncIterator](): AsyncIterableIterator { + const readableStream = this.readableStream; + const iterator = readableStream[Symbol.asyncIterator](); + + // Create and return an async iterator that yields buffers + return { + async next(): Promise> { + const { value, done } = await iterator.next(); + return { value: value as Buffer, done }; + }, + [Symbol.asyncIterator]() { + return this; + }, + }; + } +} diff --git a/seed/ts-sdk/license/src/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts b/seed/ts-sdk/license/src/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts new file mode 100644 index 00000000000..263af00911f --- /dev/null +++ b/seed/ts-sdk/license/src/core/fetcher/stream-wrappers/UndiciStreamWrapper.ts @@ -0,0 +1,243 @@ +import { StreamWrapper } from "./chooseStreamWrapper"; + +type EventCallback = (data?: any) => void; + +export class UndiciStreamWrapper + implements StreamWrapper | WritableStream, ReadFormat> +{ + private readableStream: ReadableStream; + private reader: ReadableStreamDefaultReader; + private events: Record; + private paused: boolean; + private resumeCallback: ((value?: unknown) => void) | null; + private encoding: string | null; + + constructor(readableStream: ReadableStream) { + this.readableStream = readableStream; + this.reader = this.readableStream.getReader(); + this.events = { + data: [], + end: [], + error: [], + readable: [], + close: [], + pause: [], + resume: [], + }; + this.paused = false; + this.resumeCallback = null; + this.encoding = null; + } + + public on(event: string, callback: EventCallback): void { + this.events[event]?.push(callback); + } + + public off(event: string, callback: EventCallback): void { + this.events[event] = this.events[event]?.filter((cb) => cb !== callback); + } + + public pipe( + dest: UndiciStreamWrapper | WritableStream + ): UndiciStreamWrapper | WritableStream { + this.on("data", (chunk) => { + if (dest instanceof UndiciStreamWrapper) { + dest._write(chunk); + } else { + const writer = dest.getWriter(); + writer.write(chunk).then(() => writer.releaseLock()); + } + }); + + this.on("end", () => { + if (dest instanceof UndiciStreamWrapper) { + dest._end(); + } else { + const writer = dest.getWriter(); + writer.close(); + } + }); + + this.on("error", (error) => { + if (dest instanceof UndiciStreamWrapper) { + dest._error(error); + } else { + const writer = dest.getWriter(); + writer.abort(error); + } + }); + + this._startReading(); + + return dest; + } + + public pipeTo( + dest: UndiciStreamWrapper | WritableStream + ): UndiciStreamWrapper | WritableStream { + return this.pipe(dest); + } + + public unpipe(dest: UndiciStreamWrapper | WritableStream): void { + this.off("data", (chunk) => { + if (dest instanceof UndiciStreamWrapper) { + dest._write(chunk); + } else { + const writer = dest.getWriter(); + writer.write(chunk).then(() => writer.releaseLock()); + } + }); + + this.off("end", () => { + if (dest instanceof UndiciStreamWrapper) { + dest._end(); + } else { + const writer = dest.getWriter(); + writer.close(); + } + }); + + this.off("error", (error) => { + if (dest instanceof UndiciStreamWrapper) { + dest._error(error); + } else { + const writer = dest.getWriter(); + writer.abort(error); + } + }); + } + + public destroy(error?: Error): void { + this.reader + .cancel(error) + .then(() => { + this._emit("close"); + }) + .catch((err) => { + this._emit("error", err); + }); + } + + public pause(): void { + this.paused = true; + this._emit("pause"); + } + + public resume(): void { + if (this.paused) { + this.paused = false; + this._emit("resume"); + if (this.resumeCallback) { + this.resumeCallback(); + this.resumeCallback = null; + } + } + } + + public get isPaused(): boolean { + return this.paused; + } + + public async read(): Promise { + if (this.paused) { + await new Promise((resolve) => { + this.resumeCallback = resolve; + }); + } + const { done, value } = await this.reader.read(); + if (done) { + return undefined; + } + return value; + } + + public setEncoding(encoding: string): void { + this.encoding = encoding; + } + + public async text(): Promise { + const chunks: BlobPart[] = []; + + while (true) { + const { done, value } = await this.reader.read(); + if (done) { + break; + } + if (value) { + chunks.push(value); + } + } + + const decoder = new TextDecoder(this.encoding || "utf-8"); + return decoder.decode(await new Blob(chunks).arrayBuffer()); + } + + public async json(): Promise { + const text = await this.text(); + return JSON.parse(text); + } + + private _write(chunk: ReadFormat): void { + this._emit("data", chunk); + } + + private _end(): void { + this._emit("end"); + } + + private _error(error: any): void { + this._emit("error", error); + } + + private _emit(event: string, data?: any): void { + if (this.events[event]) { + for (const callback of this.events[event] || []) { + callback(data); + } + } + } + + private async _startReading(): Promise { + try { + this._emit("readable"); + while (true) { + if (this.paused) { + await new Promise((resolve) => { + this.resumeCallback = resolve; + }); + } + const { done, value } = await this.reader.read(); + if (done) { + this._emit("end"); + this._emit("close"); + break; + } + if (value) { + this._emit("data", value); + } + } + } catch (error) { + this._emit("error", error); + } + } + + [Symbol.asyncIterator](): AsyncIterableIterator { + return { + next: async () => { + if (this.paused) { + await new Promise((resolve) => { + this.resumeCallback = resolve; + }); + } + const { done, value } = await this.reader.read(); + if (done) { + return { done: true, value: undefined }; + } + return { done: false, value }; + }, + [Symbol.asyncIterator]() { + return this; + }, + }; + } +} diff --git a/seed/ts-sdk/license/src/core/fetcher/stream-wrappers/chooseStreamWrapper.ts b/seed/ts-sdk/license/src/core/fetcher/stream-wrappers/chooseStreamWrapper.ts new file mode 100644 index 00000000000..2abd6b2ba1c --- /dev/null +++ b/seed/ts-sdk/license/src/core/fetcher/stream-wrappers/chooseStreamWrapper.ts @@ -0,0 +1,33 @@ +import type { Readable } from "readable-stream"; +import { RUNTIME } from "../../runtime"; + +export type EventCallback = (data?: any) => void; + +export interface StreamWrapper { + setEncoding(encoding?: string): void; + on(event: string, callback: EventCallback): void; + off(event: string, callback: EventCallback): void; + pipe(dest: WritableStream): WritableStream; + pipeTo(dest: WritableStream): WritableStream; + unpipe(dest?: WritableStream): void; + destroy(error?: Error): void; + pause(): void; + resume(): void; + get isPaused(): boolean; + read(): Promise; + text(): Promise; + json(): Promise; + [Symbol.asyncIterator](): AsyncIterableIterator; +} + +export async function chooseStreamWrapper(responseBody: any): Promise>> { + if (RUNTIME.type === "node" && RUNTIME.parsedVersion != null && RUNTIME.parsedVersion >= 18) { + return new (await import("./Node18UniversalStreamWrapper")).Node18UniversalStreamWrapper( + responseBody as ReadableStream + ); + } else if (RUNTIME.type !== "node" && typeof fetch === "function") { + return new (await import("./UndiciStreamWrapper")).UndiciStreamWrapper(responseBody as ReadableStream); + } else { + return new (await import("./NodePre18StreamWrapper")).NodePre18StreamWrapper(responseBody as Readable); + } +} diff --git a/seed/ts-sdk/license/src/core/index.ts b/seed/ts-sdk/license/src/core/index.ts new file mode 100644 index 00000000000..e3006860f4d --- /dev/null +++ b/seed/ts-sdk/license/src/core/index.ts @@ -0,0 +1,3 @@ +export * from "./fetcher"; +export * from "./runtime"; +export * as serialization from "./schemas"; diff --git a/seed/ts-sdk/license/src/core/runtime/index.ts b/seed/ts-sdk/license/src/core/runtime/index.ts new file mode 100644 index 00000000000..5c76dbb133f --- /dev/null +++ b/seed/ts-sdk/license/src/core/runtime/index.ts @@ -0,0 +1 @@ +export { RUNTIME } from "./runtime"; diff --git a/seed/ts-sdk/license/src/core/runtime/runtime.ts b/seed/ts-sdk/license/src/core/runtime/runtime.ts new file mode 100644 index 00000000000..4d0687e8eb4 --- /dev/null +++ b/seed/ts-sdk/license/src/core/runtime/runtime.ts @@ -0,0 +1,126 @@ +interface DenoGlobal { + version: { + deno: string; + }; +} + +interface BunGlobal { + version: string; +} + +declare const Deno: DenoGlobal; +declare const Bun: BunGlobal; + +/** + * A constant that indicates whether the environment the code is running is a Web Browser. + */ +const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined"; + +/** + * A constant that indicates whether the environment the code is running is a Web Worker. + */ +const isWebWorker = + typeof self === "object" && + // @ts-ignore + typeof self?.importScripts === "function" && + (self.constructor?.name === "DedicatedWorkerGlobalScope" || + self.constructor?.name === "ServiceWorkerGlobalScope" || + self.constructor?.name === "SharedWorkerGlobalScope"); + +/** + * A constant that indicates whether the environment the code is running is Deno. + */ +const isDeno = + typeof Deno !== "undefined" && typeof Deno.version !== "undefined" && typeof Deno.version.deno !== "undefined"; + +/** + * A constant that indicates whether the environment the code is running is Bun.sh. + */ +const isBun = typeof Bun !== "undefined" && typeof Bun.version !== "undefined"; + +/** + * A constant that indicates whether the environment the code is running is Node.JS. + */ +const isNode = + typeof process !== "undefined" && + Boolean(process.version) && + Boolean(process.versions?.node) && + // Deno spoofs process.versions.node, see https://deno.land/std@0.177.0/node/process.ts?s=versions + !isDeno && + !isBun; + +/** + * A constant that indicates whether the environment the code is running is in React-Native. + * https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Core/setUpNavigator.js + */ +const isReactNative = typeof navigator !== "undefined" && navigator?.product === "ReactNative"; + +/** + * A constant that indicates whether the environment the code is running is Cloudflare. + * https://developers.cloudflare.com/workers/runtime-apis/web-standards/#navigatoruseragent + */ +const isCloudflare = typeof globalThis !== "undefined" && globalThis?.navigator?.userAgent === "Cloudflare-Workers"; + +/** + * A constant that indicates which environment and version the SDK is running in. + */ +export const RUNTIME: Runtime = evaluateRuntime(); + +export interface Runtime { + type: "browser" | "web-worker" | "deno" | "bun" | "node" | "react-native" | "unknown" | "workerd"; + version?: string; + parsedVersion?: number; +} + +function evaluateRuntime(): Runtime { + if (isBrowser) { + return { + type: "browser", + version: window.navigator.userAgent, + }; + } + + if (isCloudflare) { + return { + type: "workerd", + }; + } + + if (isWebWorker) { + return { + type: "web-worker", + }; + } + + if (isDeno) { + return { + type: "deno", + version: Deno.version.deno, + }; + } + + if (isBun) { + return { + type: "bun", + version: Bun.version, + }; + } + + if (isNode) { + return { + type: "node", + version: process.versions.node, + parsedVersion: Number(process.versions.node.split(".")[0]), + }; + } + + if (isReactNative) { + return { + type: "react-native", + }; + } + + return { + type: "unknown", + }; +} diff --git a/seed/ts-sdk/license/src/core/schemas/Schema.ts b/seed/ts-sdk/license/src/core/schemas/Schema.ts new file mode 100644 index 00000000000..2a72eacec99 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/Schema.ts @@ -0,0 +1,99 @@ +import { SchemaUtils } from "./builders"; + +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", + RECORD: "record", + SET: "set", + UNION: "union", + UNDISCRIMINATED_UNION: "undiscriminatedUnion", + OPTIONAL: "optional", +} 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-sdk/license/src/core/schemas/builders/bigint/bigint.ts b/seed/ts-sdk/license/src/core/schemas/builders/bigint/bigint.ts new file mode 100644 index 00000000000..dc9c742e007 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/bigint/bigint.ts @@ -0,0 +1,50 @@ +import { BaseSchema, Schema, SchemaType } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils"; + +export function bigint(): Schema { + const baseSchema: BaseSchema = { + parse: (raw, { breadcrumbsPrefix = [] } = {}) => { + if (typeof raw !== "string") { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(raw, "string"), + }, + ], + }; + } + return { + ok: true, + value: BigInt(raw), + }; + }, + json: (bigint, { breadcrumbsPrefix = [] } = {}) => { + if (typeof bigint === "bigint") { + return { + ok: true, + value: bigint.toString(), + }; + } else { + return { + ok: false, + errors: [ + { + path: breadcrumbsPrefix, + message: getErrorMessageForIncorrectType(bigint, "bigint"), + }, + ], + }; + } + }, + getType: () => SchemaType.BIGINT, + }; + + return { + ...maybeSkipValidation(baseSchema), + ...getSchemaUtils(baseSchema), + }; +} diff --git a/seed/ts-sdk/license/src/core/schemas/builders/bigint/index.ts b/seed/ts-sdk/license/src/core/schemas/builders/bigint/index.ts new file mode 100644 index 00000000000..e5843043fcb --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/bigint/index.ts @@ -0,0 +1 @@ +export { bigint } from "./bigint"; diff --git a/seed/ts-sdk/license/src/core/schemas/builders/date/date.ts b/seed/ts-sdk/license/src/core/schemas/builders/date/date.ts new file mode 100644 index 00000000000..b70f24b045a --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/date/date.ts @@ -0,0 +1,65 @@ +import { BaseSchema, Schema, SchemaType } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils"; + +// 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-sdk/license/src/core/schemas/builders/date/index.ts b/seed/ts-sdk/license/src/core/schemas/builders/date/index.ts new file mode 100644 index 00000000000..187b29040f6 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/date/index.ts @@ -0,0 +1 @@ +export { date } from "./date"; diff --git a/seed/ts-sdk/license/src/core/schemas/builders/enum/enum.ts b/seed/ts-sdk/license/src/core/schemas/builders/enum/enum.ts new file mode 100644 index 00000000000..c1e24d69dec --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/enum/enum.ts @@ -0,0 +1,43 @@ +import { 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-sdk/license/src/core/schemas/builders/enum/index.ts b/seed/ts-sdk/license/src/core/schemas/builders/enum/index.ts new file mode 100644 index 00000000000..fe6faed93e3 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/enum/index.ts @@ -0,0 +1 @@ +export { enum_ } from "./enum"; diff --git a/seed/ts-sdk/license/src/core/schemas/builders/index.ts b/seed/ts-sdk/license/src/core/schemas/builders/index.ts new file mode 100644 index 00000000000..65211f92522 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/index.ts @@ -0,0 +1,14 @@ +export * from "./bigint"; +export * from "./date"; +export * from "./enum"; +export * from "./lazy"; +export * from "./list"; +export * from "./literals"; +export * from "./object"; +export * from "./object-like"; +export * from "./primitives"; +export * from "./record"; +export * from "./schema-utils"; +export * from "./set"; +export * from "./undiscriminated-union"; +export * from "./union"; diff --git a/seed/ts-sdk/license/src/core/schemas/builders/lazy/index.ts b/seed/ts-sdk/license/src/core/schemas/builders/lazy/index.ts new file mode 100644 index 00000000000..77420fb031c --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/lazy/index.ts @@ -0,0 +1,3 @@ +export { lazy } from "./lazy"; +export type { SchemaGetter } from "./lazy"; +export { lazyObject } from "./lazyObject"; diff --git a/seed/ts-sdk/license/src/core/schemas/builders/lazy/lazy.ts b/seed/ts-sdk/license/src/core/schemas/builders/lazy/lazy.ts new file mode 100644 index 00000000000..835c61f8a56 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/lazy/lazy.ts @@ -0,0 +1,32 @@ +import { BaseSchema, Schema } from "../../Schema"; +import { getSchemaUtils } from "../schema-utils"; + +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-sdk/license/src/core/schemas/builders/lazy/lazyObject.ts b/seed/ts-sdk/license/src/core/schemas/builders/lazy/lazyObject.ts new file mode 100644 index 00000000000..38c9e28404b --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/lazy/lazyObject.ts @@ -0,0 +1,20 @@ +import { getObjectUtils } from "../object"; +import { getObjectLikeUtils } from "../object-like"; +import { BaseObjectSchema, ObjectSchema } from "../object/types"; +import { getSchemaUtils } from "../schema-utils"; +import { constructLazyBaseSchema, getMemoizedSchema, 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-sdk/license/src/core/schemas/builders/list/index.ts b/seed/ts-sdk/license/src/core/schemas/builders/list/index.ts new file mode 100644 index 00000000000..25f4bcc1737 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/list/index.ts @@ -0,0 +1 @@ +export { list } from "./list"; diff --git a/seed/ts-sdk/license/src/core/schemas/builders/list/list.ts b/seed/ts-sdk/license/src/core/schemas/builders/list/list.ts new file mode 100644 index 00000000000..e4c5c4a4a99 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/list/list.ts @@ -0,0 +1,73 @@ +import { BaseSchema, MaybeValid, Schema, SchemaType, ValidationError } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils"; + +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-sdk/license/src/core/schemas/builders/literals/booleanLiteral.ts b/seed/ts-sdk/license/src/core/schemas/builders/literals/booleanLiteral.ts new file mode 100644 index 00000000000..a83d22cd48a --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/literals/booleanLiteral.ts @@ -0,0 +1,29 @@ +import { 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-sdk/license/src/core/schemas/builders/literals/index.ts b/seed/ts-sdk/license/src/core/schemas/builders/literals/index.ts new file mode 100644 index 00000000000..d2bf08fc6ca --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/literals/index.ts @@ -0,0 +1,2 @@ +export { stringLiteral } from "./stringLiteral"; +export { booleanLiteral } from "./booleanLiteral"; diff --git a/seed/ts-sdk/license/src/core/schemas/builders/literals/stringLiteral.ts b/seed/ts-sdk/license/src/core/schemas/builders/literals/stringLiteral.ts new file mode 100644 index 00000000000..3939b76b48d --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/literals/stringLiteral.ts @@ -0,0 +1,29 @@ +import { 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-sdk/license/src/core/schemas/builders/object-like/getObjectLikeUtils.ts b/seed/ts-sdk/license/src/core/schemas/builders/object-like/getObjectLikeUtils.ts new file mode 100644 index 00000000000..8331d08da89 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/object-like/getObjectLikeUtils.ts @@ -0,0 +1,79 @@ +import { BaseSchema } from "../../Schema"; +import { filterObject } from "../../utils/filterObject"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { isPlainObject } from "../../utils/isPlainObject"; +import { getSchemaUtils } from "../schema-utils"; +import { 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-sdk/license/src/core/schemas/builders/object-like/index.ts b/seed/ts-sdk/license/src/core/schemas/builders/object-like/index.ts new file mode 100644 index 00000000000..c342e72cf9d --- /dev/null +++ b/seed/ts-sdk/license/src/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-sdk/license/src/core/schemas/builders/object-like/types.ts b/seed/ts-sdk/license/src/core/schemas/builders/object-like/types.ts new file mode 100644 index 00000000000..75b3698729c --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/object-like/types.ts @@ -0,0 +1,11 @@ +import { 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-sdk/license/src/core/schemas/builders/object/index.ts b/seed/ts-sdk/license/src/core/schemas/builders/object/index.ts new file mode 100644 index 00000000000..e3f4388db28 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/object/index.ts @@ -0,0 +1,22 @@ +export { getObjectUtils, object } from "./object"; +export { objectWithoutOptionalProperties } from "./objectWithoutOptionalProperties"; +export type { + inferObjectWithoutOptionalPropertiesSchemaFromPropertySchemas, + inferParsedObjectWithoutOptionalPropertiesFromPropertySchemas, +} from "./objectWithoutOptionalProperties"; +export { isProperty, property } from "./property"; +export type { Property } from "./property"; +export type { + BaseObjectSchema, + inferObjectSchemaFromPropertySchemas, + inferParsedObject, + inferParsedObjectFromPropertySchemas, + inferParsedPropertySchema, + inferRawKey, + inferRawObject, + inferRawObjectFromPropertySchemas, + inferRawPropertySchema, + ObjectSchema, + ObjectUtils, + PropertySchemas, +} from "./types"; diff --git a/seed/ts-sdk/license/src/core/schemas/builders/object/object.ts b/seed/ts-sdk/license/src/core/schemas/builders/object/object.ts new file mode 100644 index 00000000000..e00136d72fc --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/object/object.ts @@ -0,0 +1,324 @@ +import { MaybeValid, Schema, SchemaType, 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"; +import { getSchemaUtils } from "../schema-utils"; +import { isProperty } from "./property"; +import { + 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: unknown) => 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: unknown) => 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); + 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), + }); + }, + json: (parsed, opts) => { + return validateAndTransformExtendedObject({ + extensionKeys: extension._getParsedProperties(), + value: parsed, + transformBase: (parsedBase) => schema.json(parsedBase, opts), + transformExtension: (parsedExtension) => extension.json(parsedExtension, opts), + }); + }, + getType: () => SchemaType.OBJECT, + }; + + return { + ...baseSchema, + ...getSchemaUtils(baseSchema), + ...getObjectLikeUtils(baseSchema), + ...getObjectUtils(baseSchema), + }; + }, + }; +} + +function validateAndTransformExtendedObject({ + extensionKeys, + value, + transformBase, + transformExtension, +}: { + extensionKeys: (keyof PreTransformedExtension)[]; + value: unknown; + transformBase: (value: unknown) => MaybeValid; + transformExtension: (value: unknown) => MaybeValid; +}): MaybeValid { + 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: + return true; + default: + return false; + } +} diff --git a/seed/ts-sdk/license/src/core/schemas/builders/object/objectWithoutOptionalProperties.ts b/seed/ts-sdk/license/src/core/schemas/builders/object/objectWithoutOptionalProperties.ts new file mode 100644 index 00000000000..a0951f48efc --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/object/objectWithoutOptionalProperties.ts @@ -0,0 +1,18 @@ +import { object } from "./object"; +import { 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-sdk/license/src/core/schemas/builders/object/property.ts b/seed/ts-sdk/license/src/core/schemas/builders/object/property.ts new file mode 100644 index 00000000000..d245c4b193a --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/object/property.ts @@ -0,0 +1,23 @@ +import { 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-sdk/license/src/core/schemas/builders/object/types.ts b/seed/ts-sdk/license/src/core/schemas/builders/object/types.ts new file mode 100644 index 00000000000..de9bb4074e5 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/object/types.ts @@ -0,0 +1,72 @@ +import { BaseSchema, inferParsed, inferRaw, Schema } from "../../Schema"; +import { addQuestionMarksToNullableProperties } from "../../utils/addQuestionMarksToNullableProperties"; +import { ObjectLikeUtils } from "../object-like"; +import { SchemaUtils } from "../schema-utils"; +import { 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; +} + +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-sdk/license/src/core/schemas/builders/primitives/any.ts b/seed/ts-sdk/license/src/core/schemas/builders/primitives/any.ts new file mode 100644 index 00000000000..fcaeb04255a --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/primitives/any.ts @@ -0,0 +1,4 @@ +import { SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; + +export const any = createIdentitySchemaCreator(SchemaType.ANY, (value) => ({ ok: true, value })); diff --git a/seed/ts-sdk/license/src/core/schemas/builders/primitives/boolean.ts b/seed/ts-sdk/license/src/core/schemas/builders/primitives/boolean.ts new file mode 100644 index 00000000000..fad60562120 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/primitives/boolean.ts @@ -0,0 +1,25 @@ +import { SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export const boolean = 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-sdk/license/src/core/schemas/builders/primitives/index.ts b/seed/ts-sdk/license/src/core/schemas/builders/primitives/index.ts new file mode 100644 index 00000000000..788f9416bfe --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/primitives/index.ts @@ -0,0 +1,5 @@ +export { any } from "./any"; +export { boolean } from "./boolean"; +export { number } from "./number"; +export { string } from "./string"; +export { unknown } from "./unknown"; diff --git a/seed/ts-sdk/license/src/core/schemas/builders/primitives/number.ts b/seed/ts-sdk/license/src/core/schemas/builders/primitives/number.ts new file mode 100644 index 00000000000..c2689456936 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/primitives/number.ts @@ -0,0 +1,25 @@ +import { SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export const number = 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-sdk/license/src/core/schemas/builders/primitives/string.ts b/seed/ts-sdk/license/src/core/schemas/builders/primitives/string.ts new file mode 100644 index 00000000000..949f1f2a630 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/primitives/string.ts @@ -0,0 +1,25 @@ +import { SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; + +export const string = 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-sdk/license/src/core/schemas/builders/primitives/unknown.ts b/seed/ts-sdk/license/src/core/schemas/builders/primitives/unknown.ts new file mode 100644 index 00000000000..4d5249571f5 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/primitives/unknown.ts @@ -0,0 +1,4 @@ +import { SchemaType } from "../../Schema"; +import { createIdentitySchemaCreator } from "../../utils/createIdentitySchemaCreator"; + +export const unknown = createIdentitySchemaCreator(SchemaType.UNKNOWN, (value) => ({ ok: true, value })); diff --git a/seed/ts-sdk/license/src/core/schemas/builders/record/index.ts b/seed/ts-sdk/license/src/core/schemas/builders/record/index.ts new file mode 100644 index 00000000000..82e25c5c2af --- /dev/null +++ b/seed/ts-sdk/license/src/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-sdk/license/src/core/schemas/builders/record/record.ts b/seed/ts-sdk/license/src/core/schemas/builders/record/record.ts new file mode 100644 index 00000000000..6683ac3609f --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/record/record.ts @@ -0,0 +1,130 @@ +import { MaybeValid, Schema, SchemaType, 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"; +import { 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]) => { + // skip nullish keys + if (value == null) { + return accPromise; + } + + const acc = accPromise; + + let key: string | number = stringKey; + if (isKeyNumeric) { + const numberKey = stringKey.length > 0 ? Number(stringKey) : NaN; + if (!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-sdk/license/src/core/schemas/builders/record/types.ts b/seed/ts-sdk/license/src/core/schemas/builders/record/types.ts new file mode 100644 index 00000000000..eb82cc7f65c --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/record/types.ts @@ -0,0 +1,17 @@ +import { BaseSchema } from "../../Schema"; +import { SchemaUtils } from "../schema-utils"; + +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-sdk/license/src/core/schemas/builders/schema-utils/JsonError.ts b/seed/ts-sdk/license/src/core/schemas/builders/schema-utils/JsonError.ts new file mode 100644 index 00000000000..2b89ca0e7ad --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/schema-utils/JsonError.ts @@ -0,0 +1,9 @@ +import { 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-sdk/license/src/core/schemas/builders/schema-utils/ParseError.ts b/seed/ts-sdk/license/src/core/schemas/builders/schema-utils/ParseError.ts new file mode 100644 index 00000000000..d056eb45cf7 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/schema-utils/ParseError.ts @@ -0,0 +1,9 @@ +import { 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-sdk/license/src/core/schemas/builders/schema-utils/getSchemaUtils.ts b/seed/ts-sdk/license/src/core/schemas/builders/schema-utils/getSchemaUtils.ts new file mode 100644 index 00000000000..79ecad92132 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/schema-utils/getSchemaUtils.ts @@ -0,0 +1,105 @@ +import { BaseSchema, Schema, SchemaOptions, SchemaType } from "../../Schema"; +import { JsonError } from "./JsonError"; +import { ParseError } from "./ParseError"; + +export interface SchemaUtils { + optional: () => 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 { + optional: () => optional(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 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 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-sdk/license/src/core/schemas/builders/schema-utils/index.ts b/seed/ts-sdk/license/src/core/schemas/builders/schema-utils/index.ts new file mode 100644 index 00000000000..aa04e051dfa --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/schema-utils/index.ts @@ -0,0 +1,4 @@ +export { getSchemaUtils, optional, transform } from "./getSchemaUtils"; +export type { SchemaUtils } from "./getSchemaUtils"; +export { JsonError } from "./JsonError"; +export { ParseError } from "./ParseError"; diff --git a/seed/ts-sdk/license/src/core/schemas/builders/schema-utils/stringifyValidationErrors.ts b/seed/ts-sdk/license/src/core/schemas/builders/schema-utils/stringifyValidationErrors.ts new file mode 100644 index 00000000000..4160f0a2617 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/schema-utils/stringifyValidationErrors.ts @@ -0,0 +1,8 @@ +import { 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-sdk/license/src/core/schemas/builders/set/index.ts b/seed/ts-sdk/license/src/core/schemas/builders/set/index.ts new file mode 100644 index 00000000000..f3310e8bdad --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/set/index.ts @@ -0,0 +1 @@ +export { set } from "./set"; diff --git a/seed/ts-sdk/license/src/core/schemas/builders/set/set.ts b/seed/ts-sdk/license/src/core/schemas/builders/set/set.ts new file mode 100644 index 00000000000..e9e6bb7e539 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/set/set.ts @@ -0,0 +1,43 @@ +import { BaseSchema, Schema, SchemaType } from "../../Schema"; +import { getErrorMessageForIncorrectType } from "../../utils/getErrorMessageForIncorrectType"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { list } from "../list"; +import { getSchemaUtils } from "../schema-utils"; + +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-sdk/license/src/core/schemas/builders/undiscriminated-union/index.ts b/seed/ts-sdk/license/src/core/schemas/builders/undiscriminated-union/index.ts new file mode 100644 index 00000000000..75b71cb3565 --- /dev/null +++ b/seed/ts-sdk/license/src/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-sdk/license/src/core/schemas/builders/undiscriminated-union/types.ts b/seed/ts-sdk/license/src/core/schemas/builders/undiscriminated-union/types.ts new file mode 100644 index 00000000000..43e7108a060 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/undiscriminated-union/types.ts @@ -0,0 +1,10 @@ +import { inferParsed, inferRaw, Schema } from "../../Schema"; + +export type UndiscriminatedUnionSchema = Schema< + inferRawUnidiscriminatedUnionSchema, + inferParsedUnidiscriminatedUnionSchema +>; + +export type inferRawUnidiscriminatedUnionSchema = inferRaw; + +export type inferParsedUnidiscriminatedUnionSchema = inferParsed; diff --git a/seed/ts-sdk/license/src/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts b/seed/ts-sdk/license/src/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts new file mode 100644 index 00000000000..21ed3df0f40 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/undiscriminated-union/undiscriminatedUnion.ts @@ -0,0 +1,60 @@ +import { BaseSchema, MaybeValid, Schema, SchemaOptions, SchemaType, ValidationError } from "../../Schema"; +import { maybeSkipValidation } from "../../utils/maybeSkipValidation"; +import { getSchemaUtils } from "../schema-utils"; +import { 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-sdk/license/src/core/schemas/builders/union/discriminant.ts b/seed/ts-sdk/license/src/core/schemas/builders/union/discriminant.ts new file mode 100644 index 00000000000..55065bc8946 --- /dev/null +++ b/seed/ts-sdk/license/src/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-sdk/license/src/core/schemas/builders/union/index.ts b/seed/ts-sdk/license/src/core/schemas/builders/union/index.ts new file mode 100644 index 00000000000..85fc008a2d8 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/union/index.ts @@ -0,0 +1,10 @@ +export { discriminant } from "./discriminant"; +export type { Discriminant } from "./discriminant"; +export type { + inferParsedDiscriminant, + inferParsedUnion, + inferRawDiscriminant, + inferRawUnion, + UnionSubtypes, +} from "./types"; +export { union } from "./union"; diff --git a/seed/ts-sdk/license/src/core/schemas/builders/union/types.ts b/seed/ts-sdk/license/src/core/schemas/builders/union/types.ts new file mode 100644 index 00000000000..6f82c868b2d --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/union/types.ts @@ -0,0 +1,26 @@ +import { inferParsedObject, inferRawObject, ObjectSchema } from "../object"; +import { 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-sdk/license/src/core/schemas/builders/union/union.ts b/seed/ts-sdk/license/src/core/schemas/builders/union/union.ts new file mode 100644 index 00000000000..ab61475a572 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/builders/union/union.ts @@ -0,0 +1,170 @@ +import { BaseSchema, 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"; +import { ObjectSchema } from "../object"; +import { getObjectLikeUtils, ObjectLikeSchema } from "../object-like"; +import { getSchemaUtils } from "../schema-utils"; +import { Discriminant } from "./discriminant"; +import { 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-sdk/license/src/core/schemas/index.ts b/seed/ts-sdk/license/src/core/schemas/index.ts new file mode 100644 index 00000000000..5429d8b43eb --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/index.ts @@ -0,0 +1,2 @@ +export * from "./builders"; +export type { inferParsed, inferRaw, Schema, SchemaOptions } from "./Schema"; diff --git a/seed/ts-sdk/license/src/core/schemas/utils/MaybePromise.ts b/seed/ts-sdk/license/src/core/schemas/utils/MaybePromise.ts new file mode 100644 index 00000000000..9cd354b3418 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/utils/MaybePromise.ts @@ -0,0 +1 @@ +export type MaybePromise = T | Promise; diff --git a/seed/ts-sdk/license/src/core/schemas/utils/addQuestionMarksToNullableProperties.ts b/seed/ts-sdk/license/src/core/schemas/utils/addQuestionMarksToNullableProperties.ts new file mode 100644 index 00000000000..4111d703cd0 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/utils/addQuestionMarksToNullableProperties.ts @@ -0,0 +1,15 @@ +export type addQuestionMarksToNullableProperties = { + [K in OptionalKeys]?: T[K]; +} & Pick>; + +export type OptionalKeys = { + [K in keyof T]-?: undefined extends T[K] + ? K + : null extends T[K] + ? K + : 1 extends (any extends T[K] ? 0 : 1) + ? never + : K; +}[keyof T]; + +export type RequiredKeys = Exclude>; diff --git a/seed/ts-sdk/license/src/core/schemas/utils/createIdentitySchemaCreator.ts b/seed/ts-sdk/license/src/core/schemas/utils/createIdentitySchemaCreator.ts new file mode 100644 index 00000000000..de107cf5ee1 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/utils/createIdentitySchemaCreator.ts @@ -0,0 +1,21 @@ +import { getSchemaUtils } from "../builders/schema-utils"; +import { 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-sdk/license/src/core/schemas/utils/entries.ts b/seed/ts-sdk/license/src/core/schemas/utils/entries.ts new file mode 100644 index 00000000000..e122952137d --- /dev/null +++ b/seed/ts-sdk/license/src/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-sdk/license/src/core/schemas/utils/filterObject.ts b/seed/ts-sdk/license/src/core/schemas/utils/filterObject.ts new file mode 100644 index 00000000000..2c25a3455bc --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/utils/filterObject.ts @@ -0,0 +1,10 @@ +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; + } + return acc; + // eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter + }, {} as Pick); +} diff --git a/seed/ts-sdk/license/src/core/schemas/utils/getErrorMessageForIncorrectType.ts b/seed/ts-sdk/license/src/core/schemas/utils/getErrorMessageForIncorrectType.ts new file mode 100644 index 00000000000..1a5c31027ce --- /dev/null +++ b/seed/ts-sdk/license/src/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-sdk/license/src/core/schemas/utils/isPlainObject.ts b/seed/ts-sdk/license/src/core/schemas/utils/isPlainObject.ts new file mode 100644 index 00000000000..db82a722c35 --- /dev/null +++ b/seed/ts-sdk/license/src/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-sdk/license/src/core/schemas/utils/keys.ts b/seed/ts-sdk/license/src/core/schemas/utils/keys.ts new file mode 100644 index 00000000000..01867098287 --- /dev/null +++ b/seed/ts-sdk/license/src/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-sdk/license/src/core/schemas/utils/maybeSkipValidation.ts b/seed/ts-sdk/license/src/core/schemas/utils/maybeSkipValidation.ts new file mode 100644 index 00000000000..86c07abf2b4 --- /dev/null +++ b/seed/ts-sdk/license/src/core/schemas/utils/maybeSkipValidation.ts @@ -0,0 +1,38 @@ +import { 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) { + // eslint-disable-next-line no-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-sdk/license/src/core/schemas/utils/partition.ts b/seed/ts-sdk/license/src/core/schemas/utils/partition.ts new file mode 100644 index 00000000000..f58d6f3d35f --- /dev/null +++ b/seed/ts-sdk/license/src/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-sdk/license/src/errors/SeedLicenseError.ts b/seed/ts-sdk/license/src/errors/SeedLicenseError.ts new file mode 100644 index 00000000000..727e5e8898b --- /dev/null +++ b/seed/ts-sdk/license/src/errors/SeedLicenseError.ts @@ -0,0 +1,45 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export class SeedLicenseError extends Error { + readonly statusCode?: number; + readonly body?: unknown; + + constructor({ message, statusCode, body }: { message?: string; statusCode?: number; body?: unknown }) { + super(buildMessage({ message, statusCode, body })); + Object.setPrototypeOf(this, SeedLicenseError.prototype); + if (statusCode != null) { + this.statusCode = statusCode; + } + + if (body !== undefined) { + this.body = body; + } + } +} + +function buildMessage({ + message, + statusCode, + body, +}: { + message: string | undefined; + statusCode: number | undefined; + body: unknown | undefined; +}): string { + let lines: string[] = []; + if (message != null) { + lines.push(message); + } + + if (statusCode != null) { + lines.push(`Status code: ${statusCode.toString()}`); + } + + if (body != null) { + lines.push(`Body: ${JSON.stringify(body, undefined, 2)}`); + } + + return lines.join("\n"); +} diff --git a/seed/ts-sdk/license/src/errors/SeedLicenseTimeoutError.ts b/seed/ts-sdk/license/src/errors/SeedLicenseTimeoutError.ts new file mode 100644 index 00000000000..e09b806b089 --- /dev/null +++ b/seed/ts-sdk/license/src/errors/SeedLicenseTimeoutError.ts @@ -0,0 +1,10 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +export class SeedLicenseTimeoutError extends Error { + constructor() { + super("Timeout"); + Object.setPrototypeOf(this, SeedLicenseTimeoutError.prototype); + } +} diff --git a/seed/ts-sdk/license/src/errors/index.ts b/seed/ts-sdk/license/src/errors/index.ts new file mode 100644 index 00000000000..ec05f314f68 --- /dev/null +++ b/seed/ts-sdk/license/src/errors/index.ts @@ -0,0 +1,2 @@ +export { SeedLicenseError } from "./SeedLicenseError"; +export { SeedLicenseTimeoutError } from "./SeedLicenseTimeoutError"; diff --git a/seed/ts-sdk/license/src/index.ts b/seed/ts-sdk/license/src/index.ts new file mode 100644 index 00000000000..8eb410a9c84 --- /dev/null +++ b/seed/ts-sdk/license/src/index.ts @@ -0,0 +1,3 @@ +export * as SeedLicense from "./api"; +export { SeedLicenseClient } from "./Client"; +export { SeedLicenseError, SeedLicenseTimeoutError } from "./errors"; diff --git a/seed/ts-sdk/license/src/serialization/index.ts b/seed/ts-sdk/license/src/serialization/index.ts new file mode 100644 index 00000000000..eea524d6557 --- /dev/null +++ b/seed/ts-sdk/license/src/serialization/index.ts @@ -0,0 +1 @@ +export * from "./types"; diff --git a/seed/ts-sdk/license/src/serialization/types/Type.ts b/seed/ts-sdk/license/src/serialization/types/Type.ts new file mode 100644 index 00000000000..dcea84426af --- /dev/null +++ b/seed/ts-sdk/license/src/serialization/types/Type.ts @@ -0,0 +1,17 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ + +import * as serializers from "../index"; +import * as SeedLicense from "../../api/index"; +import * as core from "../../core"; + +export const Type: core.serialization.ObjectSchema = core.serialization.object({ + name: core.serialization.string(), +}); + +export declare namespace Type { + interface Raw { + name: string; + } +} diff --git a/seed/ts-sdk/license/src/serialization/types/index.ts b/seed/ts-sdk/license/src/serialization/types/index.ts new file mode 100644 index 00000000000..3edf389a5d5 --- /dev/null +++ b/seed/ts-sdk/license/src/serialization/types/index.ts @@ -0,0 +1 @@ +export * from "./Type"; diff --git a/seed/ts-sdk/license/src/version.ts b/seed/ts-sdk/license/src/version.ts new file mode 100644 index 00000000000..b643a3e3ea2 --- /dev/null +++ b/seed/ts-sdk/license/src/version.ts @@ -0,0 +1 @@ +export const SDK_VERSION = "0.0.1"; diff --git a/seed/ts-sdk/license/tests/custom.test.ts b/seed/ts-sdk/license/tests/custom.test.ts new file mode 100644 index 00000000000..7f5e031c839 --- /dev/null +++ b/seed/ts-sdk/license/tests/custom.test.ts @@ -0,0 +1,13 @@ +/** + * 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. + */ +describe("test", () => { + it("default", () => { + expect(true).toBe(true); + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/fetcher/Fetcher.test.ts b/seed/ts-sdk/license/tests/unit/fetcher/Fetcher.test.ts new file mode 100644 index 00000000000..ff8042175ff --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/fetcher/Fetcher.test.ts @@ -0,0 +1,73 @@ +import fs from "fs"; +import { Fetcher, fetcherImpl } from "../../../src/core/fetcher/Fetcher"; +import { join } from "path"; + +describe("Test fetcherImpl", () => { + it("should handle successful request", async () => { + const mockArgs: Fetcher.Args = { + url: "https://httpbin.org/post", + method: "POST", + headers: { "X-Test": "x-test-header" }, + body: { data: "test" }, + contentType: "application/json", + requestType: "json", + }; + + global.fetch = jest.fn().mockResolvedValue({ + ok: true, + status: 200, + text: () => Promise.resolve(JSON.stringify({ data: "test" })), + json: () => ({ data: "test" }), + }); + + const result = await fetcherImpl(mockArgs); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.body).toEqual({ data: "test" }); + } + + expect(global.fetch).toHaveBeenCalledWith( + "https://httpbin.org/post", + expect.objectContaining({ + method: "POST", + headers: expect.objectContaining({ "X-Test": "x-test-header" }), + body: JSON.stringify({ data: "test" }), + }) + ); + }); + + it("should send octet stream", async () => { + const url = "https://httpbin.org/post/file"; + const mockArgs: Fetcher.Args = { + url, + method: "POST", + headers: { "X-Test": "x-test-header" }, + contentType: "application/octet-stream", + requestType: "bytes", + duplex: "half", + body: fs.createReadStream(join(__dirname, "test-file.txt")), + }; + + global.fetch = jest.fn().mockResolvedValue({ + ok: true, + status: 200, + text: () => Promise.resolve(JSON.stringify({ data: "test" })), + json: () => Promise.resolve({ data: "test" }), + }); + + const result = await fetcherImpl(mockArgs); + + expect(global.fetch).toHaveBeenCalledWith( + url, + expect.objectContaining({ + method: "POST", + headers: expect.objectContaining({ "X-Test": "x-test-header" }), + body: expect.any(fs.ReadStream), + }) + ); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.body).toEqual({ data: "test" }); + } + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/fetcher/createRequestUrl.test.ts b/seed/ts-sdk/license/tests/unit/fetcher/createRequestUrl.test.ts new file mode 100644 index 00000000000..f2cd24b6721 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/fetcher/createRequestUrl.test.ts @@ -0,0 +1,51 @@ +import { createRequestUrl } from "../../../src/core/fetcher/createRequestUrl"; + +describe("Test createRequestUrl", () => { + it("should return the base URL when no query parameters are provided", () => { + const baseUrl = "https://api.example.com"; + expect(createRequestUrl(baseUrl)).toBe(baseUrl); + }); + + it("should append simple query parameters", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { key: "value", another: "param" }; + expect(createRequestUrl(baseUrl, queryParams)).toBe("https://api.example.com?key=value&another=param"); + }); + + it("should handle array query parameters", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { items: ["a", "b", "c"] }; + expect(createRequestUrl(baseUrl, queryParams)).toBe("https://api.example.com?items=a&items=b&items=c"); + }); + + it("should handle object query parameters", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { filter: { name: "John", age: 30 } }; + expect(createRequestUrl(baseUrl, queryParams)).toBe( + "https://api.example.com?filter%5Bname%5D=John&filter%5Bage%5D=30" + ); + }); + + it("should handle mixed types of query parameters", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { + simple: "value", + array: ["x", "y"], + object: { key: "value" }, + }; + expect(createRequestUrl(baseUrl, queryParams)).toBe( + "https://api.example.com?simple=value&array=x&array=y&object%5Bkey%5D=value" + ); + }); + + it("should handle empty query parameters object", () => { + const baseUrl = "https://api.example.com"; + expect(createRequestUrl(baseUrl, {})).toBe(baseUrl); + }); + + it("should encode special characters in query parameters", () => { + const baseUrl = "https://api.example.com"; + const queryParams = { special: "a&b=c d" }; + expect(createRequestUrl(baseUrl, queryParams)).toBe("https://api.example.com?special=a%26b%3Dc%20d"); + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/fetcher/getFetchFn.test.ts b/seed/ts-sdk/license/tests/unit/fetcher/getFetchFn.test.ts new file mode 100644 index 00000000000..9b315ad095a --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/fetcher/getFetchFn.test.ts @@ -0,0 +1,22 @@ +import { RUNTIME } from "../../../src/core/runtime"; +import { getFetchFn } from "../../../src/core/fetcher/getFetchFn"; + +describe("Test for getFetchFn", () => { + it("should get node-fetch function", async () => { + if (RUNTIME.type == "node") { + if (RUNTIME.parsedVersion != null && RUNTIME.parsedVersion >= 18) { + expect(await getFetchFn()).toBe(fetch); + } else { + expect(await getFetchFn()).toEqual((await import("node-fetch")).default as any); + } + } + }); + + it("should get fetch function", async () => { + if (RUNTIME.type == "browser") { + const fetchFn = await getFetchFn(); + expect(typeof fetchFn).toBe("function"); + expect(fetchFn.name).toBe("fetch"); + } + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/fetcher/getRequestBody.test.ts b/seed/ts-sdk/license/tests/unit/fetcher/getRequestBody.test.ts new file mode 100644 index 00000000000..919604c2e12 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/fetcher/getRequestBody.test.ts @@ -0,0 +1,77 @@ +import { RUNTIME } from "../../../src/core/runtime"; +import { getRequestBody } from "../../../src/core/fetcher/getRequestBody"; + +describe("Test getRequestBody", () => { + it("should return FormData as is in Node environment", async () => { + if (RUNTIME.type === "node") { + const formData = new (await import("formdata-node")).FormData(); + formData.append("key", "value"); + const result = await getRequestBody({ + body: formData, + type: "file", + }); + expect(result).toBe(formData); + } + }); + + it("should stringify body if not FormData in Node environment", async () => { + if (RUNTIME.type === "node") { + const body = { key: "value" }; + const result = await getRequestBody({ + body, + type: "json", + }); + expect(result).toBe('{"key":"value"}'); + } + }); + + it("should return FormData in browser environment", async () => { + if (RUNTIME.type === "browser") { + const formData = new (await import("form-data")).default(); + formData.append("key", "value"); + const result = await getRequestBody({ + body: formData, + type: "file", + }); + expect(result).toBe(formData); + } + }); + + it("should stringify body if not FormData in browser environment", async () => { + if (RUNTIME.type === "browser") { + const body = { key: "value" }; + const result = await getRequestBody({ + body, + type: "json", + }); + expect(result).toBe('{"key":"value"}'); + } + }); + + it("should return the Uint8Array", async () => { + const input = new Uint8Array([1, 2, 3]); + const result = await getRequestBody({ + body: input, + type: "bytes", + }); + expect(result).toBe(input); + }); + + it("should return the input for content-type 'application/x-www-form-urlencoded'", async () => { + const input = "key=value&another=param"; + const result = await getRequestBody({ + body: input, + type: "other", + }); + expect(result).toBe(input); + }); + + it("should JSON stringify objects", async () => { + const input = { key: "value" }; + const result = await getRequestBody({ + body: input, + type: "json", + }); + expect(result).toBe('{"key":"value"}'); + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/fetcher/getResponseBody.test.ts b/seed/ts-sdk/license/tests/unit/fetcher/getResponseBody.test.ts new file mode 100644 index 00000000000..1030c517ced --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/fetcher/getResponseBody.test.ts @@ -0,0 +1,64 @@ +import { RUNTIME } from "../../../src/core/runtime"; +import { getResponseBody } from "../../../src/core/fetcher/getResponseBody"; +import { chooseStreamWrapper } from "../../../src/core/fetcher/stream-wrappers/chooseStreamWrapper"; + +describe("Test getResponseBody", () => { + it("should handle blob response type", async () => { + const mockBlob = new Blob(["test"], { type: "text/plain" }); + const mockResponse = new Response(mockBlob); + const result = await getResponseBody(mockResponse, "blob"); + // @ts-expect-error + expect(result.constructor.name).toBe("Blob"); + }); + + it("should handle sse response type", async () => { + if (RUNTIME.type === "node") { + const mockStream = new ReadableStream(); + const mockResponse = new Response(mockStream); + const result = await getResponseBody(mockResponse, "sse"); + expect(result).toBe(mockStream); + } + }); + + it("should handle streaming response type", async () => { + if (RUNTIME.type === "node") { + const mockStream = new ReadableStream(); + const mockResponse = new Response(mockStream); + const result = await getResponseBody(mockResponse, "streaming"); + // need to reinstantiate string as a result of locked state in Readable Stream after registration with Response + expect(JSON.stringify(result)).toBe(JSON.stringify(await chooseStreamWrapper(new ReadableStream()))); + } + }); + + it("should handle text response type", async () => { + const mockResponse = new Response("test text"); + const result = await getResponseBody(mockResponse, "text"); + expect(result).toBe("test text"); + }); + + it("should handle JSON response", async () => { + const mockJson = { key: "value" }; + const mockResponse = new Response(JSON.stringify(mockJson)); + const result = await getResponseBody(mockResponse); + expect(result).toEqual(mockJson); + }); + + it("should handle empty response", async () => { + const mockResponse = new Response(""); + const result = await getResponseBody(mockResponse); + expect(result).toBeUndefined(); + }); + + it("should handle non-JSON response", async () => { + const mockResponse = new Response("invalid json"); + const result = await getResponseBody(mockResponse); + expect(result).toEqual({ + ok: false, + error: { + reason: "non-json", + statusCode: 200, + rawBody: "invalid json", + }, + }); + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/fetcher/makeRequest.test.ts b/seed/ts-sdk/license/tests/unit/fetcher/makeRequest.test.ts new file mode 100644 index 00000000000..be94ab45f10 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/fetcher/makeRequest.test.ts @@ -0,0 +1,54 @@ +import { RUNTIME } from "../../../src/core/runtime"; +import { makeRequest } from "../../../src/core/fetcher/makeRequest"; + +describe("Test makeRequest", () => { + const mockPostUrl = "https://httpbin.org/post"; + const mockGetUrl = "https://httpbin.org/get"; + const mockHeaders = { "Content-Type": "application/json" }; + const mockBody = JSON.stringify({ key: "value" }); + + let mockFetch: jest.Mock; + + beforeEach(() => { + mockFetch = jest.fn(); + mockFetch.mockResolvedValue(new Response(JSON.stringify({ test: "successful" }), { status: 200 })); + }); + + it("should handle POST request correctly", async () => { + const response = await makeRequest(mockFetch, mockPostUrl, "POST", mockHeaders, mockBody); + const responseBody = await response.json(); + expect(responseBody).toEqual({ test: "successful" }); + expect(mockFetch).toHaveBeenCalledTimes(1); + const [calledUrl, calledOptions] = mockFetch.mock.calls[0]; + expect(calledUrl).toBe(mockPostUrl); + expect(calledOptions).toEqual( + expect.objectContaining({ + method: "POST", + headers: mockHeaders, + body: mockBody, + credentials: undefined, + }) + ); + expect(calledOptions.signal).toBeDefined(); + expect(calledOptions.signal).toBeInstanceOf(AbortSignal); + }); + + it("should handle GET request correctly", async () => { + const response = await makeRequest(mockFetch, mockGetUrl, "GET", mockHeaders, undefined); + const responseBody = await response.json(); + expect(responseBody).toEqual({ test: "successful" }); + expect(mockFetch).toHaveBeenCalledTimes(1); + const [calledUrl, calledOptions] = mockFetch.mock.calls[0]; + expect(calledUrl).toBe(mockGetUrl); + expect(calledOptions).toEqual( + expect.objectContaining({ + method: "GET", + headers: mockHeaders, + body: undefined, + credentials: undefined, + }) + ); + expect(calledOptions.signal).toBeDefined(); + expect(calledOptions.signal).toBeInstanceOf(AbortSignal); + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/fetcher/requestWithRetries.test.ts b/seed/ts-sdk/license/tests/unit/fetcher/requestWithRetries.test.ts new file mode 100644 index 00000000000..6f77f093edf --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/fetcher/requestWithRetries.test.ts @@ -0,0 +1,133 @@ +import { RUNTIME } from "../../../src/core/runtime"; +import { requestWithRetries } from "../../../src/core/fetcher/requestWithRetries"; + +describe("requestWithRetries", () => { + let mockFetch: jest.Mock; + let originalMathRandom: typeof Math.random; + let setTimeoutSpy: jest.SpyInstance; + + beforeEach(() => { + mockFetch = jest.fn(); + originalMathRandom = Math.random; + + // Mock Math.random for consistent jitter + Math.random = jest.fn(() => 0.5); + + jest.useFakeTimers({ doNotFake: ["nextTick"] }); + }); + + afterEach(() => { + Math.random = originalMathRandom; + jest.clearAllMocks(); + jest.clearAllTimers(); + }); + + it("should retry on retryable status codes", async () => { + setTimeoutSpy = jest.spyOn(global, "setTimeout").mockImplementation((callback) => { + process.nextTick(callback); + return null as any; + }); + + const retryableStatuses = [408, 409, 429, 500, 502]; + let callCount = 0; + + mockFetch.mockImplementation(async () => { + if (callCount < retryableStatuses.length) { + return new Response("", { status: retryableStatuses[callCount++] }); + } + return new Response("", { status: 200 }); + }); + + const responsePromise = requestWithRetries(() => mockFetch(), retryableStatuses.length); + await jest.runAllTimersAsync(); + const response = await responsePromise; + + expect(mockFetch).toHaveBeenCalledTimes(retryableStatuses.length + 1); + expect(response.status).toBe(200); + }); + + it("should respect maxRetries limit", async () => { + setTimeoutSpy = jest.spyOn(global, "setTimeout").mockImplementation((callback) => { + process.nextTick(callback); + return null as any; + }); + + const maxRetries = 2; + mockFetch.mockResolvedValue(new Response("", { status: 500 })); + + const responsePromise = requestWithRetries(() => mockFetch(), maxRetries); + await jest.runAllTimersAsync(); + const response = await responsePromise; + + expect(mockFetch).toHaveBeenCalledTimes(maxRetries + 1); + expect(response.status).toBe(500); + }); + + it("should not retry on success status codes", async () => { + setTimeoutSpy = jest.spyOn(global, "setTimeout").mockImplementation((callback) => { + process.nextTick(callback); + return null as any; + }); + + const successStatuses = [200, 201, 202]; + + for (const status of successStatuses) { + mockFetch.mockReset(); + setTimeoutSpy.mockClear(); + mockFetch.mockResolvedValueOnce(new Response("", { status })); + + const responsePromise = requestWithRetries(() => mockFetch(), 3); + await jest.runAllTimersAsync(); + await responsePromise; + + expect(mockFetch).toHaveBeenCalledTimes(1); + expect(setTimeoutSpy).not.toHaveBeenCalled(); + } + }); + + it("should apply correct exponential backoff with jitter", async () => { + setTimeoutSpy = jest.spyOn(global, "setTimeout").mockImplementation((callback) => { + process.nextTick(callback); + return null as any; + }); + + mockFetch.mockResolvedValue(new Response("", { status: 500 })); + const maxRetries = 3; + const expectedDelays = [1000, 2000, 4000]; + + const responsePromise = requestWithRetries(() => mockFetch(), maxRetries); + await jest.runAllTimersAsync(); + await responsePromise; + + // Verify setTimeout calls + expect(setTimeoutSpy).toHaveBeenCalledTimes(expectedDelays.length); + + expectedDelays.forEach((delay, index) => { + expect(setTimeoutSpy).toHaveBeenNthCalledWith(index + 1, expect.any(Function), delay); + }); + + expect(mockFetch).toHaveBeenCalledTimes(maxRetries + 1); + }); + + it("should handle concurrent retries independently", async () => { + setTimeoutSpy = jest.spyOn(global, "setTimeout").mockImplementation((callback) => { + process.nextTick(callback); + return null as any; + }); + + mockFetch + .mockResolvedValueOnce(new Response("", { status: 500 })) + .mockResolvedValueOnce(new Response("", { status: 500 })) + .mockResolvedValueOnce(new Response("", { status: 200 })) + .mockResolvedValueOnce(new Response("", { status: 200 })); + + const promise1 = requestWithRetries(() => mockFetch(), 1); + const promise2 = requestWithRetries(() => mockFetch(), 1); + + await jest.runAllTimersAsync(); + const [response1, response2] = await Promise.all([promise1, promise2]); + + expect(response1.status).toBe(200); + expect(response2.status).toBe(200); + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/fetcher/signals.test.ts b/seed/ts-sdk/license/tests/unit/fetcher/signals.test.ts new file mode 100644 index 00000000000..9cabfa07447 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/fetcher/signals.test.ts @@ -0,0 +1,69 @@ +import { anySignal, getTimeoutSignal } from "../../../src/core/fetcher/signals"; + +describe("Test getTimeoutSignal", () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it("should return an object with signal and abortId", () => { + const { signal, abortId } = getTimeoutSignal(1000); + + expect(signal).toBeDefined(); + expect(abortId).toBeDefined(); + expect(signal).toBeInstanceOf(AbortSignal); + expect(signal.aborted).toBe(false); + }); + + it("should create a signal that aborts after the specified timeout", () => { + const timeoutMs = 5000; + const { signal } = getTimeoutSignal(timeoutMs); + + expect(signal.aborted).toBe(false); + + jest.advanceTimersByTime(timeoutMs - 1); + expect(signal.aborted).toBe(false); + + jest.advanceTimersByTime(1); + expect(signal.aborted).toBe(true); + }); +}); + +describe("Test anySignal", () => { + it("should return an AbortSignal", () => { + const signal = anySignal(new AbortController().signal); + expect(signal).toBeInstanceOf(AbortSignal); + }); + + it("should abort when any of the input signals is aborted", () => { + const controller1 = new AbortController(); + const controller2 = new AbortController(); + const signal = anySignal(controller1.signal, controller2.signal); + + expect(signal.aborted).toBe(false); + controller1.abort(); + expect(signal.aborted).toBe(true); + }); + + it("should handle an array of signals", () => { + const controller1 = new AbortController(); + const controller2 = new AbortController(); + const signal = anySignal([controller1.signal, controller2.signal]); + + expect(signal.aborted).toBe(false); + controller2.abort(); + expect(signal.aborted).toBe(true); + }); + + it("should abort immediately if one of the input signals is already aborted", () => { + const controller1 = new AbortController(); + const controller2 = new AbortController(); + controller1.abort(); + + const signal = anySignal(controller1.signal, controller2.signal); + expect(signal.aborted).toBe(true); + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/fetcher/stream-wrappers/Node18UniversalStreamWrapper.test.ts b/seed/ts-sdk/license/tests/unit/fetcher/stream-wrappers/Node18UniversalStreamWrapper.test.ts new file mode 100644 index 00000000000..1dc9be0cc0e --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/fetcher/stream-wrappers/Node18UniversalStreamWrapper.test.ts @@ -0,0 +1,178 @@ +import { Node18UniversalStreamWrapper } from "../../../../src/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper"; + +describe("Node18UniversalStreamWrapper", () => { + it("should set encoding to utf-8", async () => { + const rawStream = new ReadableStream(); + const stream = new Node18UniversalStreamWrapper(rawStream); + const setEncodingSpy = jest.spyOn(stream, "setEncoding"); + + stream.setEncoding("utf-8"); + + expect(setEncodingSpy).toHaveBeenCalledWith("utf-8"); + }); + + it("should register an event listener for readable", async () => { + const rawStream = new ReadableStream(); + const stream = new Node18UniversalStreamWrapper(rawStream); + const onSpy = jest.spyOn(stream, "on"); + + stream.on("readable", () => {}); + + expect(onSpy).toHaveBeenCalledWith("readable", expect.any(Function)); + }); + + it("should remove an event listener for data", async () => { + const rawStream = new ReadableStream(); + const stream = new Node18UniversalStreamWrapper(rawStream); + const offSpy = jest.spyOn(stream, "off"); + + const fn = () => {}; + stream.on("data", fn); + stream.off("data", fn); + + expect(offSpy).toHaveBeenCalledWith("data", expect.any(Function)); + }); + + it("should write to dest when calling pipe to writable stream", async () => { + const rawStream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("test")); + controller.enqueue(new TextEncoder().encode("test")); + controller.close(); + }, + }); + const stream = new Node18UniversalStreamWrapper(rawStream); + const dest = new WritableStream({ + write(chunk) { + expect(chunk).toEqual(new TextEncoder().encode("test")); + }, + }); + + stream.pipe(dest); + }); + + it("should write to dest when calling pipe to node writable stream", async () => { + const rawStream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("test")); + controller.enqueue(new TextEncoder().encode("test")); + controller.close(); + }, + }); + const stream = new Node18UniversalStreamWrapper(rawStream); + const dest = new (await import("readable-stream")).Writable({ + write(chunk, encoding, callback) { + expect(chunk.toString()).toEqual("test"); + callback(); + }, + }); + + stream.pipe(dest); + }); + + it("should write nothing when calling pipe and unpipe", async () => { + const rawStream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("test")); + controller.enqueue(new TextEncoder().encode("test")); + controller.close(); + }, + }); + const stream = new Node18UniversalStreamWrapper(rawStream); + const buffer: Uint8Array[] = []; + const dest = new WritableStream({ + write(chunk) { + buffer.push(chunk); + }, + }); + + stream.pipe(dest); + stream.unpipe(dest); + expect(buffer).toEqual([]); + }); + + it("should destroy the stream", async () => { + const rawStream = new ReadableStream(); + const stream = new Node18UniversalStreamWrapper(rawStream); + const destroySpy = jest.spyOn(stream, "destroy"); + + stream.destroy(); + + expect(destroySpy).toHaveBeenCalled(); + }); + + it("should pause and resume the stream", async () => { + const rawStream = new ReadableStream(); + const stream = new Node18UniversalStreamWrapper(rawStream); + const pauseSpy = jest.spyOn(stream, "pause"); + const resumeSpy = jest.spyOn(stream, "resume"); + + expect(stream.isPaused).toBe(false); + stream.pause(); + expect(stream.isPaused).toBe(true); + stream.resume(); + + expect(pauseSpy).toHaveBeenCalled(); + expect(resumeSpy).toHaveBeenCalled(); + }); + + it("should read the stream", async () => { + const rawStream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("test")); + controller.enqueue(new TextEncoder().encode("test")); + controller.close(); + }, + }); + const stream = new Node18UniversalStreamWrapper(rawStream); + + expect(await stream.read()).toEqual(new TextEncoder().encode("test")); + expect(await stream.read()).toEqual(new TextEncoder().encode("test")); + }); + + it("should read the stream as text", async () => { + const rawStream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("test")); + controller.enqueue(new TextEncoder().encode("test")); + controller.close(); + }, + }); + const stream = new Node18UniversalStreamWrapper(rawStream); + + const data = await stream.text(); + + expect(data).toEqual("testtest"); + }); + + it("should read the stream as json", async () => { + const rawStream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode(JSON.stringify({ test: "test" }))); + controller.close(); + }, + }); + const stream = new Node18UniversalStreamWrapper(rawStream); + + const data = await stream.json(); + + expect(data).toEqual({ test: "test" }); + }); + + it("should allow use with async iteratable stream", async () => { + const rawStream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("test")); + controller.enqueue(new TextEncoder().encode("test")); + controller.close(); + }, + }); + let data = ""; + const stream = new Node18UniversalStreamWrapper(rawStream); + for await (const chunk of stream) { + data += new TextDecoder().decode(chunk); + } + + expect(data).toEqual("testtest"); + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/fetcher/stream-wrappers/NodePre18StreamWrapper.test.ts b/seed/ts-sdk/license/tests/unit/fetcher/stream-wrappers/NodePre18StreamWrapper.test.ts new file mode 100644 index 00000000000..0c99d3b2655 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/fetcher/stream-wrappers/NodePre18StreamWrapper.test.ts @@ -0,0 +1,124 @@ +import { NodePre18StreamWrapper } from "../../../../src/core/fetcher/stream-wrappers/NodePre18StreamWrapper"; + +describe("NodePre18StreamWrapper", () => { + it("should set encoding to utf-8", async () => { + const rawStream = (await import("readable-stream")).Readable.from(["test", "test"]); + const stream = new NodePre18StreamWrapper(rawStream); + const setEncodingSpy = jest.spyOn(stream, "setEncoding"); + + stream.setEncoding("utf-8"); + + expect(setEncodingSpy).toHaveBeenCalledWith("utf-8"); + }); + + it("should register an event listener for readable", async () => { + const rawStream = (await import("readable-stream")).Readable.from(["test", "test"]); + const stream = new NodePre18StreamWrapper(rawStream); + const onSpy = jest.spyOn(stream, "on"); + + stream.on("readable", () => {}); + + expect(onSpy).toHaveBeenCalledWith("readable", expect.any(Function)); + }); + + it("should remove an event listener for data", async () => { + const rawStream = (await import("readable-stream")).Readable.from(["test", "test"]); + const stream = new NodePre18StreamWrapper(rawStream); + const offSpy = jest.spyOn(stream, "off"); + + const fn = () => {}; + stream.on("data", fn); + stream.off("data", fn); + + expect(offSpy).toHaveBeenCalledWith("data", expect.any(Function)); + }); + + it("should write to dest when calling pipe to node writable stream", async () => { + const rawStream = (await import("readable-stream")).Readable.from(["test", "test"]); + const stream = new NodePre18StreamWrapper(rawStream); + const dest = new (await import("readable-stream")).Writable({ + write(chunk, encoding, callback) { + expect(chunk.toString()).toEqual("test"); + callback(); + }, + }); + + stream.pipe(dest); + }); + + it("should write nothing when calling pipe and unpipe", async () => { + const rawStream = (await import("readable-stream")).Readable.from(["test", "test"]); + const stream = new NodePre18StreamWrapper(rawStream); + const buffer: Uint8Array[] = []; + const dest = new (await import("readable-stream")).Writable({ + write(chunk, encoding, callback) { + buffer.push(chunk); + callback(); + }, + }); + stream.pipe(dest); + stream.unpipe(); + + expect(buffer).toEqual([]); + }); + + it("should destroy the stream", async () => { + const rawStream = (await import("readable-stream")).Readable.from(["test", "test"]); + const stream = new NodePre18StreamWrapper(rawStream); + const destroySpy = jest.spyOn(stream, "destroy"); + + stream.destroy(); + + expect(destroySpy).toHaveBeenCalledWith(); + }); + + it("should pause the stream and resume", async () => { + const rawStream = (await import("readable-stream")).Readable.from(["test", "test"]); + const stream = new NodePre18StreamWrapper(rawStream); + const pauseSpy = jest.spyOn(stream, "pause"); + + stream.pause(); + expect(stream.isPaused).toBe(true); + stream.resume(); + expect(stream.isPaused).toBe(false); + + expect(pauseSpy).toHaveBeenCalledWith(); + }); + + it("should read the stream", async () => { + const rawStream = (await import("readable-stream")).Readable.from(["test", "test"]); + const stream = new NodePre18StreamWrapper(rawStream); + + expect(await stream.read()).toEqual("test"); + expect(await stream.read()).toEqual("test"); + }); + + it("should read the stream as text", async () => { + const rawStream = (await import("readable-stream")).Readable.from(["test", "test"]); + const stream = new NodePre18StreamWrapper(rawStream); + + const data = await stream.text(); + + expect(data).toEqual("testtest"); + }); + + it("should read the stream as json", async () => { + const rawStream = (await import("readable-stream")).Readable.from([JSON.stringify({ test: "test" })]); + const stream = new NodePre18StreamWrapper(rawStream); + + const data = await stream.json(); + + expect(data).toEqual({ test: "test" }); + }); + + it("should allow use with async iteratable stream", async () => { + const rawStream = (await import("readable-stream")).Readable.from(["test", "test"]); + let data = ""; + const stream = new NodePre18StreamWrapper(rawStream); + for await (const chunk of stream) { + data += chunk; + } + + expect(data).toEqual("testtest"); + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/fetcher/stream-wrappers/UndiciStreamWrapper.test.ts b/seed/ts-sdk/license/tests/unit/fetcher/stream-wrappers/UndiciStreamWrapper.test.ts new file mode 100644 index 00000000000..1d171ce6c67 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/fetcher/stream-wrappers/UndiciStreamWrapper.test.ts @@ -0,0 +1,153 @@ +import { UndiciStreamWrapper } from "../../../../src/core/fetcher/stream-wrappers/UndiciStreamWrapper"; + +describe("UndiciStreamWrapper", () => { + it("should set encoding to utf-8", async () => { + const rawStream = new ReadableStream(); + const stream = new UndiciStreamWrapper(rawStream); + const setEncodingSpy = jest.spyOn(stream, "setEncoding"); + + stream.setEncoding("utf-8"); + + expect(setEncodingSpy).toHaveBeenCalledWith("utf-8"); + }); + + it("should register an event listener for readable", async () => { + const rawStream = new ReadableStream(); + const stream = new UndiciStreamWrapper(rawStream); + const onSpy = jest.spyOn(stream, "on"); + + stream.on("readable", () => {}); + + expect(onSpy).toHaveBeenCalledWith("readable", expect.any(Function)); + }); + + it("should remove an event listener for data", async () => { + const rawStream = new ReadableStream(); + const stream = new UndiciStreamWrapper(rawStream); + const offSpy = jest.spyOn(stream, "off"); + + const fn = () => {}; + stream.on("data", fn); + stream.off("data", fn); + + expect(offSpy).toHaveBeenCalledWith("data", expect.any(Function)); + }); + + it("should write to dest when calling pipe to writable stream", async () => { + const rawStream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("test")); + controller.enqueue(new TextEncoder().encode("test")); + controller.close(); + }, + }); + const stream = new UndiciStreamWrapper(rawStream); + const dest = new WritableStream({ + write(chunk) { + expect(chunk).toEqual(new TextEncoder().encode("test")); + }, + }); + + stream.pipe(dest); + }); + + it("should write nothing when calling pipe and unpipe", async () => { + const rawStream = new ReadableStream(); + const stream = new UndiciStreamWrapper(rawStream); + const buffer: Uint8Array[] = []; + const dest = new WritableStream({ + write(chunk) { + buffer.push(chunk); + }, + }); + stream.pipe(dest); + stream.unpipe(dest); + + expect(buffer).toEqual([]); + }); + + it("should destroy the stream", async () => { + const rawStream = new ReadableStream(); + const stream = new UndiciStreamWrapper(rawStream); + const destroySpy = jest.spyOn(stream, "destroy"); + + stream.destroy(); + + expect(destroySpy).toHaveBeenCalled(); + }); + + it("should pause and resume the stream", async () => { + const rawStream = new ReadableStream(); + const stream = new UndiciStreamWrapper(rawStream); + const pauseSpy = jest.spyOn(stream, "pause"); + const resumeSpy = jest.spyOn(stream, "resume"); + + expect(stream.isPaused).toBe(false); + stream.pause(); + expect(stream.isPaused).toBe(true); + stream.resume(); + + expect(pauseSpy).toHaveBeenCalled(); + expect(resumeSpy).toHaveBeenCalled(); + }); + + it("should read the stream", async () => { + const rawStream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("test")); + controller.enqueue(new TextEncoder().encode("test")); + controller.close(); + }, + }); + const stream = new UndiciStreamWrapper(rawStream); + + expect(await stream.read()).toEqual(new TextEncoder().encode("test")); + expect(await stream.read()).toEqual(new TextEncoder().encode("test")); + }); + + it("should read the stream as text", async () => { + const rawStream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("test")); + controller.enqueue(new TextEncoder().encode("test")); + controller.close(); + }, + }); + const stream = new UndiciStreamWrapper(rawStream); + + const data = await stream.text(); + + expect(data).toEqual("testtest"); + }); + + it("should read the stream as json", async () => { + const rawStream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode(JSON.stringify({ test: "test" }))); + controller.close(); + }, + }); + const stream = new UndiciStreamWrapper(rawStream); + + const data = await stream.json(); + + expect(data).toEqual({ test: "test" }); + }); + + it("should allow use with async iteratable stream", async () => { + const rawStream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("test")); + controller.enqueue(new TextEncoder().encode("test")); + controller.close(); + }, + }); + let data = ""; + const stream = new UndiciStreamWrapper(rawStream); + for await (const chunk of stream) { + data += new TextDecoder().decode(chunk); + } + + expect(data).toEqual("testtest"); + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/fetcher/stream-wrappers/chooseStreamWrapper.test.ts b/seed/ts-sdk/license/tests/unit/fetcher/stream-wrappers/chooseStreamWrapper.test.ts new file mode 100644 index 00000000000..17cf37a2f7f --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/fetcher/stream-wrappers/chooseStreamWrapper.test.ts @@ -0,0 +1,43 @@ +import { RUNTIME } from "../../../../src/core/runtime"; +import { chooseStreamWrapper } from "../../../../src/core/fetcher/stream-wrappers/chooseStreamWrapper"; +import { Node18UniversalStreamWrapper } from "../../../../src/core/fetcher/stream-wrappers/Node18UniversalStreamWrapper"; +import { NodePre18StreamWrapper } from "../../../../src/core/fetcher/stream-wrappers/NodePre18StreamWrapper"; +import { UndiciStreamWrapper } from "../../../../src/core/fetcher/stream-wrappers/UndiciStreamWrapper"; + +describe("chooseStreamWrapper", () => { + beforeEach(() => { + RUNTIME.type = "unknown"; + RUNTIME.parsedVersion = 0; + }); + + it('should return a Node18UniversalStreamWrapper when RUNTIME.type is "node" and RUNTIME.parsedVersion is not null and RUNTIME.parsedVersion is greater than or equal to 18', async () => { + const expected = new Node18UniversalStreamWrapper(new ReadableStream()); + RUNTIME.type = "node"; + RUNTIME.parsedVersion = 18; + + const result = await chooseStreamWrapper(new ReadableStream()); + + expect(JSON.stringify(result)).toBe(JSON.stringify(expected)); + }); + + it('should return a NodePre18StreamWrapper when RUNTIME.type is "node" and RUNTIME.parsedVersion is not null and RUNTIME.parsedVersion is less than 18', async () => { + const stream = await import("readable-stream"); + const expected = new NodePre18StreamWrapper(new stream.Readable()); + + RUNTIME.type = "node"; + RUNTIME.parsedVersion = 16; + + const result = await chooseStreamWrapper(new stream.Readable()); + + expect(JSON.stringify(result)).toEqual(JSON.stringify(expected)); + }); + + it('should return a Undici when RUNTIME.type is not "node"', async () => { + const expected = new UndiciStreamWrapper(new ReadableStream()); + RUNTIME.type = "browser"; + + const result = await chooseStreamWrapper(new ReadableStream()); + + expect(JSON.stringify(result)).toEqual(JSON.stringify(expected)); + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/fetcher/stream-wrappers/webpack.test.ts b/seed/ts-sdk/license/tests/unit/fetcher/stream-wrappers/webpack.test.ts new file mode 100644 index 00000000000..557db6dc4ef --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/fetcher/stream-wrappers/webpack.test.ts @@ -0,0 +1,38 @@ +import webpack from "webpack"; + +describe("test env compatibility", () => { + test("webpack", () => { + return new Promise((resolve, reject) => { + webpack( + { + mode: "production", + entry: "./src/index.ts", + module: { + rules: [ + { + test: /\.tsx?$/, + use: "ts-loader", + exclude: /node_modules/, + }, + ], + }, + resolve: { + extensions: [".tsx", ".ts", ".js"], + }, + }, + (err, stats) => { + try { + expect(err).toBe(null); + if (stats?.hasErrors()) { + console.log(stats?.toString()); + } + expect(stats?.hasErrors()).toBe(false); + resolve(); + } catch (error) { + reject(error); + } + } + ); + }); + }, 60_000); +}); diff --git a/seed/ts-sdk/license/tests/unit/fetcher/test-file.txt b/seed/ts-sdk/license/tests/unit/fetcher/test-file.txt new file mode 100644 index 00000000000..c66d471e359 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/fetcher/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/license/tests/unit/zurg/bigint/bigint.test.ts b/seed/ts-sdk/license/tests/unit/zurg/bigint/bigint.test.ts new file mode 100644 index 00000000000..cf9935a749a --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/bigint/bigint.test.ts @@ -0,0 +1,24 @@ +import { bigint } from "../../../../src/core/schemas/builders/bigint"; +import { itSchema } from "../utils/itSchema"; +import { itValidateJson, itValidateParse } from "../utils/itValidate"; + +describe("bigint", () => { + itSchema("converts between raw string and parsed bigint", bigint(), { + raw: "123456789012345678901234567890123456789012345678901234567890", + parsed: BigInt("123456789012345678901234567890123456789012345678901234567890"), + }); + + itValidateParse("non-string", bigint(), 42, [ + { + message: "Expected string. Received 42.", + path: [], + }, + ]); + + itValidateJson("non-bigint", bigint(), "hello", [ + { + message: 'Expected bigint. Received "hello".', + path: [], + }, + ]); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/date/date.test.ts b/seed/ts-sdk/license/tests/unit/zurg/date/date.test.ts new file mode 100644 index 00000000000..2790268a09c --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/date/date.test.ts @@ -0,0 +1,31 @@ +import { date } from "../../../../src/core/schemas/builders/date"; +import { itSchema } from "../utils/itSchema"; +import { itValidateJson, itValidateParse } from "../utils/itValidate"; + +describe("date", () => { + itSchema("converts between raw ISO string and parsed Date", date(), { + raw: "2022-09-29T05:41:21.939Z", + parsed: new Date("2022-09-29T05:41:21.939Z"), + }); + + itValidateParse("non-string", date(), 42, [ + { + message: "Expected string. Received 42.", + path: [], + }, + ]); + + itValidateParse("non-ISO", date(), "hello world", [ + { + message: 'Expected ISO 8601 date string. Received "hello world".', + path: [], + }, + ]); + + itValidateJson("non-Date", date(), "hello", [ + { + message: 'Expected Date object. Received "hello".', + path: [], + }, + ]); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/enum/enum.test.ts b/seed/ts-sdk/license/tests/unit/zurg/enum/enum.test.ts new file mode 100644 index 00000000000..ab0df0285cd --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/enum/enum.test.ts @@ -0,0 +1,30 @@ +import { enum_ } from "../../../../src/core/schemas/builders/enum"; +import { itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("enum", () => { + itSchemaIdentity(enum_(["A", "B", "C"]), "A"); + + itSchemaIdentity(enum_(["A", "B", "C"]), "D" as any, { + opts: { allowUnrecognizedEnumValues: true }, + }); + + itValidate("invalid enum", enum_(["A", "B", "C"]), "D", [ + { + message: 'Expected enum. Received "D".', + path: [], + }, + ]); + + itValidate( + "non-string", + enum_(["A", "B", "C"]), + [], + [ + { + message: "Expected string. Received list.", + path: [], + }, + ] + ); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/lazy/lazy.test.ts b/seed/ts-sdk/license/tests/unit/zurg/lazy/lazy.test.ts new file mode 100644 index 00000000000..6906bf4cf91 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/lazy/lazy.test.ts @@ -0,0 +1,57 @@ +import { Schema } from "../../../../src/core/schemas/Schema"; +import { lazy, list, object, string } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; + +describe("lazy", () => { + it("doesn't run immediately", () => { + let wasRun = false; + lazy(() => { + wasRun = true; + return string(); + }); + expect(wasRun).toBe(false); + }); + + it("only runs first time", async () => { + let count = 0; + const schema = lazy(() => { + count++; + return string(); + }); + await schema.parse("hello"); + await schema.json("world"); + expect(count).toBe(1); + }); + + itSchemaIdentity( + lazy(() => object({})), + { foo: "hello" }, + { + title: "passes opts through", + opts: { unrecognizedObjectKeys: "passthrough" }, + } + ); + + itSchemaIdentity( + lazy(() => object({ foo: string() })), + { foo: "hello" } + ); + + // eslint-disable-next-line jest/expect-expect + it("self-referencial schema doesn't compile", () => { + () => { + // @ts-expect-error + const a = lazy(() => object({ foo: a })); + }; + }); + + // eslint-disable-next-line jest/expect-expect + it("self-referencial compiles with explicit type", () => { + () => { + interface TreeNode { + children: TreeNode[]; + } + const TreeNode: Schema = lazy(() => object({ children: list(TreeNode) })); + }; + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/lazy/lazyObject.test.ts b/seed/ts-sdk/license/tests/unit/zurg/lazy/lazyObject.test.ts new file mode 100644 index 00000000000..8813cc9fbb4 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/lazy/lazyObject.test.ts @@ -0,0 +1,18 @@ +import { lazyObject, number, object, string } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; + +describe("lazy", () => { + itSchemaIdentity( + lazyObject(() => object({ foo: string() })), + { foo: "hello" } + ); + + itSchemaIdentity( + lazyObject(() => object({ foo: string() })).extend(object({ bar: number() })), + { + foo: "hello", + bar: 42, + }, + { title: "returned schema has object utils" } + ); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/lazy/recursive/a.ts b/seed/ts-sdk/license/tests/unit/zurg/lazy/recursive/a.ts new file mode 100644 index 00000000000..8b7d5e40cfa --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/lazy/recursive/a.ts @@ -0,0 +1,7 @@ +import { object } from "../../../../../src/core/schemas/builders/object"; +import { schemaB } from "./b"; + +// @ts-expect-error +export const schemaA = object({ + b: schemaB, +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/lazy/recursive/b.ts b/seed/ts-sdk/license/tests/unit/zurg/lazy/recursive/b.ts new file mode 100644 index 00000000000..fb219d54c8e --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/lazy/recursive/b.ts @@ -0,0 +1,8 @@ +import { object } from "../../../../../src/core/schemas/builders/object"; +import { optional } from "../../../../../src/core/schemas/builders/schema-utils"; +import { schemaA } from "./a"; + +// @ts-expect-error +export const schemaB = object({ + a: optional(schemaA), +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/list/list.test.ts b/seed/ts-sdk/license/tests/unit/zurg/list/list.test.ts new file mode 100644 index 00000000000..424ed642db2 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/list/list.test.ts @@ -0,0 +1,41 @@ +import { list, object, property, string } from "../../../../src/core/schemas/builders"; +import { itSchema, itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("list", () => { + itSchemaIdentity(list(string()), ["hello", "world"], { + title: "functions as identity when item type is primitive", + }); + + itSchema( + "converts objects correctly", + list( + object({ + helloWorld: property("hello_world", string()), + }) + ), + { + raw: [{ hello_world: "123" }], + parsed: [{ helloWorld: "123" }], + } + ); + + itValidate("not a list", list(string()), 42, [ + { + path: [], + message: "Expected list. Received 42.", + }, + ]); + + itValidate( + "invalid item type", + list(string()), + [42], + [ + { + path: ["[0]"], + message: "Expected string. Received 42.", + }, + ] + ); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/literals/stringLiteral.test.ts b/seed/ts-sdk/license/tests/unit/zurg/literals/stringLiteral.test.ts new file mode 100644 index 00000000000..fa6c88873c6 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/literals/stringLiteral.test.ts @@ -0,0 +1,21 @@ +import { stringLiteral } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("stringLiteral", () => { + itSchemaIdentity(stringLiteral("A"), "A"); + + itValidate("incorrect string", stringLiteral("A"), "B", [ + { + path: [], + message: 'Expected "A". Received "B".', + }, + ]); + + itValidate("non-string", stringLiteral("A"), 42, [ + { + path: [], + message: 'Expected "A". Received 42.', + }, + ]); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/object-like/withParsedProperties.test.ts b/seed/ts-sdk/license/tests/unit/zurg/object-like/withParsedProperties.test.ts new file mode 100644 index 00000000000..9f5dd0ed39b --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/object-like/withParsedProperties.test.ts @@ -0,0 +1,57 @@ +import { object, property, string, stringLiteral } from "../../../../src/core/schemas/builders"; + +describe("withParsedProperties", () => { + it("Added properties included on parsed object", async () => { + const schema = object({ + foo: property("raw_foo", string()), + bar: stringLiteral("bar"), + }).withParsedProperties({ + printFoo: (parsed) => () => parsed.foo, + printHelloWorld: () => () => "Hello world", + helloWorld: "Hello world", + }); + + const parsed = await schema.parse({ raw_foo: "value of foo", bar: "bar" }); + if (!parsed.ok) { + throw new Error("Failed to parse"); + } + expect(parsed.value.printFoo()).toBe("value of foo"); + expect(parsed.value.printHelloWorld()).toBe("Hello world"); + expect(parsed.value.helloWorld).toBe("Hello world"); + }); + + it("Added property is removed on raw object", async () => { + const schema = object({ + foo: property("raw_foo", string()), + bar: stringLiteral("bar"), + }).withParsedProperties({ + printFoo: (parsed) => () => parsed.foo, + }); + + const original = { raw_foo: "value of foo", bar: "bar" } as const; + const parsed = await schema.parse(original); + if (!parsed.ok) { + throw new Error("Failed to parse()"); + } + + const raw = await schema.json(parsed.value); + + if (!raw.ok) { + throw new Error("Failed to json()"); + } + + expect(raw.value).toEqual(original); + }); + + describe("compile", () => { + // eslint-disable-next-line jest/expect-expect + it("doesn't compile with non-object schema", () => { + () => + object({ + foo: string(), + }) + // @ts-expect-error + .withParsedProperties(42); + }); + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/object/extend.test.ts b/seed/ts-sdk/license/tests/unit/zurg/object/extend.test.ts new file mode 100644 index 00000000000..54fc8c4ebf8 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/object/extend.test.ts @@ -0,0 +1,89 @@ +import { boolean, object, property, string, stringLiteral } from "../../../../src/core/schemas/builders"; +import { itSchema, itSchemaIdentity } from "../utils/itSchema"; + +describe("extend", () => { + itSchemaIdentity( + object({ + foo: string(), + }).extend( + object({ + bar: stringLiteral("bar"), + }) + ), + { + foo: "", + bar: "bar", + } as const, + { + title: "extended properties are included in schema", + } + ); + + itSchemaIdentity( + object({ + foo: string(), + }) + .extend( + object({ + bar: stringLiteral("bar"), + }) + ) + .extend( + object({ + baz: boolean(), + }) + ), + { + foo: "", + bar: "bar", + baz: true, + } as const, + { + title: "extensions can be extended", + } + ); + + itSchema( + "converts nested object", + object({ + item: object({ + helloWorld: property("hello_world", string()), + }), + }).extend( + object({ + goodbye: property("goodbye_raw", string()), + }) + ), + { + raw: { item: { hello_world: "yo" }, goodbye_raw: "peace" }, + parsed: { item: { helloWorld: "yo" }, goodbye: "peace" }, + } + ); + + itSchema( + "extensions work with raw/parsed property name conversions", + object({ + item: property("item_raw", string()), + }).extend( + object({ + goodbye: property("goodbye_raw", string()), + }) + ), + { + raw: { item_raw: "hi", goodbye_raw: "peace" }, + parsed: { item: "hi", goodbye: "peace" }, + } + ); + + describe("compile", () => { + // eslint-disable-next-line jest/expect-expect + it("doesn't compile with non-object schema", () => { + () => + object({ + foo: string(), + }) + // @ts-expect-error + .extend([]); + }); + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/object/object.test.ts b/seed/ts-sdk/license/tests/unit/zurg/object/object.test.ts new file mode 100644 index 00000000000..0acf0e240f6 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/object/object.test.ts @@ -0,0 +1,255 @@ +import { any, number, object, property, string, stringLiteral, unknown } from "../../../../src/core/schemas/builders"; +import { itJson, itParse, itSchema, itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("object", () => { + itSchemaIdentity( + object({ + foo: string(), + bar: stringLiteral("bar"), + }), + { + foo: "", + bar: "bar", + }, + { + title: "functions as identity when values are primitives and property() isn't used", + } + ); + + itSchema( + "uses raw key from property()", + object({ + foo: property("raw_foo", string()), + bar: stringLiteral("bar"), + }), + { + raw: { raw_foo: "foo", bar: "bar" }, + parsed: { foo: "foo", bar: "bar" }, + } + ); + + itSchema( + "keys with unknown type can be omitted", + object({ + foo: unknown(), + }), + { + raw: {}, + parsed: {}, + } + ); + + itSchema( + "keys with any type can be omitted", + object({ + foo: any(), + }), + { + raw: {}, + parsed: {}, + } + ); + + describe("unrecognizedObjectKeys", () => { + describe("parse", () => { + itParse( + 'includes unknown values when unrecognizedObjectKeys === "passthrough"', + object({ + foo: property("raw_foo", string()), + bar: stringLiteral("bar"), + }), + { + raw: { + raw_foo: "foo", + bar: "bar", + // @ts-expect-error + baz: "yoyo", + }, + parsed: { + foo: "foo", + bar: "bar", + // @ts-expect-error + baz: "yoyo", + }, + opts: { + unrecognizedObjectKeys: "passthrough", + }, + } + ); + + itParse( + 'strips unknown values when unrecognizedObjectKeys === "strip"', + object({ + foo: property("raw_foo", string()), + bar: stringLiteral("bar"), + }), + { + raw: { + raw_foo: "foo", + bar: "bar", + // @ts-expect-error + baz: "yoyo", + }, + parsed: { + foo: "foo", + bar: "bar", + }, + opts: { + unrecognizedObjectKeys: "strip", + }, + } + ); + }); + + describe("json", () => { + itJson( + 'includes unknown values when unrecognizedObjectKeys === "passthrough"', + object({ + foo: property("raw_foo", string()), + bar: stringLiteral("bar"), + }), + { + raw: { + raw_foo: "foo", + bar: "bar", + // @ts-expect-error + baz: "yoyo", + }, + parsed: { + foo: "foo", + bar: "bar", + // @ts-expect-error + baz: "yoyo", + }, + opts: { + unrecognizedObjectKeys: "passthrough", + }, + } + ); + + itJson( + 'strips unknown values when unrecognizedObjectKeys === "strip"', + object({ + foo: property("raw_foo", string()), + bar: stringLiteral("bar"), + }), + { + raw: { + raw_foo: "foo", + bar: "bar", + }, + parsed: { + foo: "foo", + bar: "bar", + // @ts-expect-error + baz: "yoyo", + }, + opts: { + unrecognizedObjectKeys: "strip", + }, + } + ); + }); + }); + + describe("nullish properties", () => { + itSchema("missing properties are not added", object({ foo: property("raw_foo", string().optional()) }), { + raw: {}, + parsed: {}, + }); + + itSchema("undefined properties are not dropped", object({ foo: property("raw_foo", string().optional()) }), { + raw: { raw_foo: null }, + parsed: { foo: undefined }, + }); + + itSchema("null properties are not dropped", object({ foo: property("raw_foo", string().optional()) }), { + raw: { raw_foo: null }, + parsed: { foo: undefined }, + }); + + describe("extensions", () => { + itSchema( + "undefined properties are not dropped", + object({}).extend(object({ foo: property("raw_foo", string().optional()) })), + { + raw: { raw_foo: null }, + parsed: { foo: undefined }, + } + ); + + describe("parse()", () => { + itParse( + "null properties are not dropped", + object({}).extend(object({ foo: property("raw_foo", string().optional()) })), + { + raw: { raw_foo: null }, + parsed: { foo: undefined }, + } + ); + }); + }); + }); + + itValidate( + "missing property", + object({ + foo: string(), + bar: stringLiteral("bar"), + }), + { foo: "hello" }, + [ + { + path: [], + message: 'Missing required key "bar"', + }, + ] + ); + + itValidate( + "extra property", + object({ + foo: string(), + bar: stringLiteral("bar"), + }), + { foo: "hello", bar: "bar", baz: 42 }, + [ + { + path: ["baz"], + message: 'Unexpected key "baz"', + }, + ] + ); + + itValidate( + "not an object", + object({ + foo: string(), + bar: stringLiteral("bar"), + }), + [], + [ + { + path: [], + message: "Expected object. Received list.", + }, + ] + ); + + itValidate( + "nested validation error", + object({ + foo: object({ + bar: number(), + }), + }), + { foo: { bar: "hello" } }, + [ + { + path: ["foo", "bar"], + message: 'Expected number. Received "hello".', + }, + ] + ); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/object/objectWithoutOptionalProperties.test.ts b/seed/ts-sdk/license/tests/unit/zurg/object/objectWithoutOptionalProperties.test.ts new file mode 100644 index 00000000000..d87a65febfd --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/object/objectWithoutOptionalProperties.test.ts @@ -0,0 +1,21 @@ +import { objectWithoutOptionalProperties, string, stringLiteral } from "../../../../src/core/schemas/builders"; +import { itSchema } from "../utils/itSchema"; + +describe("objectWithoutOptionalProperties", () => { + itSchema( + "all properties are required", + objectWithoutOptionalProperties({ + foo: string(), + bar: stringLiteral("bar").optional(), + }), + { + raw: { + foo: "hello", + }, + // @ts-expect-error + parsed: { + foo: "hello", + }, + } + ); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/primitives/any.test.ts b/seed/ts-sdk/license/tests/unit/zurg/primitives/any.test.ts new file mode 100644 index 00000000000..1adbbe2a838 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/primitives/any.test.ts @@ -0,0 +1,6 @@ +import { any } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; + +describe("any", () => { + itSchemaIdentity(any(), true); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/primitives/boolean.test.ts b/seed/ts-sdk/license/tests/unit/zurg/primitives/boolean.test.ts new file mode 100644 index 00000000000..897a8295dca --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/primitives/boolean.test.ts @@ -0,0 +1,14 @@ +import { boolean } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("boolean", () => { + itSchemaIdentity(boolean(), true); + + itValidate("non-boolean", boolean(), {}, [ + { + path: [], + message: "Expected boolean. Received object.", + }, + ]); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/primitives/number.test.ts b/seed/ts-sdk/license/tests/unit/zurg/primitives/number.test.ts new file mode 100644 index 00000000000..2d01415a60b --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/primitives/number.test.ts @@ -0,0 +1,14 @@ +import { number } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("number", () => { + itSchemaIdentity(number(), 42); + + itValidate("non-number", number(), "hello", [ + { + path: [], + message: 'Expected number. Received "hello".', + }, + ]); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/primitives/string.test.ts b/seed/ts-sdk/license/tests/unit/zurg/primitives/string.test.ts new file mode 100644 index 00000000000..57b2368784a --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/primitives/string.test.ts @@ -0,0 +1,14 @@ +import { string } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("string", () => { + itSchemaIdentity(string(), "hello"); + + itValidate("non-string", string(), 42, [ + { + path: [], + message: "Expected string. Received 42.", + }, + ]); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/primitives/unknown.test.ts b/seed/ts-sdk/license/tests/unit/zurg/primitives/unknown.test.ts new file mode 100644 index 00000000000..4d17a7dbd00 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/primitives/unknown.test.ts @@ -0,0 +1,6 @@ +import { unknown } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; + +describe("unknown", () => { + itSchemaIdentity(unknown(), true); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/record/record.test.ts b/seed/ts-sdk/license/tests/unit/zurg/record/record.test.ts new file mode 100644 index 00000000000..7e4ba39cc55 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/record/record.test.ts @@ -0,0 +1,34 @@ +import { number, record, string } from "../../../../src/core/schemas/builders"; +import { itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("record", () => { + itSchemaIdentity(record(string(), string()), { hello: "world" }); + itSchemaIdentity(record(number(), string()), { 42: "world" }); + + itValidate( + "non-record", + record(number(), string()), + [], + [ + { + path: [], + message: "Expected object. Received list.", + }, + ] + ); + + itValidate("invalid key type", record(number(), string()), { hello: "world" }, [ + { + path: ["hello (key)"], + message: 'Expected number. Received "hello".', + }, + ]); + + itValidate("invalid value type", record(string(), number()), { hello: "world" }, [ + { + path: ["hello"], + message: 'Expected number. Received "world".', + }, + ]); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/schema-utils/getSchemaUtils.test.ts b/seed/ts-sdk/license/tests/unit/zurg/schema-utils/getSchemaUtils.test.ts new file mode 100644 index 00000000000..da10086bc1d --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/schema-utils/getSchemaUtils.test.ts @@ -0,0 +1,83 @@ +import { object, string } from "../../../../src/core/schemas/builders"; +import { itSchema } from "../utils/itSchema"; + +describe("getSchemaUtils", () => { + describe("optional()", () => { + itSchema("optional fields allow original schema", string().optional(), { + raw: "hello", + parsed: "hello", + }); + + itSchema("optional fields are not required", string().optional(), { + raw: null, + parsed: undefined, + }); + }); + + describe("transform()", () => { + itSchema( + "transorm and untransform run correctly", + string().transform({ + transform: (x) => x + "X", + untransform: (x) => (x as string).slice(0, -1), + }), + { + raw: "hello", + parsed: "helloX", + } + ); + }); + + describe("parseOrThrow()", () => { + it("parses valid value", async () => { + const value = string().parseOrThrow("hello"); + expect(value).toBe("hello"); + }); + + it("throws on invalid value", async () => { + const value = () => object({ a: string(), b: string() }).parseOrThrow({ a: 24 }); + expect(value).toThrowError(new Error('a: Expected string. Received 24.; Missing required key "b"')); + }); + }); + + describe("jsonOrThrow()", () => { + it("serializes valid value", async () => { + const value = string().jsonOrThrow("hello"); + expect(value).toBe("hello"); + }); + + it("throws on invalid value", async () => { + const value = () => object({ a: string(), b: string() }).jsonOrThrow({ a: 24 }); + expect(value).toThrowError(new Error('a: Expected string. Received 24.; Missing required key "b"')); + }); + }); + + describe("omitUndefined", () => { + it("serializes undefined as null", async () => { + const value = object({ + a: string().optional(), + b: string().optional(), + }).jsonOrThrow({ + a: "hello", + b: undefined, + }); + expect(value).toEqual({ a: "hello", b: null }); + }); + + it("omits undefined values", async () => { + const value = object({ + a: string().optional(), + b: string().optional(), + }).jsonOrThrow( + { + a: "hello", + b: undefined, + }, + { + omitUndefined: true, + } + ); + expect(value).toEqual({ a: "hello" }); + }); + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/schema.test.ts b/seed/ts-sdk/license/tests/unit/zurg/schema.test.ts new file mode 100644 index 00000000000..94089a9a91b --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/schema.test.ts @@ -0,0 +1,78 @@ +import { + boolean, + discriminant, + list, + number, + object, + string, + stringLiteral, + union, +} from "../../../src/core/schemas/builders"; +import { booleanLiteral } from "../../../src/core/schemas/builders/literals/booleanLiteral"; +import { property } from "../../../src/core/schemas/builders/object/property"; +import { itSchema } from "./utils/itSchema"; + +describe("Schema", () => { + itSchema( + "large nested object", + object({ + a: string(), + b: stringLiteral("b value"), + c: property( + "raw_c", + list( + object({ + animal: union(discriminant("type", "_type"), { + dog: object({ value: boolean() }), + cat: object({ value: property("raw_cat", number()) }), + }), + }) + ) + ), + d: property("raw_d", boolean()), + e: booleanLiteral(true), + }), + { + raw: { + a: "hello", + b: "b value", + raw_c: [ + { + animal: { + _type: "dog", + value: true, + }, + }, + { + animal: { + _type: "cat", + raw_cat: 42, + }, + }, + ], + raw_d: false, + e: true, + }, + parsed: { + a: "hello", + b: "b value", + c: [ + { + animal: { + type: "dog", + value: true, + }, + }, + { + animal: { + type: "cat", + value: 42, + }, + }, + ], + d: false, + e: true, + }, + } + ); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/set/set.test.ts b/seed/ts-sdk/license/tests/unit/zurg/set/set.test.ts new file mode 100644 index 00000000000..e17f908c80e --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/set/set.test.ts @@ -0,0 +1,48 @@ +import { set, string } from "../../../../src/core/schemas/builders"; +import { itSchema } from "../utils/itSchema"; +import { itValidateJson, itValidateParse } from "../utils/itValidate"; + +describe("set", () => { + itSchema("converts between raw list and parsed Set", set(string()), { + raw: ["A", "B"], + parsed: new Set(["A", "B"]), + }); + + itValidateParse("not a list", set(string()), 42, [ + { + path: [], + message: "Expected list. Received 42.", + }, + ]); + + itValidateJson( + "not a Set", + set(string()), + [], + [ + { + path: [], + message: "Expected Set. Received list.", + }, + ] + ); + + itValidateParse( + "invalid item type", + set(string()), + [42], + [ + { + path: ["[0]"], + message: "Expected string. Received 42.", + }, + ] + ); + + itValidateJson("invalid item type", set(string()), new Set([42]), [ + { + path: ["[0]"], + message: "Expected string. Received 42.", + }, + ]); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/skipValidation.test.ts b/seed/ts-sdk/license/tests/unit/zurg/skipValidation.test.ts new file mode 100644 index 00000000000..5dc88096a9f --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/skipValidation.test.ts @@ -0,0 +1,45 @@ +/* eslint-disable no-console */ + +import { boolean, number, object, property, string, undiscriminatedUnion } from "../../../src/core/schemas/builders"; + +describe("skipValidation", () => { + it("allows data that doesn't conform to the schema", async () => { + const warningLogs: string[] = []; + const originalConsoleWarn = console.warn; + console.warn = (...args) => warningLogs.push(args.join(" ")); + + const schema = object({ + camelCase: property("snake_case", string()), + numberProperty: number(), + requiredProperty: boolean(), + anyPrimitive: undiscriminatedUnion([string(), number(), boolean()]), + }); + + const parsed = await schema.parse( + { + snake_case: "hello", + numberProperty: "oops", + anyPrimitive: true, + }, + { + skipValidation: true, + } + ); + + expect(parsed).toEqual({ + ok: true, + value: { + camelCase: "hello", + numberProperty: "oops", + anyPrimitive: true, + }, + }); + + expect(warningLogs).toEqual([ + `Failed to validate. + - numberProperty: Expected number. Received "oops".`, + ]); + + console.warn = originalConsoleWarn; + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/undiscriminated-union/undiscriminatedUnion.test.ts b/seed/ts-sdk/license/tests/unit/zurg/undiscriminated-union/undiscriminatedUnion.test.ts new file mode 100644 index 00000000000..0e66433371c --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/undiscriminated-union/undiscriminatedUnion.test.ts @@ -0,0 +1,44 @@ +import { number, object, property, string, undiscriminatedUnion } from "../../../../src/core/schemas/builders"; +import { itSchema, itSchemaIdentity } from "../utils/itSchema"; + +describe("undiscriminatedUnion", () => { + itSchemaIdentity(undiscriminatedUnion([string(), number()]), "hello world"); + + itSchemaIdentity(undiscriminatedUnion([object({ hello: string() }), object({ goodbye: string() })]), { + goodbye: "foo", + }); + + itSchema( + "Correctly transforms", + undiscriminatedUnion([object({ hello: string() }), object({ helloWorld: property("hello_world", string()) })]), + { + raw: { hello_world: "foo " }, + parsed: { helloWorld: "foo " }, + } + ); + + it("Returns errors for all variants", async () => { + const result = await undiscriminatedUnion([string(), number()]).parse(true); + if (result.ok) { + throw new Error("Unexpectedly passed validation"); + } + expect(result.errors).toEqual([ + { + message: "[Variant 0] Expected string. Received true.", + path: [], + }, + { + message: "[Variant 1] Expected number. Received true.", + path: [], + }, + ]); + }); + + describe("compile", () => { + // eslint-disable-next-line jest/expect-expect + it("doesn't compile with zero members", () => { + // @ts-expect-error + () => undiscriminatedUnion([]); + }); + }); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/union/union.test.ts b/seed/ts-sdk/license/tests/unit/zurg/union/union.test.ts new file mode 100644 index 00000000000..790184603ac --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/union/union.test.ts @@ -0,0 +1,113 @@ +import { boolean, discriminant, number, object, string, union } from "../../../../src/core/schemas/builders"; +import { itSchema, itSchemaIdentity } from "../utils/itSchema"; +import { itValidate } from "../utils/itValidate"; + +describe("union", () => { + itSchemaIdentity( + union("type", { + lion: object({ + meows: boolean(), + }), + giraffe: object({ + heightInInches: number(), + }), + }), + { type: "lion", meows: true }, + { title: "doesn't transform discriminant when it's a string" } + ); + + itSchema( + "transforms discriminant when it's a discriminant()", + union(discriminant("type", "_type"), { + lion: object({ meows: boolean() }), + giraffe: object({ heightInInches: number() }), + }), + { + raw: { _type: "lion", meows: true }, + parsed: { type: "lion", meows: true }, + } + ); + + describe("allowUnrecognizedUnionMembers", () => { + itSchema( + "transforms discriminant & passes through values when discriminant value is unrecognized", + union(discriminant("type", "_type"), { + lion: object({ meows: boolean() }), + giraffe: object({ heightInInches: number() }), + }), + { + // @ts-expect-error + raw: { _type: "moose", isAMoose: true }, + // @ts-expect-error + parsed: { type: "moose", isAMoose: true }, + opts: { + allowUnrecognizedUnionMembers: true, + }, + } + ); + }); + + describe("withParsedProperties", () => { + it("Added property is included on parsed object", async () => { + const schema = union("type", { + lion: object({}), + tiger: object({ value: string() }), + }).withParsedProperties({ + printType: (parsed) => () => parsed.type, + }); + + const parsed = await schema.parse({ type: "lion" }); + if (!parsed.ok) { + throw new Error("Failed to parse"); + } + expect(parsed.value.printType()).toBe("lion"); + }); + }); + + itValidate( + "non-object", + union("type", { + lion: object({}), + tiger: object({ value: string() }), + }), + [], + [ + { + path: [], + message: "Expected object. Received list.", + }, + ] + ); + + itValidate( + "missing discriminant", + union("type", { + lion: object({}), + tiger: object({ value: string() }), + }), + {}, + [ + { + path: [], + message: 'Missing discriminant ("type")', + }, + ] + ); + + itValidate( + "unrecognized discriminant value", + union("type", { + lion: object({}), + tiger: object({ value: string() }), + }), + { + type: "bear", + }, + [ + { + path: ["type"], + message: 'Expected enum. Received "bear".', + }, + ] + ); +}); diff --git a/seed/ts-sdk/license/tests/unit/zurg/utils/itSchema.ts b/seed/ts-sdk/license/tests/unit/zurg/utils/itSchema.ts new file mode 100644 index 00000000000..67b6c928175 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/utils/itSchema.ts @@ -0,0 +1,78 @@ +/* eslint-disable jest/no-export */ +import { Schema, SchemaOptions } from "../../../../src/core/schemas/Schema"; + +export function itSchemaIdentity( + schema: Schema, + value: T, + { title = "functions as identity", opts }: { title?: string; opts?: SchemaOptions } = {} +): void { + itSchema(title, schema, { raw: value, parsed: value, opts }); +} + +export function itSchema( + title: string, + schema: Schema, + { + raw, + parsed, + opts, + only = false, + }: { + raw: Raw; + parsed: Parsed; + opts?: SchemaOptions; + only?: boolean; + } +): void { + // eslint-disable-next-line jest/valid-title + (only ? describe.only : describe)(title, () => { + itParse("parse()", schema, { raw, parsed, opts }); + itJson("json()", schema, { raw, parsed, opts }); + }); +} + +export function itParse( + title: string, + schema: Schema, + { + raw, + parsed, + opts, + }: { + raw: Raw; + parsed: Parsed; + opts?: SchemaOptions; + } +): void { + // eslint-disable-next-line jest/valid-title + it(title, () => { + const maybeValid = schema.parse(raw, opts); + if (!maybeValid.ok) { + throw new Error("Failed to parse() " + JSON.stringify(maybeValid.errors, undefined, 4)); + } + expect(maybeValid.value).toStrictEqual(parsed); + }); +} + +export function itJson( + title: string, + schema: Schema, + { + raw, + parsed, + opts, + }: { + raw: Raw; + parsed: Parsed; + opts?: SchemaOptions; + } +): void { + // eslint-disable-next-line jest/valid-title + it(title, () => { + const maybeValid = schema.json(parsed, opts); + if (!maybeValid.ok) { + throw new Error("Failed to json() " + JSON.stringify(maybeValid.errors, undefined, 4)); + } + expect(maybeValid.value).toStrictEqual(raw); + }); +} diff --git a/seed/ts-sdk/license/tests/unit/zurg/utils/itValidate.ts b/seed/ts-sdk/license/tests/unit/zurg/utils/itValidate.ts new file mode 100644 index 00000000000..75b2c08b036 --- /dev/null +++ b/seed/ts-sdk/license/tests/unit/zurg/utils/itValidate.ts @@ -0,0 +1,56 @@ +/* eslint-disable jest/no-export */ +import { Schema, SchemaOptions, ValidationError } from "../../../../src/core/schemas/Schema"; + +export function itValidate( + title: string, + schema: Schema, + input: unknown, + errors: ValidationError[], + opts?: SchemaOptions +): void { + // eslint-disable-next-line jest/valid-title + describe("parse()", () => { + itValidateParse(title, schema, input, errors, opts); + }); + describe("json()", () => { + itValidateJson(title, schema, input, errors, opts); + }); +} + +export function itValidateParse( + title: string, + schema: Schema, + raw: unknown, + errors: ValidationError[], + opts?: SchemaOptions +): void { + describe("parse", () => { + // eslint-disable-next-line jest/valid-title + it(title, async () => { + const maybeValid = await schema.parse(raw, opts); + if (maybeValid.ok) { + throw new Error("Value passed validation"); + } + expect(maybeValid.errors).toStrictEqual(errors); + }); + }); +} + +export function itValidateJson( + title: string, + schema: Schema, + parsed: unknown, + errors: ValidationError[], + opts?: SchemaOptions +): void { + describe("json", () => { + // eslint-disable-next-line jest/valid-title + it(title, async () => { + const maybeValid = await schema.json(parsed, opts); + if (maybeValid.ok) { + throw new Error("Value passed validation"); + } + expect(maybeValid.errors).toStrictEqual(errors); + }); + }); +} diff --git a/seed/ts-sdk/license/tsconfig.json b/seed/ts-sdk/license/tsconfig.json new file mode 100644 index 00000000000..538c94fe015 --- /dev/null +++ b/seed/ts-sdk/license/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "extendedDiagnostics": true, + "strict": true, + "target": "ES6", + "module": "CommonJS", + "moduleResolution": "node", + "esModuleInterop": true, + "skipLibCheck": true, + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "baseUrl": "src" + }, + "include": ["src"], + "exclude": [] +}