From 7a14ed6de96e2831dd26ff655d7e3e5d0083a532 Mon Sep 17 00:00:00 2001 From: jsklan <100491078+jsklan@users.noreply.github.com> Date: Tue, 24 Feb 2026 18:58:56 -0500 Subject: [PATCH 01/20] chore(ci): sync update-test.yml with ci.yml (#12732) * chore(ci): sync update-test.yml with ci.yml Co-Authored-By: unknown <> * chore(ci): extend timeouts by 200% Co-Authored-By: unknown <> * Increase timeout for test jobs to 25 minutes * Increase ETE tests timeout from 10 to 20 minutes --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Fern Support <126544928+fern-support@users.noreply.github.com> --- .github/workflows/ci.yml | 12 ++++----- .github/workflows/update-test.yml | 43 ++++++++++++++++++++++--------- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d1f4d5cc1b21..768022921573 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,7 +111,7 @@ jobs: test: if: github.repository == 'fern-api/fern' runs-on: Test - timeout-minutes: 15 + timeout-minutes: 25 steps: - name: Checkout repo uses: actions/checkout@v4 @@ -164,7 +164,7 @@ jobs: test-ete: if: github.repository == 'fern-api/fern' runs-on: Test - timeout-minutes: 15 + timeout-minutes: 25 steps: - name: Checkout repo uses: actions/checkout@v4 @@ -188,16 +188,16 @@ jobs: run: go install github.com/fern-api/protoc-gen-openapi/cmd/protoc-gen-openapi@latest - name: Build CLI (dev) - timeout-minutes: 5 + timeout-minutes: 10 run: pnpm turbo run dist:cli:dev --filter=@fern-api/cli --filter=@fern-api/cli-v2 - name: Build seed CLI - timeout-minutes: 5 + timeout-minutes: 10 run: pnpm seed:build - name: Run ETE tests if: ${{ !github.event.inputs.update_snapshots }} - timeout-minutes: 10 + timeout-minutes: 20 env: FERN_ORG_TOKEN_DEV: ${{ secrets.FERN_ORG_TOKEN_DEV }} run: | @@ -205,7 +205,7 @@ jobs: - name: Run ETE tests with snapshot updates if: ${{ github.event.inputs.update_snapshots == 'true' }} - timeout-minutes: 10 + timeout-minutes: 20 env: FERN_ORG_TOKEN_DEV: ${{ secrets.FERN_ORG_TOKEN_DEV }} run: | diff --git a/.github/workflows/update-test.yml b/.github/workflows/update-test.yml index 80191a1439c1..35ee407f6462 100644 --- a/.github/workflows/update-test.yml +++ b/.github/workflows/update-test.yml @@ -8,20 +8,22 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -permissions: - contents: write - env: DO_NOT_TRACK: "1" + TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} + TURBO_TEAM: "buildwithfern" + TURBO_REMOTE_CACHE_TIMEOUT: 60 jobs: update-test: if: github.repository == 'fern-api/fern' && github.ref != 'refs/heads/main' - runs-on: ubuntu-latest - timeout-minutes: 30 + runs-on: Test + timeout-minutes: 90 steps: - name: Checkout repo uses: actions/checkout@v4 + with: + lfs: true - name: Install uses: ./.github/actions/install @@ -30,6 +32,9 @@ jobs: with: github_token: ${{ github.token }} + - name: Verify buf + run: buf --version + - uses: actions/setup-go@v5 with: go-version: "stable" @@ -41,15 +46,29 @@ jobs: - name: Run test:update run: pnpm test:update + - name: Build CLI (dev) + timeout-minutes: 15 + run: pnpm turbo run dist:cli:dev --filter=@fern-api/cli --filter=@fern-api/cli-v2 + + - name: Build seed CLI + timeout-minutes: 15 + run: pnpm seed:build + - name: Run test:ete:update + timeout-minutes: 30 env: FERN_ORG_TOKEN_DEV: ${{ secrets.FERN_ORG_TOKEN_DEV }} - run: FERN_TOKEN=${{ secrets.FERN_ORG_TOKEN_DEV }} pnpm test:ete:update + run: | + FERN_TOKEN=${{ secrets.FERN_ORG_TOKEN_DEV }} pnpm --filter @fern-api/ete-tests test -- -u - name: Commit and Push Changes - uses: EndBug/add-and-commit@v9 - with: - add: "." - push: true - fetch: false - message: "chore: update test snapshots" + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + git add . + if ! git diff --staged --quiet; then + git commit -m "chore: update test snapshots" + git push + else + echo "No snapshot changes to commit" + fi From d2520f0cc75fd1353703818084c5678b49cb288c Mon Sep 17 00:00:00 2001 From: Alex McKinney Date: Tue, 24 Feb 2026 19:16:10 -0500 Subject: [PATCH 02/20] fix(cli): Flush PostHog to emit events (#12729) --- packages/cli/cli-v2/package.json | 2 +- .../cli/cli-v2/src/telemetry/TelemetryClient.ts | 4 ++-- .../src/telemetry/__test__/TelemetryClient.test.ts | 14 +++++++------- pnpm-lock.yaml | 14 ++------------ 4 files changed, 12 insertions(+), 22 deletions(-) diff --git a/packages/cli/cli-v2/package.json b/packages/cli/cli-v2/package.json index 07808e6c6685..c3050d4c6e3e 100644 --- a/packages/cli/cli-v2/package.json +++ b/packages/cli/cli-v2/package.json @@ -84,7 +84,7 @@ "js-yaml": "catalog:", "jsonwebtoken": "catalog:", "ora": "catalog:", - "posthog-node": "^4.3.2", + "posthog-node": "^5.14.0", "tsup": "catalog:", "typescript": "catalog:", "uuid": "catalog:", diff --git a/packages/cli/cli-v2/src/telemetry/TelemetryClient.ts b/packages/cli/cli-v2/src/telemetry/TelemetryClient.ts index 97cecc9c8dab..05b4e95e5ffa 100644 --- a/packages/cli/cli-v2/src/telemetry/TelemetryClient.ts +++ b/packages/cli/cli-v2/src/telemetry/TelemetryClient.ts @@ -58,7 +58,7 @@ export class TelemetryClient { try { this.posthog.capture({ distinctId: await this.getDistinctId(), - event: "CLI", + event: "cli", properties: { ...this.baseTags, ...this.accumulatedTags, @@ -80,7 +80,7 @@ export class TelemetryClient { return; } try { - await this.posthog.flush(); + await this.posthog.shutdown(); } catch { // no-op } diff --git a/packages/cli/cli-v2/src/telemetry/__test__/TelemetryClient.test.ts b/packages/cli/cli-v2/src/telemetry/__test__/TelemetryClient.test.ts index 6bf3ae29fe10..5abf6556b154 100644 --- a/packages/cli/cli-v2/src/telemetry/__test__/TelemetryClient.test.ts +++ b/packages/cli/cli-v2/src/telemetry/__test__/TelemetryClient.test.ts @@ -5,15 +5,15 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; // vi.hoisted creates refs accessible inside vi.mock factory functions, // which are hoisted to the top of the file before any imports. -const { mockCapture, mockFlush } = vi.hoisted(() => ({ +const { mockCapture, mockShutdown } = vi.hoisted(() => ({ mockCapture: vi.fn(), - mockFlush: vi.fn().mockResolvedValue(undefined) + mockShutdown: vi.fn().mockResolvedValue(undefined) })); vi.mock("posthog-node", () => ({ // eslint-disable-next-line func-style PostHog: vi.fn(function () { - return { capture: mockCapture, flush: mockFlush }; + return { capture: mockCapture, shutdown: mockShutdown }; }) })); @@ -31,7 +31,7 @@ beforeEach(async () => { process.env["POSTHOG_API_KEY"] = "phc_test"; delete process.env["FERN_TELEMETRY_DISABLED"]; mockCapture.mockClear(); - mockFlush.mockClear(); + mockShutdown.mockClear(); }); afterEach(async () => { @@ -55,7 +55,7 @@ describe("TelemetryClient", () => { expect(mockCapture).toHaveBeenCalledOnce(); expect(mockCapture).toHaveBeenCalledWith( expect.objectContaining({ - event: "CLI", + event: "cli", properties: expect.objectContaining({ command: "sdk generate", status: "success", @@ -114,14 +114,14 @@ describe("TelemetryClient", () => { it("delegates to the posthog client", async () => { const client = new TelemetryClient({ isTTY: false }); await client.flush(); - expect(mockFlush).toHaveBeenCalledOnce(); + expect(mockShutdown).toHaveBeenCalledOnce(); }); it("is a no-op when disabled", async () => { delete process.env["POSTHOG_API_KEY"]; const client = new TelemetryClient({ isTTY: false }); await client.flush(); - expect(mockFlush).not.toHaveBeenCalled(); + expect(mockShutdown).not.toHaveBeenCalled(); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fac80de358f4..0e36966453d4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5240,8 +5240,8 @@ importers: specifier: 'catalog:' version: 7.0.1 posthog-node: - specifier: ^4.3.2 - version: 4.18.0 + specifier: ^5.14.0 + version: 5.24.7 tsup: specifier: 'catalog:' version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.3.3) @@ -13796,10 +13796,6 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} - posthog-node@4.18.0: - resolution: {integrity: sha512-XROs1h+DNatgKh/AlIlCtDxWzwrKdYDb2mOs58n4yN8BkGN9ewqeQwG5ApS4/IzwCb7HPttUkOVulkYatd2PIw==} - engines: {node: '>=15.0.0'} - posthog-node@5.24.7: resolution: {integrity: sha512-IJ0Zj+v+eg/JQMZ75n0Hcp4NzuQzWcZjqFjcUQs6RhW2l5FiQIq09sKJMleXX33hYxD6sfjFsDTqugJlgeAohg==} engines: {node: ^20.20.0 || >=22.22.0} @@ -21056,12 +21052,6 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - posthog-node@4.18.0: - dependencies: - axios: 1.13.5 - transitivePeerDependencies: - - debug - posthog-node@5.24.7: dependencies: '@posthog/core': 1.17.0 From 4fc97a063c04c1717802f45de71429b32b24c70d Mon Sep 17 00:00:00 2001 From: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com> Date: Tue, 24 Feb 2026 19:36:03 -0500 Subject: [PATCH 03/20] fix(cli): fall back to 0.0.1 when AUTO versioning cannot extract previous version (#12725) Co-authored-by: unknown <> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Fern Support <126544928+fern-support@users.noreply.github.com> --- packages/cli/cli/versions.yml | 10 ++++++++++ .../src/AutoVersioningService.ts | 18 ++++++++++++------ .../src/LocalTaskHandler.ts | 18 +++++++++++++----- .../local-workspace-runner/src/VersionUtils.ts | 7 +++---- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/packages/cli/cli/versions.yml b/packages/cli/cli/versions.yml index 0fe108312216..971601c186cd 100644 --- a/packages/cli/cli/versions.yml +++ b/packages/cli/cli/versions.yml @@ -1,4 +1,14 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 3.86.1 + changelogEntry: + - summary: | + Fall back to initial version 0.0.1 when AUTO versioning cannot extract + the previous version instead of failing the entire generation. This + handles new SDK repositories where all files are additions and no + previous version lines exist in the diff. + type: fix + createdAt: "2026-02-24" + irVersion: 65 - version: 3.86.0 changelogEntry: - summary: | diff --git a/packages/cli/generation/local-generation/local-workspace-runner/src/AutoVersioningService.ts b/packages/cli/generation/local-generation/local-workspace-runner/src/AutoVersioningService.ts index ebe1fde03950..0ca1b5a18736 100644 --- a/packages/cli/generation/local-generation/local-workspace-runner/src/AutoVersioningService.ts +++ b/packages/cli/generation/local-generation/local-workspace-runner/src/AutoVersioningService.ts @@ -83,8 +83,14 @@ export class AutoVersioningService { } const extracted = this.extractPreviousVersionFromDiffLine(matchingMinusLine); - this.logger.debug(`Extracted previous version from diff (file: ${currentFile}): ${extracted}`); - return extracted; + if (extracted != undefined) { + this.logger.debug(`Extracted previous version from diff (file: ${currentFile}): ${extracted}`); + return extracted; + } + this.logger.debug( + `Could not parse version from matching minus line (file: ${currentFile}); continuing search.` + ); + continue; } } @@ -445,10 +451,9 @@ export class AutoVersioningService { * Assumes the line format is like: "version = '505.503.4455'" or "version: 505.503.4455" * * @param lineWithMagicVersion A line from git diff containing the magic version - * @return The inferred previous version if found - * @throws AutoVersioningException if no valid version can be extracted + * @return The inferred previous version if found, or undefined if the version cannot be parsed */ - private extractPreviousVersionFromDiffLine(lineWithMagicVersion: string): string { + private extractPreviousVersionFromDiffLine(lineWithMagicVersion: string): string | undefined { const prevVersionPattern = /[-].*?([v]?\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?)/; const matcher = lineWithMagicVersion.match(prevVersionPattern); @@ -458,6 +463,7 @@ export class AutoVersioningService { return version; } - throw new AutoVersioningException("Could not extract previous version from diff line: " + lineWithMagicVersion); + this.logger.warn("Could not extract previous version from diff line: " + lineWithMagicVersion); + return undefined; } } diff --git a/packages/cli/generation/local-generation/local-workspace-runner/src/LocalTaskHandler.ts b/packages/cli/generation/local-generation/local-workspace-runner/src/LocalTaskHandler.ts index ddfc58725be1..2241a05dbf26 100644 --- a/packages/cli/generation/local-generation/local-workspace-runner/src/LocalTaskHandler.ts +++ b/packages/cli/generation/local-generation/local-workspace-runner/src/LocalTaskHandler.ts @@ -194,12 +194,20 @@ export class LocalTaskHandler { } } catch (error) { if (error instanceof AutoVersioningException) { - // When user explicitly requested AUTO versioning, we must fail if we can't extract version - this.context.logger.error(`AUTO versioning failed: ${error.message}`); - throw new Error( - `Failed to extract previous version for automatic semantic versioning. ` + - `Please ensure your project has a version file in a supported format. Error: ${error.message}` + // Fall back to initial version when we can't extract the previous version + // (e.g., new SDK repos where all files are additions, or unsupported version formats) + this.context.logger.warn( + `AUTO versioning could not extract previous version: ${error.message}. ` + + `Falling back to initial version 0.0.1.` ); + const initialVersion = this.version?.startsWith("v") ? "v0.0.1" : "0.0.1"; + const commitMessage = this.isWhitelabel + ? "Initial SDK generation" + : "Initial SDK generation\n\n🌿 Generated with Fern"; + return { + version: initialVersion, + commitMessage + }; } this.context.logger.error(`Failed to perform automatic versioning: ${error}`); diff --git a/packages/cli/generation/local-generation/local-workspace-runner/src/VersionUtils.ts b/packages/cli/generation/local-generation/local-workspace-runner/src/VersionUtils.ts index c15543db86c3..c1a99ce4fcb0 100644 --- a/packages/cli/generation/local-generation/local-workspace-runner/src/VersionUtils.ts +++ b/packages/cli/generation/local-generation/local-workspace-runner/src/VersionUtils.ts @@ -78,10 +78,9 @@ export function incrementVersion(currentVersion: string, versionBump: VersionBum * Assumes the line format is like: "version = '505.503.4455'" or "version: 505.503.4455" * * @param lineWithMagicVersion A line from git diff containing the magic version - * @return The inferred previous version if found - * @throws AutoVersioningException if no valid version can be extracted + * @return The inferred previous version if found, or undefined if the version cannot be parsed */ -export function extractPreviousVersionFromDiffLine(lineWithMagicVersion: string): string { +export function extractPreviousVersionFromDiffLine(lineWithMagicVersion: string): string | undefined { const prevVersionPattern = /[-].*?([v]?\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?)/; const matcher = lineWithMagicVersion.match(prevVersionPattern); @@ -90,5 +89,5 @@ export function extractPreviousVersionFromDiffLine(lineWithMagicVersion: string) return version; } - throw new AutoVersioningException("Could not extract previous version from diff line: " + lineWithMagicVersion); + return undefined; } From ed1f4042a38ab53c375ed8f735f1d48298d5150c Mon Sep 17 00:00:00 2001 From: Alex McKinney Date: Tue, 24 Feb 2026 19:44:29 -0500 Subject: [PATCH 04/20] feat(cli): implement `fern sdk add` command (#12715) --- packages/cli/cli-v2/package.json | 1 + .../cli/cli-v2/src/commands/init/command.ts | 2 +- .../cli-v2/src/commands/sdk/add/command.ts | 382 ++++++++++++ .../cli/cli-v2/src/commands/sdk/add/index.ts | 1 + .../cli/cli-v2/src/commands/sdk/command.ts | 3 +- .../src/commands/sdk/generate/command.ts | 12 +- .../commands/sdk/generate/parseOutputArg.ts | 11 +- .../cli-v2/src/commands/sdk/utils/gitUrl.ts | 14 + .../commands/sdk/utils/isRemoteReference.ts | 12 + .../cli-v2/src/config/fern-yml/constants.ts | 9 + packages/cli/cli-v2/src/init/Wizard.ts | 16 +- packages/cli/cli-v2/src/migrator/Migrator.ts | 3 +- .../cli/cli-v2/src/sdk/config/Language.ts | 30 + .../cli/ete-tests/src/tests/v2/add.test.ts | 579 ++++++++++++++++++ pnpm-lock.yaml | 3 + 15 files changed, 1040 insertions(+), 38 deletions(-) create mode 100644 packages/cli/cli-v2/src/commands/sdk/add/command.ts create mode 100644 packages/cli/cli-v2/src/commands/sdk/add/index.ts create mode 100644 packages/cli/cli-v2/src/commands/sdk/utils/gitUrl.ts create mode 100644 packages/cli/cli-v2/src/commands/sdk/utils/isRemoteReference.ts create mode 100644 packages/cli/cli-v2/src/config/fern-yml/constants.ts create mode 100644 packages/cli/ete-tests/src/tests/v2/add.test.ts diff --git a/packages/cli/cli-v2/package.json b/packages/cli/cli-v2/package.json index c3050d4c6e3e..89251f5adb1e 100644 --- a/packages/cli/cli-v2/package.json +++ b/packages/cli/cli-v2/package.json @@ -82,6 +82,7 @@ "inquirer": "^9.2.15", "is-ci": "catalog:", "js-yaml": "catalog:", + "yaml": "catalog:", "jsonwebtoken": "catalog:", "ora": "catalog:", "posthog-node": "^5.14.0", diff --git a/packages/cli/cli-v2/src/commands/init/command.ts b/packages/cli/cli-v2/src/commands/init/command.ts index ed302e8cb00b..565c980e1342 100644 --- a/packages/cli/cli-v2/src/commands/init/command.ts +++ b/packages/cli/cli-v2/src/commands/init/command.ts @@ -4,6 +4,7 @@ import chalk from "chalk"; import { mkdir, writeFile } from "fs/promises"; import path from "path"; import type { Argv } from "yargs"; +import { FERN_YML_FILENAME } from "../../config/fern-yml/constants"; import { FernYmlBuilder } from "../../config/fern-yml/FernYmlBuilder"; import type { Context } from "../../context/Context"; import type { GlobalArgs } from "../../context/GlobalArgs"; @@ -21,7 +22,6 @@ export declare namespace InitCommand { } } -const FERN_YML_FILENAME = "fern.yml"; const SPECS_DIR = "openapi"; export class InitCommand { diff --git a/packages/cli/cli-v2/src/commands/sdk/add/command.ts b/packages/cli/cli-v2/src/commands/sdk/add/command.ts new file mode 100644 index 000000000000..1a899bf7b76c --- /dev/null +++ b/packages/cli/cli-v2/src/commands/sdk/add/command.ts @@ -0,0 +1,382 @@ +import { schemas } from "@fern-api/config"; +import { getLatestGeneratorVersion } from "@fern-api/configuration-loader"; +import { AbsoluteFilePath, dirname, doesPathExist, join, RelativeFilePath } from "@fern-api/fs-utils"; +import chalk from "chalk"; +import { readFile, writeFile } from "fs/promises"; +import inquirer from "inquirer"; +import { type Document, parseDocument } from "yaml"; +import type { Argv } from "yargs"; +import { FERN_YML_FILENAME, REF_KEY } from "../../../config/fern-yml/constants.js"; +import type { Context } from "../../../context/Context.js"; +import type { GlobalArgs } from "../../../context/GlobalArgs.js"; +import { CliError } from "../../../errors/CliError.js"; +import { LANGUAGE_TO_DOCKER_IMAGE } from "../../../sdk/config/converter/constants.js"; +import { LANGUAGE_DISPLAY_NAMES, LANGUAGE_ORDER, LANGUAGES, type Language } from "../../../sdk/config/Language.js"; +import { Icons } from "../../../ui/format.js"; +import { Version } from "../../../version.js"; +import { command } from "../../_internal/command.js"; +import { isGitUrl } from "../utils/gitUrl.js"; +import { isRemoteReference } from "../utils/isRemoteReference.js"; + +export declare namespace AddCommand { + export interface Args extends GlobalArgs { + /** The SDK target to add (e.g. typescript, python, go). */ + target?: string; + + /** Pin the latest stable version instead of "latest" (e.g. 1.0.0). */ + stable: boolean; + + /** Output path or git URL (e.g. ./sdks/typescript). */ + output?: string; + + /** The group to add the target to. */ + group?: string; + + /** Accept all defaults (non-interactive mode). */ + yes: boolean; + } +} + +export class AddCommand { + public async handle(context: Context, args: AddCommand.Args): Promise { + const workspace = await context.loadWorkspaceOrThrow(); + + const fernYmlPath = workspace.absoluteFilePath; + if (fernYmlPath == null) { + throw new CliError({ + message: `No ${FERN_YML_FILENAME} found. Run 'fern init' to initialize a project.` + }); + } + + if (!context.isTTY || args.yes) { + return this.handleWithFlags({ context, args, fernYmlPath }); + } + + return this.handleInteractive({ context, args, fernYmlPath }); + } + + private async handleWithFlags({ + context, + args, + fernYmlPath + }: { + context: Context; + args: AddCommand.Args; + fernYmlPath: AbsoluteFilePath; + }): Promise { + if (args.target == null) { + throw new CliError({ + message: `Missing required flags:\n\n --target SDK language (e.g. typescript, python, go)` + }); + } + + const language = this.parseLanguage(args.target); + const output = args.output != null ? this.parseOutput(args.output) : { path: `./sdks/${language}` }; + const version = args.stable ? await this.resolveStableVersion({ context, language }) : undefined; + + await this.addTargetToFernYml({ + context, + fernYmlPath, + language, + output, + version, + group: args.group + }); + + context.stderr.info(`${Icons.success} Added '${language}' to ${FERN_YML_FILENAME}`); + } + + private async handleInteractive({ + context, + args, + fernYmlPath + }: { + context: Context; + args: AddCommand.Args; + fernYmlPath: AbsoluteFilePath; + }): Promise { + const language = args.target != null ? this.parseLanguage(args.target) : await this.promptLanguage(); + + await this.checkForDuplicate({ fernYmlPath, language }); + + const output = args.output != null ? this.parseOutput(args.output) : { path: `./sdks/${language}` }; + const version = args.stable ? await this.resolveStableVersion({ context, language }) : undefined; + + await this.addTargetToFernYml({ + context, + fernYmlPath, + language, + output, + version, + group: args.group + }); + + context.stderr.info(`${Icons.success} Added '${language}' to ${FERN_YML_FILENAME}`); + } + + private async promptLanguage(): Promise { + const { language } = await inquirer.prompt<{ language: Language }>([ + { + type: "list", + name: "language", + message: "Which SDK language would you like to add?", + loop: false, + choices: LANGUAGE_ORDER.map((lang) => ({ + name: LANGUAGE_DISPLAY_NAMES[lang], + value: lang + })) + } + ]); + return language; + } + + private parseOutput(value: string): schemas.OutputSchema { + if (isGitUrl(value)) { + return { + git: { + repository: value + } + }; + } + + if (isRemoteReference(value)) { + throw new CliError({ + message: + `"${value}" looks like a remote reference but is not a recognized git URL.\n\n` + + ` Please specify a local path (e.g. ./sdks/my-sdk) or a git URL:\n` + + ` https://github.com/owner/repo` + }); + } + + return { path: value }; + } + + private async resolveStableVersion({ + context, + language + }: { + context: Context; + language: Language; + }): Promise { + const image = LANGUAGE_TO_DOCKER_IMAGE[language]; + try { + const version = await getLatestGeneratorVersion({ + generatorName: image, + cliVersion: Version, + channel: undefined + }); + return version ?? undefined; + } catch { + context.stderr.info( + chalk.dim(` Failed to resolve latest stable version for ${language}; using "latest".`) + ); + return "latest"; + } + } + + private async checkForDuplicate({ + fernYmlPath, + language + }: { + fernYmlPath: AbsoluteFilePath; + language: Language; + }): Promise { + const { targetsFilePath, document } = await this.resolveTargetsFile(fernYmlPath); + const targetsPath = this.resolveTargetsPath({ document, isRefTarget: targetsFilePath !== fernYmlPath }); + if (document.hasIn([...targetsPath, language])) { + throw new CliError({ + message: `Target '${language}' already exists in ${FERN_YML_FILENAME}.` + }); + } + } + + /** + * Adds a new SDK target to the fern.yml (or the file that actually + * contains the `targets` key, if the `sdks` section uses a `$ref`). + * + * Uses the `yaml` library's Document API so that in-line comments and + * formatting in the original file are preserved. + */ + private async addTargetToFernYml({ + context, + fernYmlPath, + language, + output, + version, + group + }: { + context: Context; + fernYmlPath: AbsoluteFilePath; + language: Language; + output: schemas.OutputSchema; + version: string | undefined; + group: string | undefined; + }): Promise { + const { targetsFilePath, document } = await this.resolveTargetsFile(fernYmlPath); + + // Find the targets map. If we followed a $ref from `sdks`, the resolved + // file's root IS the sdks object; otherwise it's `.sdks.targets`. + const isRefTarget = targetsFilePath !== fernYmlPath; + + const targetsPath = this.resolveTargetsPath({ document, isRefTarget }); + if (document.hasIn([...targetsPath, language])) { + throw new CliError({ + message: `Target '${language}' already exists in ${FERN_YML_FILENAME}.` + }); + } + + // Build the new target node. + const newTarget: Record = {}; + if (version != null) { + newTarget.version = version; + } + newTarget.output = this.buildOutputForYaml(output); + if (group != null) { + newTarget.group = [group]; + } + + // Ensure the parent path exists in the document (i.e. similar to 'mkdir -p'). + this.ensureMapPath(document, targetsPath); + + // Add the new target using the Document API in order to preserve comments. + document.setIn([...targetsPath, language], document.createNode(newTarget)); + + await writeFile(targetsFilePath, document.toString(), "utf-8"); + } + + /** + * Resolves the file that actually contains the `targets` key. + * + * If the `sdks` value in fern.yml is a `$ref`, we follow it and return + * the referenced file's Document instead. + */ + private async resolveTargetsFile( + fernYmlPath: AbsoluteFilePath + ): Promise<{ targetsFilePath: AbsoluteFilePath; document: Document }> { + const content = await readFile(fernYmlPath, "utf-8"); + const document = parseDocument(content); + const doc = document.toJS() as Record; + + if (doc == null || typeof doc !== "object") { + throw new CliError({ + message: `Invalid ${FERN_YML_FILENAME}: expected a YAML object; run 'fern init' to initialize a new file.` + }); + } + + // Check if sdks uses a $ref. + const sdksValue = doc.sdks; + if (sdksValue != null && typeof sdksValue === "object" && REF_KEY in sdksValue) { + const refPath = (sdksValue as Record)[REF_KEY]; + if (typeof refPath === "string") { + const resolvedPath = join(dirname(fernYmlPath), RelativeFilePath.of(refPath)); + if (await doesPathExist(resolvedPath)) { + const refContent = await readFile(resolvedPath, "utf-8"); + const refDocument = parseDocument(refContent); + return { targetsFilePath: resolvedPath, document: refDocument }; + } + } + } + + return { targetsFilePath: fernYmlPath, document }; + } + + /** + * Determines the YAML path to the targets map within a document. + * + * If we resolved a `$ref`, the referenced file's root is the sdks config + * so `targets` is at the root level. Otherwise `targets` is nested under + * `sdks`. + */ + private resolveTargetsPath({ document, isRefTarget }: { document: Document; isRefTarget: boolean }): string[] { + if (isRefTarget) { + // Referenced file should always have targets at root. + return ["targets"]; + } + return ["sdks", "targets"]; + } + + /** + * Ensures that the map path exists in the document, creating intermediate + * maps as needed. + */ + private ensureMapPath(document: Document, path: (string | number)[]): void { + for (let i = 1; i <= path.length; i++) { + const subPath = path.slice(0, i); + const existing = document.getIn(subPath); + if (existing == null) { + document.setIn(subPath, document.createNode({})); + } + } + } + + private buildOutputForYaml(output: schemas.OutputSchema): Record { + if (output.git != null) { + const git = output.git; + if (schemas.isGitOutputGitHubRepository(git)) { + const gitConfig: Record = { + repository: git.repository + }; + if (git.mode != null) { + gitConfig.mode = git.mode; + } + return { git: gitConfig }; + } + if (schemas.isGitOutputSelfHosted(git)) { + const gitConfig: Record = { + uri: git.uri, + token: git.token + }; + if (git.mode != null) { + gitConfig.mode = git.mode; + } + return { git: gitConfig }; + } + } + return { path: output.path }; + } + + private parseLanguage(target: string): Language { + const lang = target as Language; + if (LANGUAGES.includes(lang)) { + return lang; + } + throw new CliError({ + message: `"${target}" is not a supported language. Supported: ${LANGUAGES.join(", ")}` + }); + } +} + +export function addAddCommand(cli: Argv): void { + const cmd = new AddCommand(); + command( + cli, + "add", + "Add a new SDK target to fern.yml", + (context, args) => cmd.handle(context, args as AddCommand.Args), + (yargs) => + yargs + .option("target", { + type: "string", + description: "The SDK language to add (e.g. typescript, python, go)" + }) + .option("stable", { + type: "boolean", + default: false, + description: "Pin the latest stable version from the Fern registry" + }) + .option("output", { + type: "string", + description: "Output path or git URL (e.g. ./sdks/go)" + }) + .option("group", { + type: "string", + description: "Add the target to a specific group" + }) + .option("yes", { + type: "boolean", + alias: "y", + description: "Accept all defaults (non-interactive mode)", + default: false + }) + ); +} diff --git a/packages/cli/cli-v2/src/commands/sdk/add/index.ts b/packages/cli/cli-v2/src/commands/sdk/add/index.ts new file mode 100644 index 000000000000..bf48005f8723 --- /dev/null +++ b/packages/cli/cli-v2/src/commands/sdk/add/index.ts @@ -0,0 +1 @@ +export { addAddCommand } from "./command.js"; diff --git a/packages/cli/cli-v2/src/commands/sdk/command.ts b/packages/cli/cli-v2/src/commands/sdk/command.ts index f0821d16cb7d..d5e42273ff65 100644 --- a/packages/cli/cli-v2/src/commands/sdk/command.ts +++ b/packages/cli/cli-v2/src/commands/sdk/command.ts @@ -1,8 +1,9 @@ import type { Argv } from "yargs"; import type { GlobalArgs } from "../../context/GlobalArgs.js"; import { commandGroup } from "../_internal/commandGroup.js"; +import { addAddCommand } from "./add/index.js"; import { addGenerateCommand } from "./generate/index.js"; export function addSdkCommand(cli: Argv): void { - commandGroup(cli, "sdk ", "Configure and generate SDKs", [addGenerateCommand]); + commandGroup(cli, "sdk ", "Configure and generate SDKs", [addAddCommand, addGenerateCommand]); } diff --git a/packages/cli/cli-v2/src/commands/sdk/generate/command.ts b/packages/cli/cli-v2/src/commands/sdk/generate/command.ts index cbddcd60c22d..6347517dfe42 100644 --- a/packages/cli/cli-v2/src/commands/sdk/generate/command.ts +++ b/packages/cli/cli-v2/src/commands/sdk/generate/command.ts @@ -23,6 +23,7 @@ import type { TaskStageLabels } from "../../../ui/TaskStageLabels.js"; import type { Workspace } from "../../../workspace/Workspace.js"; import { WorkspaceBuilder } from "../../../workspace/WorkspaceBuilder.js"; import { command } from "../../_internal/command.js"; +import { isGitUrl } from "../utils/gitUrl.js"; export declare namespace GenerateCommand { export interface Args extends GlobalArgs { @@ -387,7 +388,7 @@ export class GenerateCommand { * - Anything else is treated as a local path. */ private parseTargetOutput(args: GenerateCommand.Args): schemas.OutputSchema { - if (args.output != null && this.isGitUrl(args.output)) { + if (args.output != null && isGitUrl(args.output)) { if (!args.local) { throw new CliError({ message: @@ -539,15 +540,6 @@ export class GenerateCommand { return !args.local || targets.some((t) => t.output.git != null && schemas.isGitOutputSelfHosted(t.output.git)); } - private isGitUrl(value: string): boolean { - return ( - value.endsWith(".git") || - value.startsWith("https://github.com/") || - value.startsWith("https://gitlab.com/") || - value.startsWith("git@") - ); - } - private maybePluralSdks(targets: Target[]): string { return targets.length === 1 ? "SDK" : "SDKs"; } diff --git a/packages/cli/cli-v2/src/commands/sdk/generate/parseOutputArg.ts b/packages/cli/cli-v2/src/commands/sdk/generate/parseOutputArg.ts index 014408650cdd..284fab0892a5 100644 --- a/packages/cli/cli-v2/src/commands/sdk/generate/parseOutputArg.ts +++ b/packages/cli/cli-v2/src/commands/sdk/generate/parseOutputArg.ts @@ -1,5 +1,7 @@ import type { schemas } from "@fern-api/config"; +import { isGitUrl } from "../utils/gitUrl.js"; + /** * Parses the --output argument into an OutputSchema. * @@ -30,12 +32,3 @@ export function parseOutputArg(outputArg: string): schemas.OutputSchema { return { path: outputArg }; } - -function isGitUrl(value: string): boolean { - return ( - value.endsWith(".git") || - value.startsWith("https://github.com/") || - value.startsWith("https://gitlab.com/") || - value.startsWith("git@") - ); -} diff --git a/packages/cli/cli-v2/src/commands/sdk/utils/gitUrl.ts b/packages/cli/cli-v2/src/commands/sdk/utils/gitUrl.ts new file mode 100644 index 000000000000..e3876a82bddb --- /dev/null +++ b/packages/cli/cli-v2/src/commands/sdk/utils/gitUrl.ts @@ -0,0 +1,14 @@ +/** + * Returns true if the given value looks like a git URL. + * + * Matches URLs ending in `.git`, or starting with `https://github.com/`, + * `https://gitlab.com/`, or `git@`. + */ +export function isGitUrl(value: string): boolean { + return ( + value.endsWith(".git") || + value.startsWith("https://github.com/") || + value.startsWith("https://gitlab.com/") || + value.startsWith("git@") + ); +} diff --git a/packages/cli/cli-v2/src/commands/sdk/utils/isRemoteReference.ts b/packages/cli/cli-v2/src/commands/sdk/utils/isRemoteReference.ts new file mode 100644 index 000000000000..de141aea4860 --- /dev/null +++ b/packages/cli/cli-v2/src/commands/sdk/utils/isRemoteReference.ts @@ -0,0 +1,12 @@ +/** + * Returns true if the value looks like a remote reference but is not a + * recognized git URL (e.g. plain http/https/ssh URLs, or user@host patterns). + */ +export function isRemoteReference(value: string): boolean { + return ( + value.startsWith("http://") || + value.startsWith("https://") || + value.startsWith("ssh://") || + (value.includes("@") && value.includes(":")) + ); +} diff --git a/packages/cli/cli-v2/src/config/fern-yml/constants.ts b/packages/cli/cli-v2/src/config/fern-yml/constants.ts new file mode 100644 index 000000000000..1cd734a7dda0 --- /dev/null +++ b/packages/cli/cli-v2/src/config/fern-yml/constants.ts @@ -0,0 +1,9 @@ +/** + * The filename used for the Fern project configuration. + */ +export const FERN_YML_FILENAME = "fern.yml"; + +/** + * The key used for file-based references in fern.yml. + */ +export const REF_KEY = "$ref"; diff --git a/packages/cli/cli-v2/src/init/Wizard.ts b/packages/cli/cli-v2/src/init/Wizard.ts index c5bb18259056..2ec17e9102dc 100644 --- a/packages/cli/cli-v2/src/init/Wizard.ts +++ b/packages/cli/cli-v2/src/init/Wizard.ts @@ -15,24 +15,10 @@ import path from "path"; import type { FernYmlBuilder } from "../config/fern-yml/FernYmlBuilder"; import { TaskContextAdapter } from "../context/adapter/TaskContextAdapter"; import type { Context } from "../context/Context"; -import type { Language } from "../sdk/config/Language"; +import { LANGUAGE_DISPLAY_NAMES, LANGUAGE_ORDER, type Language } from "../sdk/config/Language"; import { Icons } from "../ui/format"; import { withSpinner } from "../ui/withSpinner"; -const LANGUAGE_DISPLAY_NAMES: Record = { - typescript: "TypeScript", - python: "Python", - go: "Go", - java: "Java", - csharp: "C#", - ruby: "Ruby", - php: "PHP", - rust: "Rust", - swift: "Swift" -}; - -const LANGUAGE_ORDER: Language[] = ["typescript", "python", "go", "java", "csharp", "ruby", "php", "rust", "swift"]; - const FERN_BANNER = [ "ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•—", "ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•‘", diff --git a/packages/cli/cli-v2/src/migrator/Migrator.ts b/packages/cli/cli-v2/src/migrator/Migrator.ts index 20563186e0b6..f60408f9081a 100644 --- a/packages/cli/cli-v2/src/migrator/Migrator.ts +++ b/packages/cli/cli-v2/src/migrator/Migrator.ts @@ -4,13 +4,12 @@ import { AbsoluteFilePath, doesPathExist, join, RelativeFilePath } from "@fern-a import type { Logger } from "@fern-api/logger"; import { readdir, rm, writeFile } from "fs/promises"; import yaml from "js-yaml"; +import { FERN_YML_FILENAME } from "../config/fern-yml/constants.js"; import { convertMultiApi, convertSingleApi } from "./converters/index.js"; import { FernConfigJsonMigrator } from "./fern-config-json/index.js"; import { GeneratorsYmlMigrator } from "./generators-yml/index.js"; import type { MigratorResult, MigratorWarning } from "./types/index.js"; -const FERN_YML_FILENAME = "fern.yml"; - export interface MigratorConfig { cwd: AbsoluteFilePath; logger: Logger; diff --git a/packages/cli/cli-v2/src/sdk/config/Language.ts b/packages/cli/cli-v2/src/sdk/config/Language.ts index 0873653af761..10ab64a47e0e 100644 --- a/packages/cli/cli-v2/src/sdk/config/Language.ts +++ b/packages/cli/cli-v2/src/sdk/config/Language.ts @@ -4,3 +4,33 @@ export const LANGUAGES = ["csharp", "go", "java", "php", "python", "ruby", "rust", "swift", "typescript"] as const; export type Language = (typeof LANGUAGES)[number]; + +/** + * Human-readable display names for each language. + */ +export const LANGUAGE_DISPLAY_NAMES: Record = { + typescript: "TypeScript", + python: "Python", + go: "Go", + java: "Java", + csharp: "C#", + ruby: "Ruby", + php: "PHP", + rust: "Rust", + swift: "Swift" +}; + +/** + * Preferred display order for language selection prompts. + */ +export const LANGUAGE_ORDER: Language[] = [ + "typescript", + "python", + "go", + "java", + "csharp", + "ruby", + "php", + "rust", + "swift" +]; diff --git a/packages/cli/ete-tests/src/tests/v2/add.test.ts b/packages/cli/ete-tests/src/tests/v2/add.test.ts new file mode 100644 index 000000000000..f2d4273a7383 --- /dev/null +++ b/packages/cli/ete-tests/src/tests/v2/add.test.ts @@ -0,0 +1,579 @@ +import * as fs from "fs/promises"; +import jsYaml from "js-yaml"; +import * as path from "path"; +import { describe, expect, it } from "vitest"; +import { createTempFixture } from "../../utils/createTempFixture.js"; +import { runCliV2 } from "../../utils/runCliV2.js"; + +// Minimal fern.yml with no sdks section. +const FERN_YML_NO_SDKS = `org: test-org`; + +// fern.yml with an existing typescript target. +const FERN_YML_WITH_TYPESCRIPT = ` +org: test-org +api: + specs: + - openapi: ./openapi.yml +sdks: + targets: + typescript: + output: + path: ./sdks/typescript +`; + +// Minimal stub OpenAPI spec. +const SIMPLE_OPENAPI = ` +openapi: "3.0.3" +info: + title: Test API + version: 1.0.0 +paths: {} +`; + +describe("fern sdk add", () => { + it("fails when no fern.yml exists", async () => { + const fixture = await createTempFixture({}); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "typescript", "--yes"], + cwd: fixture.path, + expectError: true + }); + expect(result.exitCode).not.toBe(0); + expect(result.stderrPlain).toContain("fern.yml"); + expect(result.stderrPlain).toContain("fern init"); + } finally { + await fixture.cleanup(); + } + }); + + it("fails when --target is missing", async () => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--yes"], + cwd: fixture.path, + expectError: true + }); + expect(result.exitCode).not.toBe(0); + expect(result.stderrPlain).toContain("Missing required flags"); + expect(result.stderrPlain).toContain("--target"); + } finally { + await fixture.cleanup(); + } + }); + + it("fails for unsupported language", async () => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "cobol", "--yes"], + cwd: fixture.path, + expectError: true + }); + expect(result.exitCode).not.toBe(0); + expect(result.stderrPlain).toContain('"cobol" is not a supported language'); + expect(result.stderrPlain).toContain("typescript"); + } finally { + await fixture.cleanup(); + } + }); + + it("fails for wrong casing of language", async () => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "TypeScript", "--yes"], + cwd: fixture.path, + expectError: true + }); + expect(result.exitCode).not.toBe(0); + expect(result.stderrPlain).toContain('"TypeScript" is not a supported language'); + } finally { + await fixture.cleanup(); + } + }); + + it("fails for invalid remote URL that is not a git URL", async () => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "typescript", "--output", "http://example.com/repo", "--yes"], + cwd: fixture.path, + expectError: true + }); + expect(result.exitCode).not.toBe(0); + expect(result.stderrPlain).toContain("looks like a remote reference"); + expect(result.stderrPlain).toContain("https://github.com"); + } finally { + await fixture.cleanup(); + } + }); + + it("fails when target already exists", async () => { + const fixture = await createTempFixture({ + "fern.yml": FERN_YML_WITH_TYPESCRIPT, + "openapi.yml": SIMPLE_OPENAPI + }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "typescript", "--yes"], + cwd: fixture.path, + expectError: true + }); + expect(result.exitCode).not.toBe(0); + expect(result.stderrPlain).toContain("Target 'typescript' already exists"); + } finally { + await fixture.cleanup(); + } + }); + + it("adds a target with default output path", async () => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "python", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const written = await readFile(fixture.path, "fern.yml"); + expect(written).toContain("python:"); + expect(written).toContain("path: ./sdks/python"); + } finally { + await fixture.cleanup(); + } + }); + + it("adds a target with custom output path", async () => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "go", "--output", "./custom/go", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const written = await readFile(fixture.path, "fern.yml"); + expect(written).toContain("go:"); + expect(written).toContain("path: ./custom/go"); + } finally { + await fixture.cleanup(); + } + }); + + it("adds a new target alongside an existing target", async () => { + const fixture = await createTempFixture({ + "fern.yml": FERN_YML_WITH_TYPESCRIPT, + "openapi.yml": SIMPLE_OPENAPI + }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "python", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const written = await readFile(fixture.path, "fern.yml"); + expect(written).toContain("typescript:"); + expect(written).toContain("python:"); + } finally { + await fixture.cleanup(); + } + }); + + it("creates sdks.targets section when absent", async () => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "typescript", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const written = await readFile(fixture.path, "fern.yml"); + expect(written).toContain("sdks:"); + expect(written).toContain("targets:"); + expect(written).toContain("typescript:"); + } finally { + await fixture.cleanup(); + } + }); + + it("adds a target with GitHub HTTPS URL", async () => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "typescript", "--output", "https://github.com/acme/ts-sdk", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const written = await readFile(fixture.path, "fern.yml"); + expect(written).toContain("git:"); + expect(written).toContain("repository: https://github.com/acme/ts-sdk"); + expect(written).not.toContain("path:"); + } finally { + await fixture.cleanup(); + } + }); + + it("adds a target with GitLab HTTPS URL", async () => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "python", "--output", "https://gitlab.com/acme/py-sdk", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const written = await readFile(fixture.path, "fern.yml"); + expect(written).toContain("repository: https://gitlab.com/acme/py-sdk"); + } finally { + await fixture.cleanup(); + } + }); + + it("adds a target with .git-suffixed URL", async () => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "java", "--output", "https://github.com/acme/java-sdk.git", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const written = await readFile(fixture.path, "fern.yml"); + expect(written).toContain("repository: https://github.com/acme/java-sdk.git"); + } finally { + await fixture.cleanup(); + } + }); + + it("adds a target with git@ SSH URL", async () => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "go", "--output", "git@github.com:acme/go-sdk.git", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const written = await readFile(fixture.path, "fern.yml"); + expect(written).toContain("git:"); + expect(written).toContain("repository: git@github.com:acme/go-sdk.git"); + } finally { + await fixture.cleanup(); + } + }); + + it("adds group when --group is specified", async () => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "typescript", "--group", "prod", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const written = await readFile(fixture.path, "fern.yml"); + expect(written).toContain("group:"); + expect(written).toContain("- prod"); + } finally { + await fixture.cleanup(); + } + }); + + it("does not add group when --group is not specified", async () => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "go", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const written = await readFile(fixture.path, "fern.yml"); + expect(written).not.toContain("group:"); + } finally { + await fixture.cleanup(); + } + }); + + it("preserves existing comments when adding a new target", async () => { + const fernYmlWithComments = ` +org: test-org + +# This is the API spec for our service +api: + specs: + - openapi: ./openapi.yml + +# SDK configuration +sdks: + # Existing target + targets: + typescript: + # version pinned for stability + output: + path: ./sdks/typescript +`; + const fixture = await createTempFixture({ "fern.yml": fernYmlWithComments, "openapi.yml": SIMPLE_OPENAPI }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "python", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const written = await readFile(fixture.path, "fern.yml"); + expect(written).toContain("# This is the API spec for our service"); + expect(written).toContain("# SDK configuration"); + expect(written).toContain("# Existing target"); + expect(written).toContain("# version pinned for stability"); + expect(written).toContain("python:"); + } finally { + await fixture.cleanup(); + } + }); + + it("preserves inline comments when adding a new target", async () => { + const fernYmlWithInlineComments = `org: test-org # the org name + +api: + specs: + - openapi: ./openapi.yml + +sdks: + targets: + typescript: # TypeScript SDK + output: + path: ./sdks/typescript # output directory +`; + const fixture = await createTempFixture({ + "fern.yml": fernYmlWithInlineComments, + "openapi.yml": SIMPLE_OPENAPI + }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "go", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const written = await readFile(fixture.path, "fern.yml"); + expect(written).toContain("# the org name"); + expect(written).toContain("# TypeScript SDK"); + expect(written).toContain("# output directory"); + expect(written).toContain("go:"); + } finally { + await fixture.cleanup(); + } + }); + + it("writes new target to $ref-referenced file", async () => { + const fernYmlWithRef = `org: test-org + +api: + specs: + - openapi: ./openapi.yml + +sdks: + $ref: ./sdks.yml +`; + const sdksYmlContent = `targets: + typescript: + output: + path: ./sdks/typescript +`; + const fixture = await createTempFixture({ + "fern.yml": fernYmlWithRef, + "sdks.yml": sdksYmlContent, + "openapi.yml": SIMPLE_OPENAPI + }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "python", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const sdksYmlWritten = await readFile(fixture.path, "sdks.yml"); + expect(sdksYmlWritten).toContain("python:"); + + const fernYmlWritten = await readFile(fixture.path, "fern.yml"); + expect(fernYmlWritten).toContain("$ref"); + expect(fernYmlWritten).not.toContain("python:"); + } finally { + await fixture.cleanup(); + } + }); + + it("preserves comments in $ref-referenced file", async () => { + const fernYmlWithRef = `org: test-org + +api: + specs: + - openapi: ./openapi.yml + +sdks: + $ref: ./sdks.yml +`; + const sdksYmlWithComments = `# SDK targets +targets: + # existing TypeScript SDK + typescript: + output: + path: ./sdks/typescript +`; + const fixture = await createTempFixture({ + "fern.yml": fernYmlWithRef, + "sdks.yml": sdksYmlWithComments, + "openapi.yml": SIMPLE_OPENAPI + }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "go", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const sdksYmlWritten = await readFile(fixture.path, "sdks.yml"); + expect(sdksYmlWritten).toContain("# SDK targets"); + expect(sdksYmlWritten).toContain("# existing TypeScript SDK"); + expect(sdksYmlWritten).toContain("go:"); + } finally { + await fixture.cleanup(); + } + }); + + it("fails on duplicate target in $ref-referenced file", async () => { + const fernYmlWithRef = `org: test-org + +api: + specs: + - openapi: ./openapi.yml + +sdks: + $ref: ./sdks.yml +`; + const sdksYmlContent = `targets: + python: + output: + path: ./sdks/python +`; + const fixture = await createTempFixture({ + "fern.yml": fernYmlWithRef, + "sdks.yml": sdksYmlContent, + "openapi.yml": SIMPLE_OPENAPI + }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "python", "--yes"], + cwd: fixture.path, + expectError: true + }); + expect(result.exitCode).not.toBe(0); + expect(result.stderrPlain).toContain("Target 'python' already exists"); + } finally { + await fixture.cleanup(); + } + }); + + it("produces valid YAML with correct structure for local path target", async () => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "csharp", "--output", "./sdks/csharp", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const written = await readFile(fixture.path, "fern.yml"); + const parsed = jsYaml.load(written) as Record; + const sdks = parsed.sdks as Record; + const targets = sdks.targets as Record; + const csharp = targets.csharp as Record; + const output = csharp.output as Record; + expect(output.path).toBe("./sdks/csharp"); + expect(csharp.version).toBeUndefined(); + expect(csharp.group).toBeUndefined(); + } finally { + await fixture.cleanup(); + } + }); + + it("produces valid YAML with correct structure for git target", async () => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "ruby", "--output", "https://github.com/acme/ruby-sdk", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const written = await readFile(fixture.path, "fern.yml"); + const parsed = jsYaml.load(written) as Record; + const sdks = parsed.sdks as Record; + const targets = sdks.targets as Record; + const ruby = targets.ruby as Record; + const output = ruby.output as Record; + const git = output.git as Record; + expect(git.repository).toBe("https://github.com/acme/ruby-sdk"); + expect(output.path).toBeUndefined(); + } finally { + await fixture.cleanup(); + } + }); + + it("produces valid YAML with correct group structure", async () => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", "swift", "--group", "mobile", "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const written = await readFile(fixture.path, "fern.yml"); + const parsed = jsYaml.load(written) as Record; + const sdks = parsed.sdks as Record; + const targets = sdks.targets as Record; + const swift = targets.swift as Record; + expect(Array.isArray(swift.group)).toBe(true); + expect((swift.group as string[])[0]).toBe("mobile"); + } finally { + await fixture.cleanup(); + } + }); + + it.each([ + "csharp", + "go", + "java", + "php", + "python", + "ruby", + "rust", + "swift", + "typescript" + ])("should accept language '%s'", async (lang) => { + const fixture = await createTempFixture({ "fern.yml": FERN_YML_NO_SDKS }); + try { + const result = await runCliV2({ + args: ["sdk", "add", "--target", lang, "--yes"], + cwd: fixture.path + }); + expect(result.exitCode).toBe(0); + + const written = await readFile(fixture.path, "fern.yml"); + expect(written).toContain(`${lang}:`); + } finally { + await fixture.cleanup(); + } + }); +}, 60_000); + +async function readFile(fixturePath: string, relPath: string): Promise { + return fs.readFile(path.join(fixturePath, relPath), "utf-8"); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0e36966453d4..8fbb838055a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5254,6 +5254,9 @@ importers: vitest: specifier: 'catalog:' version: 4.0.18(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3) + yaml: + specifier: 2.3.3 + version: 2.3.3 yargs: specifier: ^17.4.1 version: 17.7.2 From d29ee4a9e0fa879a3fd290043a1f53ff89939089 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 24 Feb 2026 19:59:01 -0500 Subject: [PATCH 05/20] chore(python): update python-sdk seed (#12740) Co-authored-by: fern-support --- .../additional_init_exports/reference.md | 79 ++++++++++++++++ .../additional_init_exports/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../aliases_with_validation/reference.md | 79 ++++++++++++++++ .../aliases_with_validation/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../aliases_without_validation/reference.md | 79 ++++++++++++++++ .../aliases_without_validation/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../exhaustive/custom-transport/reference.md | 79 ++++++++++++++++ .../exhaustive/custom-transport/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../deps_with_min_python_version/reference.md | 79 ++++++++++++++++ .../deps_with_min_python_version/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../exhaustive/eager-imports/reference.md | 79 ++++++++++++++++ .../exhaustive/eager-imports/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../extra_dependencies/reference.md | 79 ++++++++++++++++ .../extra_dependencies/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../extra_dev_dependencies/reference.md | 79 ++++++++++++++++ .../extra_dev_dependencies/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../five-second-timeout/reference.md | 79 ++++++++++++++++ .../five-second-timeout/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../follow_redirects_by_default/reference.md | 79 ++++++++++++++++ .../follow_redirects_by_default/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../exhaustive/import-paths/reference.md | 79 ++++++++++++++++ .../exhaustive/import-paths/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../exhaustive/improved_imports/reference.md | 79 ++++++++++++++++ .../exhaustive/improved_imports/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../exhaustive/infinite-timeout/reference.md | 79 ++++++++++++++++ .../exhaustive/infinite-timeout/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../inline-path-params/reference.md | 79 ++++++++++++++++ .../inline-path-params/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 87 ++++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../inline_request_params/reference.md | 79 ++++++++++++++++ .../inline_request_params/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../exhaustive/no-custom-config/reference.md | 79 ++++++++++++++++ .../exhaustive/no-custom-config/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../wiremock/wiremock-mappings.json | 2 +- .../exhaustive/package-path/reference.md | 79 ++++++++++++++++ .../exhaustive/package-path/snippet.json | 13 +++ .../doll/structure/endpoints/params/client.py | 85 +++++++++++++++++ .../structure/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../pydantic-extra-fields/reference.md | 79 ++++++++++++++++ .../pydantic-extra-fields/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../pydantic-ignore-fields/reference.md | 79 ++++++++++++++++ .../pydantic-ignore-fields/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../pydantic-v1-with-utils/reference.md | 79 ++++++++++++++++ .../pydantic-v1-with-utils/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../pydantic-v1-wrapped/reference.md | 79 ++++++++++++++++ .../pydantic-v1-wrapped/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../exhaustive/pydantic-v1/reference.md | 79 ++++++++++++++++ .../exhaustive/pydantic-v1/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../pydantic-v2-wrapped/reference.md | 79 ++++++++++++++++ .../pydantic-v2-wrapped/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../exhaustive/pyproject_extras/reference.md | 79 ++++++++++++++++ .../exhaustive/pyproject_extras/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../skip-pydantic-validation/reference.md | 79 ++++++++++++++++ .../skip-pydantic-validation/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../exhaustive/union-utils/reference.md | 79 ++++++++++++++++ .../exhaustive/union-utils/snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../reference.md | 79 ++++++++++++++++ .../snippet.json | 13 +++ .../src/seed/endpoints/params/client.py | 85 +++++++++++++++++ .../src/seed/endpoints/params/raw_client.py | 91 +++++++++++++++++++ .../wiremock/wiremock-mappings.json | 2 +- 110 files changed, 7240 insertions(+), 2 deletions(-) diff --git a/seed/python-sdk/exhaustive/additional_init_exports/reference.md b/seed/python-sdk/exhaustive/additional_init_exports/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/additional_init_exports/reference.md +++ b/seed/python-sdk/exhaustive/additional_init_exports/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/additional_init_exports/snippet.json b/seed/python-sdk/exhaustive/additional_init_exports/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/additional_init_exports/snippet.json +++ b/seed/python-sdk/exhaustive/additional_init_exports/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/additional_init_exports/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/additional_init_exports/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/additional_init_exports/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/additional_init_exports/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/additional_init_exports/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/additional_init_exports/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/additional_init_exports/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/additional_init_exports/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/aliases_with_validation/reference.md b/seed/python-sdk/exhaustive/aliases_with_validation/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/aliases_with_validation/reference.md +++ b/seed/python-sdk/exhaustive/aliases_with_validation/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/aliases_with_validation/snippet.json b/seed/python-sdk/exhaustive/aliases_with_validation/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/aliases_with_validation/snippet.json +++ b/seed/python-sdk/exhaustive/aliases_with_validation/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/aliases_with_validation/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/aliases_with_validation/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/aliases_with_validation/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/aliases_with_validation/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/aliases_with_validation/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/aliases_with_validation/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/aliases_with_validation/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/aliases_with_validation/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/aliases_without_validation/reference.md b/seed/python-sdk/exhaustive/aliases_without_validation/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/aliases_without_validation/reference.md +++ b/seed/python-sdk/exhaustive/aliases_without_validation/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/aliases_without_validation/snippet.json b/seed/python-sdk/exhaustive/aliases_without_validation/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/aliases_without_validation/snippet.json +++ b/seed/python-sdk/exhaustive/aliases_without_validation/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/aliases_without_validation/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/aliases_without_validation/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/aliases_without_validation/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/aliases_without_validation/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/aliases_without_validation/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/aliases_without_validation/src/seed/endpoints/params/raw_client.py index 055504f43712..09ad73be1ab0 100644 --- a/seed/python-sdk/exhaustive/aliases_without_validation/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/aliases_without_validation/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.request_options import RequestOptions from ...core.unchecked_base_model import construct_type +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + construct_type( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + construct_type( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/custom-transport/reference.md b/seed/python-sdk/exhaustive/custom-transport/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/custom-transport/reference.md +++ b/seed/python-sdk/exhaustive/custom-transport/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/custom-transport/snippet.json b/seed/python-sdk/exhaustive/custom-transport/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/custom-transport/snippet.json +++ b/seed/python-sdk/exhaustive/custom-transport/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/custom-transport/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/custom-transport/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/custom-transport/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/custom-transport/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/custom-transport/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/custom-transport/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/custom-transport/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/custom-transport/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/deps_with_min_python_version/reference.md b/seed/python-sdk/exhaustive/deps_with_min_python_version/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/deps_with_min_python_version/reference.md +++ b/seed/python-sdk/exhaustive/deps_with_min_python_version/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/deps_with_min_python_version/snippet.json b/seed/python-sdk/exhaustive/deps_with_min_python_version/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/deps_with_min_python_version/snippet.json +++ b/seed/python-sdk/exhaustive/deps_with_min_python_version/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/deps_with_min_python_version/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/deps_with_min_python_version/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/deps_with_min_python_version/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/deps_with_min_python_version/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/deps_with_min_python_version/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/deps_with_min_python_version/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/deps_with_min_python_version/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/deps_with_min_python_version/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/eager-imports/reference.md b/seed/python-sdk/exhaustive/eager-imports/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/eager-imports/reference.md +++ b/seed/python-sdk/exhaustive/eager-imports/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/eager-imports/snippet.json b/seed/python-sdk/exhaustive/eager-imports/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/eager-imports/snippet.json +++ b/seed/python-sdk/exhaustive/eager-imports/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/eager-imports/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/eager-imports/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/eager-imports/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/eager-imports/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/eager-imports/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/eager-imports/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/eager-imports/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/eager-imports/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/extra_dependencies/reference.md b/seed/python-sdk/exhaustive/extra_dependencies/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/extra_dependencies/reference.md +++ b/seed/python-sdk/exhaustive/extra_dependencies/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/extra_dependencies/snippet.json b/seed/python-sdk/exhaustive/extra_dependencies/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/extra_dependencies/snippet.json +++ b/seed/python-sdk/exhaustive/extra_dependencies/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/extra_dependencies/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/extra_dependencies/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/extra_dependencies/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/extra_dependencies/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/extra_dependencies/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/extra_dependencies/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/extra_dependencies/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/extra_dependencies/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/extra_dev_dependencies/reference.md b/seed/python-sdk/exhaustive/extra_dev_dependencies/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/extra_dev_dependencies/reference.md +++ b/seed/python-sdk/exhaustive/extra_dev_dependencies/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/extra_dev_dependencies/snippet.json b/seed/python-sdk/exhaustive/extra_dev_dependencies/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/extra_dev_dependencies/snippet.json +++ b/seed/python-sdk/exhaustive/extra_dev_dependencies/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/extra_dev_dependencies/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/extra_dev_dependencies/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/extra_dev_dependencies/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/extra_dev_dependencies/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/extra_dev_dependencies/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/extra_dev_dependencies/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/extra_dev_dependencies/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/extra_dev_dependencies/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/five-second-timeout/reference.md b/seed/python-sdk/exhaustive/five-second-timeout/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/five-second-timeout/reference.md +++ b/seed/python-sdk/exhaustive/five-second-timeout/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/five-second-timeout/snippet.json b/seed/python-sdk/exhaustive/five-second-timeout/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/five-second-timeout/snippet.json +++ b/seed/python-sdk/exhaustive/five-second-timeout/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/five-second-timeout/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/five-second-timeout/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/five-second-timeout/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/five-second-timeout/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/five-second-timeout/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/five-second-timeout/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/five-second-timeout/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/five-second-timeout/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/follow_redirects_by_default/reference.md b/seed/python-sdk/exhaustive/follow_redirects_by_default/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/follow_redirects_by_default/reference.md +++ b/seed/python-sdk/exhaustive/follow_redirects_by_default/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/follow_redirects_by_default/snippet.json b/seed/python-sdk/exhaustive/follow_redirects_by_default/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/follow_redirects_by_default/snippet.json +++ b/seed/python-sdk/exhaustive/follow_redirects_by_default/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/follow_redirects_by_default/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/follow_redirects_by_default/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/follow_redirects_by_default/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/follow_redirects_by_default/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/follow_redirects_by_default/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/follow_redirects_by_default/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/follow_redirects_by_default/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/follow_redirects_by_default/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/import-paths/reference.md b/seed/python-sdk/exhaustive/import-paths/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/import-paths/reference.md +++ b/seed/python-sdk/exhaustive/import-paths/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/import-paths/snippet.json b/seed/python-sdk/exhaustive/import-paths/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/import-paths/snippet.json +++ b/seed/python-sdk/exhaustive/import-paths/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/import-paths/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/import-paths/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/import-paths/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/import-paths/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/import-paths/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/import-paths/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/import-paths/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/import-paths/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/improved_imports/reference.md b/seed/python-sdk/exhaustive/improved_imports/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/improved_imports/reference.md +++ b/seed/python-sdk/exhaustive/improved_imports/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/improved_imports/snippet.json b/seed/python-sdk/exhaustive/improved_imports/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/improved_imports/snippet.json +++ b/seed/python-sdk/exhaustive/improved_imports/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/improved_imports/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/improved_imports/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/improved_imports/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/improved_imports/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/improved_imports/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/improved_imports/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/improved_imports/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/improved_imports/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/infinite-timeout/reference.md b/seed/python-sdk/exhaustive/infinite-timeout/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/infinite-timeout/reference.md +++ b/seed/python-sdk/exhaustive/infinite-timeout/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/infinite-timeout/snippet.json b/seed/python-sdk/exhaustive/infinite-timeout/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/infinite-timeout/snippet.json +++ b/seed/python-sdk/exhaustive/infinite-timeout/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/infinite-timeout/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/infinite-timeout/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/infinite-timeout/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/infinite-timeout/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/infinite-timeout/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/infinite-timeout/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/infinite-timeout/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/infinite-timeout/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/inline-path-params/reference.md b/seed/python-sdk/exhaustive/inline-path-params/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/inline-path-params/reference.md +++ b/seed/python-sdk/exhaustive/inline-path-params/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/inline-path-params/snippet.json b/seed/python-sdk/exhaustive/inline-path-params/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/inline-path-params/snippet.json +++ b/seed/python-sdk/exhaustive/inline-path-params/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/inline-path-params/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/inline-path-params/src/seed/endpoints/params/client.py index 6d959147622d..60ee1677db74 100644 --- a/seed/python-sdk/exhaustive/inline-path-params/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/inline-path-params/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -305,6 +306,44 @@ def modify_with_inline_path( ) return _response.data + def upload_with_path( + self, + *, + param: str, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param=param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -668,3 +707,51 @@ async def main() -> None: param=param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + *, + param: str, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path( + param=param, request=request, request_options=request_options + ) + return _response.data diff --git a/seed/python-sdk/exhaustive/inline-path-params/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/inline-path-params/src/seed/endpoints/params/raw_client.py index 8c4c42b6d151..85e7a99e77ba 100644 --- a/seed/python-sdk/exhaustive/inline-path-params/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/inline-path-params/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + *, + param: str, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + *, + param: str, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/inline_request_params/reference.md b/seed/python-sdk/exhaustive/inline_request_params/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/inline_request_params/reference.md +++ b/seed/python-sdk/exhaustive/inline_request_params/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/inline_request_params/snippet.json b/seed/python-sdk/exhaustive/inline_request_params/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/inline_request_params/snippet.json +++ b/seed/python-sdk/exhaustive/inline_request_params/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/inline_request_params/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/inline_request_params/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/inline_request_params/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/inline_request_params/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/inline_request_params/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/inline_request_params/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/inline_request_params/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/inline_request_params/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/no-custom-config/reference.md b/seed/python-sdk/exhaustive/no-custom-config/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/no-custom-config/reference.md +++ b/seed/python-sdk/exhaustive/no-custom-config/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/no-custom-config/snippet.json b/seed/python-sdk/exhaustive/no-custom-config/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/no-custom-config/snippet.json +++ b/seed/python-sdk/exhaustive/no-custom-config/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/no-custom-config/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/no-custom-config/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/no-custom-config/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/no-custom-config/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/no-custom-config/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/no-custom-config/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/no-custom-config/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/no-custom-config/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/no-custom-config/wiremock/wiremock-mappings.json b/seed/python-sdk/exhaustive/no-custom-config/wiremock/wiremock-mappings.json index 4916bea9a994..8ffc6a19b116 100644 --- a/seed/python-sdk/exhaustive/no-custom-config/wiremock/wiremock-mappings.json +++ b/seed/python-sdk/exhaustive/no-custom-config/wiremock/wiremock-mappings.json @@ -1 +1 @@ -{"mappings":[{"id":"864d58e6-0c08-4f38-b7e1-2638f8d7fd6d","name":"getAndReturnListOfPrimitives - default","request":{"urlPathTemplate":"/container/list-of-primitives","method":"POST"},"response":{"status":200,"body":"[\n \"string\",\n \"string\"\n]","headers":{"Content-Type":"application/json"}},"uuid":"864d58e6-0c08-4f38-b7e1-2638f8d7fd6d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"ac1d4c4f-a8a7-4c27-ae64-7fc977cfa122","name":"getAndReturnListOfObjects - default","request":{"urlPathTemplate":"/container/list-of-objects","method":"POST"},"response":{"status":200,"body":"[\n {\n \"string\": \"string\"\n },\n {\n \"string\": \"string\"\n }\n]","headers":{"Content-Type":"application/json"}},"uuid":"ac1d4c4f-a8a7-4c27-ae64-7fc977cfa122","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"591d5c48-a536-452b-8a2e-ad7c23c38298","name":"getAndReturnSetOfPrimitives - default","request":{"urlPathTemplate":"/container/set-of-primitives","method":"POST"},"response":{"status":200,"body":"[\n \"string\"\n]","headers":{"Content-Type":"application/json"}},"uuid":"591d5c48-a536-452b-8a2e-ad7c23c38298","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e1d5f52b-7a51-464f-ac8f-83c0345a3a35","name":"getAndReturnSetOfObjects - default","request":{"urlPathTemplate":"/container/set-of-objects","method":"POST"},"response":{"status":200,"body":"[\n {\n \"string\": \"string\"\n }\n]","headers":{"Content-Type":"application/json"}},"uuid":"e1d5f52b-7a51-464f-ac8f-83c0345a3a35","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4b1d33b3-ca7d-462a-a2e3-23d531ae2922","name":"getAndReturnMapPrimToPrim - default","request":{"urlPathTemplate":"/container/map-prim-to-prim","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"4b1d33b3-ca7d-462a-a2e3-23d531ae2922","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b01ac2b9-3470-48aa-badc-57d331bb5a49","name":"getAndReturnMapOfPrimToObject - default","request":{"urlPathTemplate":"/container/map-prim-to-object","method":"POST"},"response":{"status":200,"body":"{\n \"string\": {\n \"string\": \"string\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"b01ac2b9-3470-48aa-badc-57d331bb5a49","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"eaf9315d-55c4-4434-8004-a70c25af5656","name":"getAndReturnMapOfPrimToUndiscriminatedUnion - default","request":{"urlPathTemplate":"/container/map-prim-to-union","method":"POST"},"response":{"status":200,"body":"{\n \"string\": 1.1\n}","headers":{"Content-Type":"application/json"}},"uuid":"eaf9315d-55c4-4434-8004-a70c25af5656","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e5271904-de0a-425f-940d-d6f6bde34755","name":"getAndReturnOptional - default","request":{"urlPathTemplate":"/container/opt-objects","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"e5271904-de0a-425f-940d-d6f6bde34755","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d19e1fbe-79cc-465c-962e-e1866ca2361b","name":"postJsonPatchContentType - default","request":{"urlPathTemplate":"/foo/bar","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"d19e1fbe-79cc-465c-962e-e1866ca2361b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4a107cf5-6284-48f8-9ddb-99d944ba989b","name":"postJsonPatchContentWithCharsetType - default","request":{"urlPathTemplate":"/foo/baz","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"4a107cf5-6284-48f8-9ddb-99d944ba989b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"60fd3c8a-3983-41b9-8178-f42997388900","name":"getAndReturnEnum - default","request":{"urlPathTemplate":"/enum","method":"POST"},"response":{"status":200,"body":"\"SUNNY\"","headers":{"Content-Type":"application/json"}},"uuid":"60fd3c8a-3983-41b9-8178-f42997388900","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"3d95c052-db6e-4eff-95f2-895666a5af54","name":"testGet - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"GET","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"3d95c052-db6e-4eff-95f2-895666a5af54","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"2552873f-9f1d-4557-a6c7-6c6b7a55b566","name":"testPost - default","request":{"urlPathTemplate":"/http-methods","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"2552873f-9f1d-4557-a6c7-6c6b7a55b566","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"db42fbdf-5426-41b7-b7ea-28ed39a38e82","name":"testPut - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"PUT","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"db42fbdf-5426-41b7-b7ea-28ed39a38e82","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c9def317-32a0-4bc0-b9b6-5efae8ca44a6","name":"testPatch - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"PATCH","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"c9def317-32a0-4bc0-b9b6-5efae8ca44a6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"05333635-c9ce-4c1a-bb6d-95e8a0fb80dc","name":"testDelete - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"DELETE","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"05333635-c9ce-4c1a-bb6d-95e8a0fb80dc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"41cdef0e-040f-4d08-8426-87b19e60f7d7","name":"getAndReturnWithOptionalField - default","request":{"urlPathTemplate":"/object/get-and-return-with-optional-field","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"41cdef0e-040f-4d08-8426-87b19e60f7d7","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"f74df550-df8c-4503-b202-0b9b3165c1a7","name":"getAndReturnWithRequiredField - default","request":{"urlPathTemplate":"/object/get-and-return-with-required-field","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"f74df550-df8c-4503-b202-0b9b3165c1a7","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0c904dbb-ce54-48a2-8364-13a4989be7f2","name":"getAndReturnWithMapOfMap - default","request":{"urlPathTemplate":"/object/get-and-return-with-map-of-map","method":"POST"},"response":{"status":200,"body":"{\n \"map\": {\n \"map\": {\n \"map\": \"map\"\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"0c904dbb-ce54-48a2-8364-13a4989be7f2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"67e969fc-ff81-4a67-9858-66a29ffc9b72","name":"getAndReturnNestedWithOptionalField - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-optional-field","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"67e969fc-ff81-4a67-9858-66a29ffc9b72","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"169827d0-4247-4236-8cef-34b94d2659de","name":"getAndReturnNestedWithRequiredField - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-required-field/{string}","method":"POST","pathParameters":{"string":{"equalTo":"string"}}},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"169827d0-4247-4236-8cef-34b94d2659de","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6d9ed308-5724-4bbc-a4f7-28dc264d188f","name":"getAndReturnNestedWithRequiredFieldAsList - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-required-field-list","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"6d9ed308-5724-4bbc-a4f7-28dc264d188f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e2cc3e92-6e37-4a50-9081-23952fdd73fe","name":"getAndReturnWithDatetimeLikeString - default","request":{"urlPathTemplate":"/object/get-and-return-with-datetime-like-string","method":"POST"},"response":{"status":200,"body":"{\n \"datetimeLikeString\": \"2023-08-31T14:15:22Z\",\n \"actualDatetime\": \"2023-08-31T14:15:22Z\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"e2cc3e92-6e37-4a50-9081-23952fdd73fe","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"f15b2079-2482-4ed2-951d-d2b5c4de9afd","name":"listItems - default","request":{"urlPathTemplate":"/pagination","method":"GET"},"response":{"status":200,"body":"{\n \"items\": [\n {\n \"string\": \"string\"\n },\n {\n \"string\": \"string\"\n }\n ],\n \"next\": \"next\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"f15b2079-2482-4ed2-951d-d2b5c4de9afd","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"97806fdb-f31f-4f90-84b8-f9cc1713d53d","name":"getWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"97806fdb-f31f-4f90-84b8-f9cc1713d53d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"711fc64f-4af9-4084-8c29-1e7a9e58be70","name":"getWithInlinePath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"711fc64f-4af9-4084-8c29-1e7a9e58be70","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8e5739b3-d75f-47d7-b6b8-a663d91a66b5","name":"getWithQuery - default","request":{"urlPathTemplate":"/params","method":"GET"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"8e5739b3-d75f-47d7-b6b8-a663d91a66b5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"4125d349-732b-4ff7-948d-1eeb977ed13b","name":"getWithAllowMultipleQuery - default","request":{"urlPathTemplate":"/params","method":"GET"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"4125d349-732b-4ff7-948d-1eeb977ed13b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"1933d96a-a1b9-4cf1-85ba-c1e8eff5bd56","name":"getWithPathAndQuery - default","request":{"urlPathTemplate":"/params/path-query/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"1933d96a-a1b9-4cf1-85ba-c1e8eff5bd56","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c4f5012a-fb3f-45ac-b695-beedc3353ad8","name":"getWithInlinePathAndQuery - default","request":{"urlPathTemplate":"/params/path-query/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"c4f5012a-fb3f-45ac-b695-beedc3353ad8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"046bf7d6-751b-48e9-bfc6-ff74a17e88e1","name":"modifyWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"PUT","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"046bf7d6-751b-48e9-bfc6-ff74a17e88e1","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6bfc7195-b99c-4449-bf47-7c4f74f6f33b","name":"modifyWithInlinePath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"PUT","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"6bfc7195-b99c-4449-bf47-7c4f74f6f33b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0506ae7c-87b7-4cd2-9e50-31d60f81893f","name":"getAndReturnString - default","request":{"urlPathTemplate":"/primitive/string","method":"POST"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"0506ae7c-87b7-4cd2-9e50-31d60f81893f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"67f875d1-e19e-440d-8f0c-7e1d24ef2619","name":"getAndReturnInt - default","request":{"urlPathTemplate":"/primitive/integer","method":"POST"},"response":{"status":200,"body":"1","headers":{"Content-Type":"application/json"}},"uuid":"67f875d1-e19e-440d-8f0c-7e1d24ef2619","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"5fadbebd-86c0-41f9-8be5-864e39eb5924","name":"getAndReturnLong - default","request":{"urlPathTemplate":"/primitive/long","method":"POST"},"response":{"status":200,"body":"1000000","headers":{"Content-Type":"application/json"}},"uuid":"5fadbebd-86c0-41f9-8be5-864e39eb5924","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e03872b6-86b1-490b-9195-86e5d3a014f2","name":"getAndReturnDouble - default","request":{"urlPathTemplate":"/primitive/double","method":"POST"},"response":{"status":200,"body":"1.1","headers":{"Content-Type":"application/json"}},"uuid":"e03872b6-86b1-490b-9195-86e5d3a014f2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"442e632f-890a-4105-9448-f7015127e3b4","name":"getAndReturnBool - default","request":{"urlPathTemplate":"/primitive/boolean","method":"POST"},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"442e632f-890a-4105-9448-f7015127e3b4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"ad76fa81-1e63-43fd-9a99-1d5e4339d98c","name":"getAndReturnDatetime - default","request":{"urlPathTemplate":"/primitive/datetime","method":"POST"},"response":{"status":200,"body":"\"2024-01-15T09:30:00Z\"","headers":{"Content-Type":"application/json"}},"uuid":"ad76fa81-1e63-43fd-9a99-1d5e4339d98c","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"afb524b1-54ab-4674-8446-fdd574c368cc","name":"getAndReturnDate - default","request":{"urlPathTemplate":"/primitive/date","method":"POST"},"response":{"status":200,"body":"\"2023-01-15\"","headers":{"Content-Type":"application/json"}},"uuid":"afb524b1-54ab-4674-8446-fdd574c368cc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"969c0f3a-218a-45b7-b17f-64b1a9307d43","name":"getAndReturnUUID - default","request":{"urlPathTemplate":"/primitive/uuid","method":"POST"},"response":{"status":200,"body":"\"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\"","headers":{"Content-Type":"application/json"}},"uuid":"969c0f3a-218a-45b7-b17f-64b1a9307d43","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"04baa20d-d318-40e6-9784-c40158c16acd","name":"getAndReturnBase64 - default","request":{"urlPathTemplate":"/primitive/base64","method":"POST"},"response":{"status":200,"body":"\"SGVsbG8gd29ybGQh\"","headers":{"Content-Type":"application/json"}},"uuid":"04baa20d-d318-40e6-9784-c40158c16acd","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"fddd5aaf-ab0d-4ef6-949c-5d329f6d7eb2","name":"Put - default","request":{"urlPathTemplate":"/{id}","method":"PUT","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\n \"errors\": [\n {\n \"category\": \"API_ERROR\",\n \"code\": \"INTERNAL_SERVER_ERROR\",\n \"detail\": \"detail\",\n \"field\": \"field\"\n },\n {\n \"category\": \"API_ERROR\",\n \"code\": \"INTERNAL_SERVER_ERROR\",\n \"detail\": \"detail\",\n \"field\": \"field\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"fddd5aaf-ab0d-4ef6-949c-5d329f6d7eb2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d29330f9-49ff-49c4-9081-7c83dbad315b","name":"getAndReturnUnion - default","request":{"urlPathTemplate":"/union","method":"POST"},"response":{"status":200,"body":"{\n \"animal\": \"dog\",\n \"name\": \"name\",\n \"likesToWoof\": true\n}","headers":{"Content-Type":"application/json"}},"uuid":"d29330f9-49ff-49c4-9081-7c83dbad315b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"343f71f3-36ce-4684-b762-d60a086b43a4","name":"withMixedCase - default","request":{"urlPathTemplate":"/urls/MixedCase","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"343f71f3-36ce-4684-b762-d60a086b43a4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"48f36314-b2b7-4910-9e1d-5b05f3346a60","name":"noEndingSlash - default","request":{"urlPathTemplate":"/urls/no-ending-slash","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"48f36314-b2b7-4910-9e1d-5b05f3346a60","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"f7b95029-2f25-4f70-8b4c-0855712747d8","name":"withEndingSlash - default","request":{"urlPathTemplate":"/urls/with-ending-slash/","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"f7b95029-2f25-4f70-8b4c-0855712747d8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"2f9671a5-e6da-43a8-be27-d0be0191dcbf","name":"withUnderscores - default","request":{"urlPathTemplate":"/urls/with_underscores","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"2f9671a5-e6da-43a8-be27-d0be0191dcbf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"4a6d0aaa-05f2-47bb-9f40-6e3743b9d2bf","name":"postWithObjectBodyandResponse - default","request":{"urlPathTemplate":"/req-bodies/object","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"4a6d0aaa-05f2-47bb-9f40-6e3743b9d2bf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c6e7cc6c-b76f-4860-a9ad-52dc8f55b6e6","name":"postWithNoAuth - default","request":{"urlPathTemplate":"/no-auth","method":"POST"},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"c6e7cc6c-b76f-4860-a9ad-52dc8f55b6e6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8098eeea-bc6b-4068-9601-566c2092f83f","name":"getWithNoRequestBody - default","request":{"urlPathTemplate":"/no-req-body","method":"GET"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"8098eeea-bc6b-4068-9601-566c2092f83f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"7dd9f944-1b35-42e9-a5ed-e48214ac8e91","name":"postWithNoRequestBody - default","request":{"urlPathTemplate":"/no-req-body","method":"POST"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"7dd9f944-1b35-42e9-a5ed-e48214ac8e91","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d7b54168-aef4-4d68-a9c5-446e97dee2fc","name":"getWithCustomHeader - default","request":{"urlPathTemplate":"/test-headers/custom-header","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"d7b54168-aef4-4d68-a9c5-446e97dee2fc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}}],"meta":{"total":52}} \ No newline at end of file +{"mappings":[{"id":"864d58e6-0c08-4f38-b7e1-2638f8d7fd6d","name":"getAndReturnListOfPrimitives - default","request":{"urlPathTemplate":"/container/list-of-primitives","method":"POST"},"response":{"status":200,"body":"[\n \"string\",\n \"string\"\n]","headers":{"Content-Type":"application/json"}},"uuid":"864d58e6-0c08-4f38-b7e1-2638f8d7fd6d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"ac1d4c4f-a8a7-4c27-ae64-7fc977cfa122","name":"getAndReturnListOfObjects - default","request":{"urlPathTemplate":"/container/list-of-objects","method":"POST"},"response":{"status":200,"body":"[\n {\n \"string\": \"string\"\n },\n {\n \"string\": \"string\"\n }\n]","headers":{"Content-Type":"application/json"}},"uuid":"ac1d4c4f-a8a7-4c27-ae64-7fc977cfa122","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"591d5c48-a536-452b-8a2e-ad7c23c38298","name":"getAndReturnSetOfPrimitives - default","request":{"urlPathTemplate":"/container/set-of-primitives","method":"POST"},"response":{"status":200,"body":"[\n \"string\"\n]","headers":{"Content-Type":"application/json"}},"uuid":"591d5c48-a536-452b-8a2e-ad7c23c38298","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e1d5f52b-7a51-464f-ac8f-83c0345a3a35","name":"getAndReturnSetOfObjects - default","request":{"urlPathTemplate":"/container/set-of-objects","method":"POST"},"response":{"status":200,"body":"[\n {\n \"string\": \"string\"\n }\n]","headers":{"Content-Type":"application/json"}},"uuid":"e1d5f52b-7a51-464f-ac8f-83c0345a3a35","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4b1d33b3-ca7d-462a-a2e3-23d531ae2922","name":"getAndReturnMapPrimToPrim - default","request":{"urlPathTemplate":"/container/map-prim-to-prim","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"4b1d33b3-ca7d-462a-a2e3-23d531ae2922","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b01ac2b9-3470-48aa-badc-57d331bb5a49","name":"getAndReturnMapOfPrimToObject - default","request":{"urlPathTemplate":"/container/map-prim-to-object","method":"POST"},"response":{"status":200,"body":"{\n \"string\": {\n \"string\": \"string\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"b01ac2b9-3470-48aa-badc-57d331bb5a49","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"eaf9315d-55c4-4434-8004-a70c25af5656","name":"getAndReturnMapOfPrimToUndiscriminatedUnion - default","request":{"urlPathTemplate":"/container/map-prim-to-union","method":"POST"},"response":{"status":200,"body":"{\n \"string\": 1.1\n}","headers":{"Content-Type":"application/json"}},"uuid":"eaf9315d-55c4-4434-8004-a70c25af5656","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e5271904-de0a-425f-940d-d6f6bde34755","name":"getAndReturnOptional - default","request":{"urlPathTemplate":"/container/opt-objects","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"e5271904-de0a-425f-940d-d6f6bde34755","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d19e1fbe-79cc-465c-962e-e1866ca2361b","name":"postJsonPatchContentType - default","request":{"urlPathTemplate":"/foo/bar","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"d19e1fbe-79cc-465c-962e-e1866ca2361b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4a107cf5-6284-48f8-9ddb-99d944ba989b","name":"postJsonPatchContentWithCharsetType - default","request":{"urlPathTemplate":"/foo/baz","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"4a107cf5-6284-48f8-9ddb-99d944ba989b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"60fd3c8a-3983-41b9-8178-f42997388900","name":"getAndReturnEnum - default","request":{"urlPathTemplate":"/enum","method":"POST"},"response":{"status":200,"body":"\"SUNNY\"","headers":{"Content-Type":"application/json"}},"uuid":"60fd3c8a-3983-41b9-8178-f42997388900","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"3d95c052-db6e-4eff-95f2-895666a5af54","name":"testGet - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"GET","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"3d95c052-db6e-4eff-95f2-895666a5af54","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"2552873f-9f1d-4557-a6c7-6c6b7a55b566","name":"testPost - default","request":{"urlPathTemplate":"/http-methods","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"2552873f-9f1d-4557-a6c7-6c6b7a55b566","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"db42fbdf-5426-41b7-b7ea-28ed39a38e82","name":"testPut - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"PUT","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"db42fbdf-5426-41b7-b7ea-28ed39a38e82","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c9def317-32a0-4bc0-b9b6-5efae8ca44a6","name":"testPatch - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"PATCH","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"c9def317-32a0-4bc0-b9b6-5efae8ca44a6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"05333635-c9ce-4c1a-bb6d-95e8a0fb80dc","name":"testDelete - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"DELETE","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"05333635-c9ce-4c1a-bb6d-95e8a0fb80dc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"41cdef0e-040f-4d08-8426-87b19e60f7d7","name":"getAndReturnWithOptionalField - default","request":{"urlPathTemplate":"/object/get-and-return-with-optional-field","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"41cdef0e-040f-4d08-8426-87b19e60f7d7","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"f74df550-df8c-4503-b202-0b9b3165c1a7","name":"getAndReturnWithRequiredField - default","request":{"urlPathTemplate":"/object/get-and-return-with-required-field","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"f74df550-df8c-4503-b202-0b9b3165c1a7","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0c904dbb-ce54-48a2-8364-13a4989be7f2","name":"getAndReturnWithMapOfMap - default","request":{"urlPathTemplate":"/object/get-and-return-with-map-of-map","method":"POST"},"response":{"status":200,"body":"{\n \"map\": {\n \"map\": {\n \"map\": \"map\"\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"0c904dbb-ce54-48a2-8364-13a4989be7f2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"67e969fc-ff81-4a67-9858-66a29ffc9b72","name":"getAndReturnNestedWithOptionalField - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-optional-field","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"67e969fc-ff81-4a67-9858-66a29ffc9b72","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"169827d0-4247-4236-8cef-34b94d2659de","name":"getAndReturnNestedWithRequiredField - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-required-field/{string}","method":"POST","pathParameters":{"string":{"equalTo":"string"}}},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"169827d0-4247-4236-8cef-34b94d2659de","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6d9ed308-5724-4bbc-a4f7-28dc264d188f","name":"getAndReturnNestedWithRequiredFieldAsList - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-required-field-list","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"6d9ed308-5724-4bbc-a4f7-28dc264d188f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e2cc3e92-6e37-4a50-9081-23952fdd73fe","name":"getAndReturnWithDatetimeLikeString - default","request":{"urlPathTemplate":"/object/get-and-return-with-datetime-like-string","method":"POST"},"response":{"status":200,"body":"{\n \"datetimeLikeString\": \"2023-08-31T14:15:22Z\",\n \"actualDatetime\": \"2023-08-31T14:15:22Z\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"e2cc3e92-6e37-4a50-9081-23952fdd73fe","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"f15b2079-2482-4ed2-951d-d2b5c4de9afd","name":"listItems - default","request":{"urlPathTemplate":"/pagination","method":"GET"},"response":{"status":200,"body":"{\n \"items\": [\n {\n \"string\": \"string\"\n },\n {\n \"string\": \"string\"\n }\n ],\n \"next\": \"next\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"f15b2079-2482-4ed2-951d-d2b5c4de9afd","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"97806fdb-f31f-4f90-84b8-f9cc1713d53d","name":"getWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"97806fdb-f31f-4f90-84b8-f9cc1713d53d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"711fc64f-4af9-4084-8c29-1e7a9e58be70","name":"getWithInlinePath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"711fc64f-4af9-4084-8c29-1e7a9e58be70","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8e5739b3-d75f-47d7-b6b8-a663d91a66b5","name":"getWithQuery - default","request":{"urlPathTemplate":"/params","method":"GET"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"8e5739b3-d75f-47d7-b6b8-a663d91a66b5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"4125d349-732b-4ff7-948d-1eeb977ed13b","name":"getWithAllowMultipleQuery - default","request":{"urlPathTemplate":"/params","method":"GET"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"4125d349-732b-4ff7-948d-1eeb977ed13b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"1933d96a-a1b9-4cf1-85ba-c1e8eff5bd56","name":"getWithPathAndQuery - default","request":{"urlPathTemplate":"/params/path-query/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"1933d96a-a1b9-4cf1-85ba-c1e8eff5bd56","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c4f5012a-fb3f-45ac-b695-beedc3353ad8","name":"getWithInlinePathAndQuery - default","request":{"urlPathTemplate":"/params/path-query/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"c4f5012a-fb3f-45ac-b695-beedc3353ad8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"046bf7d6-751b-48e9-bfc6-ff74a17e88e1","name":"modifyWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"PUT","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"046bf7d6-751b-48e9-bfc6-ff74a17e88e1","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6bfc7195-b99c-4449-bf47-7c4f74f6f33b","name":"modifyWithInlinePath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"PUT","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"6bfc7195-b99c-4449-bf47-7c4f74f6f33b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"658d6145-14f6-48d3-9c66-c63e01eb6fd2","name":"uploadWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"POST","pathParameters":{"param":{"equalTo":"upload-path"}}},"response":{"status":200,"body":"{\n \"string\": \"uploaded\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"658d6145-14f6-48d3-9c66-c63e01eb6fd2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0506ae7c-87b7-4cd2-9e50-31d60f81893f","name":"getAndReturnString - default","request":{"urlPathTemplate":"/primitive/string","method":"POST"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"0506ae7c-87b7-4cd2-9e50-31d60f81893f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"67f875d1-e19e-440d-8f0c-7e1d24ef2619","name":"getAndReturnInt - default","request":{"urlPathTemplate":"/primitive/integer","method":"POST"},"response":{"status":200,"body":"1","headers":{"Content-Type":"application/json"}},"uuid":"67f875d1-e19e-440d-8f0c-7e1d24ef2619","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"5fadbebd-86c0-41f9-8be5-864e39eb5924","name":"getAndReturnLong - default","request":{"urlPathTemplate":"/primitive/long","method":"POST"},"response":{"status":200,"body":"1000000","headers":{"Content-Type":"application/json"}},"uuid":"5fadbebd-86c0-41f9-8be5-864e39eb5924","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e03872b6-86b1-490b-9195-86e5d3a014f2","name":"getAndReturnDouble - default","request":{"urlPathTemplate":"/primitive/double","method":"POST"},"response":{"status":200,"body":"1.1","headers":{"Content-Type":"application/json"}},"uuid":"e03872b6-86b1-490b-9195-86e5d3a014f2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"442e632f-890a-4105-9448-f7015127e3b4","name":"getAndReturnBool - default","request":{"urlPathTemplate":"/primitive/boolean","method":"POST"},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"442e632f-890a-4105-9448-f7015127e3b4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"ad76fa81-1e63-43fd-9a99-1d5e4339d98c","name":"getAndReturnDatetime - default","request":{"urlPathTemplate":"/primitive/datetime","method":"POST"},"response":{"status":200,"body":"\"2024-01-15T09:30:00Z\"","headers":{"Content-Type":"application/json"}},"uuid":"ad76fa81-1e63-43fd-9a99-1d5e4339d98c","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"afb524b1-54ab-4674-8446-fdd574c368cc","name":"getAndReturnDate - default","request":{"urlPathTemplate":"/primitive/date","method":"POST"},"response":{"status":200,"body":"\"2023-01-15\"","headers":{"Content-Type":"application/json"}},"uuid":"afb524b1-54ab-4674-8446-fdd574c368cc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"969c0f3a-218a-45b7-b17f-64b1a9307d43","name":"getAndReturnUUID - default","request":{"urlPathTemplate":"/primitive/uuid","method":"POST"},"response":{"status":200,"body":"\"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\"","headers":{"Content-Type":"application/json"}},"uuid":"969c0f3a-218a-45b7-b17f-64b1a9307d43","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"04baa20d-d318-40e6-9784-c40158c16acd","name":"getAndReturnBase64 - default","request":{"urlPathTemplate":"/primitive/base64","method":"POST"},"response":{"status":200,"body":"\"SGVsbG8gd29ybGQh\"","headers":{"Content-Type":"application/json"}},"uuid":"04baa20d-d318-40e6-9784-c40158c16acd","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"fddd5aaf-ab0d-4ef6-949c-5d329f6d7eb2","name":"Put - default","request":{"urlPathTemplate":"/{id}","method":"PUT","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\n \"errors\": [\n {\n \"category\": \"API_ERROR\",\n \"code\": \"INTERNAL_SERVER_ERROR\",\n \"detail\": \"detail\",\n \"field\": \"field\"\n },\n {\n \"category\": \"API_ERROR\",\n \"code\": \"INTERNAL_SERVER_ERROR\",\n \"detail\": \"detail\",\n \"field\": \"field\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"fddd5aaf-ab0d-4ef6-949c-5d329f6d7eb2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d29330f9-49ff-49c4-9081-7c83dbad315b","name":"getAndReturnUnion - default","request":{"urlPathTemplate":"/union","method":"POST"},"response":{"status":200,"body":"{\n \"animal\": \"dog\",\n \"name\": \"name\",\n \"likesToWoof\": true\n}","headers":{"Content-Type":"application/json"}},"uuid":"d29330f9-49ff-49c4-9081-7c83dbad315b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"343f71f3-36ce-4684-b762-d60a086b43a4","name":"withMixedCase - default","request":{"urlPathTemplate":"/urls/MixedCase","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"343f71f3-36ce-4684-b762-d60a086b43a4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"48f36314-b2b7-4910-9e1d-5b05f3346a60","name":"noEndingSlash - default","request":{"urlPathTemplate":"/urls/no-ending-slash","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"48f36314-b2b7-4910-9e1d-5b05f3346a60","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"f7b95029-2f25-4f70-8b4c-0855712747d8","name":"withEndingSlash - default","request":{"urlPathTemplate":"/urls/with-ending-slash/","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"f7b95029-2f25-4f70-8b4c-0855712747d8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"2f9671a5-e6da-43a8-be27-d0be0191dcbf","name":"withUnderscores - default","request":{"urlPathTemplate":"/urls/with_underscores","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"2f9671a5-e6da-43a8-be27-d0be0191dcbf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"4a6d0aaa-05f2-47bb-9f40-6e3743b9d2bf","name":"postWithObjectBodyandResponse - default","request":{"urlPathTemplate":"/req-bodies/object","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"4a6d0aaa-05f2-47bb-9f40-6e3743b9d2bf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c6e7cc6c-b76f-4860-a9ad-52dc8f55b6e6","name":"postWithNoAuth - default","request":{"urlPathTemplate":"/no-auth","method":"POST"},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"c6e7cc6c-b76f-4860-a9ad-52dc8f55b6e6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8098eeea-bc6b-4068-9601-566c2092f83f","name":"getWithNoRequestBody - default","request":{"urlPathTemplate":"/no-req-body","method":"GET"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"8098eeea-bc6b-4068-9601-566c2092f83f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"7dd9f944-1b35-42e9-a5ed-e48214ac8e91","name":"postWithNoRequestBody - default","request":{"urlPathTemplate":"/no-req-body","method":"POST"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"7dd9f944-1b35-42e9-a5ed-e48214ac8e91","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d7b54168-aef4-4d68-a9c5-446e97dee2fc","name":"getWithCustomHeader - default","request":{"urlPathTemplate":"/test-headers/custom-header","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"d7b54168-aef4-4d68-a9c5-446e97dee2fc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}}],"meta":{"total":53}} \ No newline at end of file diff --git a/seed/python-sdk/exhaustive/package-path/reference.md b/seed/python-sdk/exhaustive/package-path/reference.md index 1732e9836343..186414267ac6 100644 --- a/seed/python-sdk/exhaustive/package-path/reference.md +++ b/seed/python-sdk/exhaustive/package-path/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/package-path/snippet.json b/seed/python-sdk/exhaustive/package-path/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/package-path/snippet.json +++ b/seed/python-sdk/exhaustive/package-path/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/package-path/src/seed/matryoshka/doll/structure/endpoints/params/client.py b/seed/python-sdk/exhaustive/package-path/src/seed/matryoshka/doll/structure/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/package-path/src/seed/matryoshka/doll/structure/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/package-path/src/seed/matryoshka/doll/structure/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/package-path/src/seed/matryoshka/doll/structure/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/package-path/src/seed/matryoshka/doll/structure/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/package-path/src/seed/matryoshka/doll/structure/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/package-path/src/seed/matryoshka/doll/structure/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/pydantic-extra-fields/reference.md b/seed/python-sdk/exhaustive/pydantic-extra-fields/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/pydantic-extra-fields/reference.md +++ b/seed/python-sdk/exhaustive/pydantic-extra-fields/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/pydantic-extra-fields/snippet.json b/seed/python-sdk/exhaustive/pydantic-extra-fields/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/pydantic-extra-fields/snippet.json +++ b/seed/python-sdk/exhaustive/pydantic-extra-fields/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/pydantic-extra-fields/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/pydantic-extra-fields/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/pydantic-extra-fields/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/pydantic-extra-fields/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/pydantic-extra-fields/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/pydantic-extra-fields/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/pydantic-extra-fields/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/pydantic-extra-fields/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/pydantic-ignore-fields/reference.md b/seed/python-sdk/exhaustive/pydantic-ignore-fields/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/pydantic-ignore-fields/reference.md +++ b/seed/python-sdk/exhaustive/pydantic-ignore-fields/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/pydantic-ignore-fields/snippet.json b/seed/python-sdk/exhaustive/pydantic-ignore-fields/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/pydantic-ignore-fields/snippet.json +++ b/seed/python-sdk/exhaustive/pydantic-ignore-fields/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/pydantic-ignore-fields/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/pydantic-ignore-fields/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/pydantic-ignore-fields/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/pydantic-ignore-fields/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/pydantic-ignore-fields/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/pydantic-ignore-fields/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/pydantic-ignore-fields/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/pydantic-ignore-fields/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/pydantic-v1-with-utils/reference.md b/seed/python-sdk/exhaustive/pydantic-v1-with-utils/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1-with-utils/reference.md +++ b/seed/python-sdk/exhaustive/pydantic-v1-with-utils/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/pydantic-v1-with-utils/snippet.json b/seed/python-sdk/exhaustive/pydantic-v1-with-utils/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1-with-utils/snippet.json +++ b/seed/python-sdk/exhaustive/pydantic-v1-with-utils/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/pydantic-v1-with-utils/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/pydantic-v1-with-utils/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1-with-utils/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/pydantic-v1-with-utils/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/pydantic-v1-with-utils/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/pydantic-v1-with-utils/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1-with-utils/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/pydantic-v1-with-utils/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/pydantic-v1-wrapped/reference.md b/seed/python-sdk/exhaustive/pydantic-v1-wrapped/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1-wrapped/reference.md +++ b/seed/python-sdk/exhaustive/pydantic-v1-wrapped/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/pydantic-v1-wrapped/snippet.json b/seed/python-sdk/exhaustive/pydantic-v1-wrapped/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1-wrapped/snippet.json +++ b/seed/python-sdk/exhaustive/pydantic-v1-wrapped/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/pydantic-v1-wrapped/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/pydantic-v1-wrapped/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1-wrapped/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/pydantic-v1-wrapped/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/pydantic-v1-wrapped/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/pydantic-v1-wrapped/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1-wrapped/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/pydantic-v1-wrapped/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/pydantic-v1/reference.md b/seed/python-sdk/exhaustive/pydantic-v1/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1/reference.md +++ b/seed/python-sdk/exhaustive/pydantic-v1/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/pydantic-v1/snippet.json b/seed/python-sdk/exhaustive/pydantic-v1/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1/snippet.json +++ b/seed/python-sdk/exhaustive/pydantic-v1/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/pydantic-v1/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/pydantic-v1/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/pydantic-v1/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/pydantic-v1/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/pydantic-v1/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/pydantic-v1/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/pydantic-v1/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/pydantic-v2-wrapped/reference.md b/seed/python-sdk/exhaustive/pydantic-v2-wrapped/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/pydantic-v2-wrapped/reference.md +++ b/seed/python-sdk/exhaustive/pydantic-v2-wrapped/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/pydantic-v2-wrapped/snippet.json b/seed/python-sdk/exhaustive/pydantic-v2-wrapped/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/pydantic-v2-wrapped/snippet.json +++ b/seed/python-sdk/exhaustive/pydantic-v2-wrapped/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/pydantic-v2-wrapped/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/pydantic-v2-wrapped/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/pydantic-v2-wrapped/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/pydantic-v2-wrapped/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/pydantic-v2-wrapped/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/pydantic-v2-wrapped/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/pydantic-v2-wrapped/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/pydantic-v2-wrapped/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/pyproject_extras/reference.md b/seed/python-sdk/exhaustive/pyproject_extras/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/pyproject_extras/reference.md +++ b/seed/python-sdk/exhaustive/pyproject_extras/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/pyproject_extras/snippet.json b/seed/python-sdk/exhaustive/pyproject_extras/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/pyproject_extras/snippet.json +++ b/seed/python-sdk/exhaustive/pyproject_extras/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/pyproject_extras/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/pyproject_extras/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/pyproject_extras/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/pyproject_extras/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/pyproject_extras/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/pyproject_extras/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/pyproject_extras/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/pyproject_extras/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/skip-pydantic-validation/reference.md b/seed/python-sdk/exhaustive/skip-pydantic-validation/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/skip-pydantic-validation/reference.md +++ b/seed/python-sdk/exhaustive/skip-pydantic-validation/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/skip-pydantic-validation/snippet.json b/seed/python-sdk/exhaustive/skip-pydantic-validation/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/skip-pydantic-validation/snippet.json +++ b/seed/python-sdk/exhaustive/skip-pydantic-validation/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/skip-pydantic-validation/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/skip-pydantic-validation/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/skip-pydantic-validation/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/skip-pydantic-validation/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/skip-pydantic-validation/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/skip-pydantic-validation/src/seed/endpoints/params/raw_client.py index 055504f43712..09ad73be1ab0 100644 --- a/seed/python-sdk/exhaustive/skip-pydantic-validation/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/skip-pydantic-validation/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.request_options import RequestOptions from ...core.unchecked_base_model import construct_type +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + construct_type( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + construct_type( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/union-utils/reference.md b/seed/python-sdk/exhaustive/union-utils/reference.md index 595cb4814fe6..56b3de9184cc 100644 --- a/seed/python-sdk/exhaustive/union-utils/reference.md +++ b/seed/python-sdk/exhaustive/union-utils/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import SeedExhaustive + +client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/union-utils/snippet.json b/seed/python-sdk/exhaustive/union-utils/snippet.json index bec5cef59172..8bb1fa708f01 100644 --- a/seed/python-sdk/exhaustive/union-utils/snippet.json +++ b/seed/python-sdk/exhaustive/union-utils/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import SeedExhaustive\n\nclient = SeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncSeedExhaustive\n\nclient = AsyncSeedExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/union-utils/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/union-utils/src/seed/endpoints/params/client.py index f8e235381b5b..c054777a1ad3 100644 --- a/seed/python-sdk/exhaustive/union-utils/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/union-utils/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import SeedExhaustive + + client = SeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncSeedExhaustive + + client = AsyncSeedExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/union-utils/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/union-utils/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/union-utils/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/union-utils/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/wire-tests-custom-client-name/reference.md b/seed/python-sdk/exhaustive/wire-tests-custom-client-name/reference.md index d091f950d5ad..604cc9dc747d 100644 --- a/seed/python-sdk/exhaustive/wire-tests-custom-client-name/reference.md +++ b/seed/python-sdk/exhaustive/wire-tests-custom-client-name/reference.md @@ -2687,6 +2687,85 @@ client.endpoints.params.modify_with_inline_path( + + + + +
client.endpoints.params.upload_with_path(...) -> AsyncHttpResponse[ObjectWithRequiredField] +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```python +from seed import Exhaustive + +client = Exhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", +) +client.endpoints.params.upload_with_path( + param="upload-path", +) + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `str` + +
+
+ +
+
+ +**request:** `typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/seed/python-sdk/exhaustive/wire-tests-custom-client-name/snippet.json b/seed/python-sdk/exhaustive/wire-tests-custom-client-name/snippet.json index 025c4dfa5665..aaededc9b413 100644 --- a/seed/python-sdk/exhaustive/wire-tests-custom-client-name/snippet.json +++ b/seed/python-sdk/exhaustive/wire-tests-custom-client-name/snippet.json @@ -417,6 +417,19 @@ "type": "python" } }, + { + "example_identifier": "default", + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "sync_client": "from seed import Exhaustive\n\nclient = Exhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\nclient.endpoints.params.upload_with_path(\n param=\"upload-path\",\n)\n", + "async_client": "import asyncio\n\nfrom seed import AsyncExhaustive\n\nclient = AsyncExhaustive(\n token=\"YOUR_TOKEN\",\n base_url=\"https://yourhost.com/path/to/api\",\n)\n\n\nasync def main() -> None:\n await client.endpoints.params.upload_with_path(\n param=\"upload-path\",\n )\n\n\nasyncio.run(main())\n", + "type": "python" + } + }, { "example_identifier": "default", "id": { diff --git a/seed/python-sdk/exhaustive/wire-tests-custom-client-name/src/seed/endpoints/params/client.py b/seed/python-sdk/exhaustive/wire-tests-custom-client-name/src/seed/endpoints/params/client.py index a5f950f699b9..d0eee460f2f6 100644 --- a/seed/python-sdk/exhaustive/wire-tests-custom-client-name/src/seed/endpoints/params/client.py +++ b/seed/python-sdk/exhaustive/wire-tests-custom-client-name/src/seed/endpoints/params/client.py @@ -4,6 +4,7 @@ from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField from .raw_client import AsyncRawParamsClient, RawParamsClient # this is used as the default value for optional parameters @@ -301,6 +302,44 @@ def modify_with_inline_path( _response = self._raw_client.modify_with_inline_path(param, request=request, request_options=request_options) return _response.data + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + from seed import Exhaustive + + client = Exhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + client.endpoints.params.upload_with_path( + param="upload-path", + ) + """ + _response = self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data + class AsyncParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -660,3 +699,49 @@ async def main() -> None: param, request=request, request_options=request_options ) return _response.data + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> ObjectWithRequiredField: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ObjectWithRequiredField + + Examples + -------- + import asyncio + + from seed import AsyncExhaustive + + client = AsyncExhaustive( + token="YOUR_TOKEN", + base_url="https://yourhost.com/path/to/api", + ) + + + async def main() -> None: + await client.endpoints.params.upload_with_path( + param="upload-path", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.upload_with_path(param, request=request, request_options=request_options) + return _response.data diff --git a/seed/python-sdk/exhaustive/wire-tests-custom-client-name/src/seed/endpoints/params/raw_client.py b/seed/python-sdk/exhaustive/wire-tests-custom-client-name/src/seed/endpoints/params/raw_client.py index 9328189418af..9599efd343f5 100644 --- a/seed/python-sdk/exhaustive/wire-tests-custom-client-name/src/seed/endpoints/params/raw_client.py +++ b/seed/python-sdk/exhaustive/wire-tests-custom-client-name/src/seed/endpoints/params/raw_client.py @@ -9,6 +9,7 @@ from ...core.jsonable_encoder import jsonable_encoder from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...types.object.types.object_with_required_field import ObjectWithRequiredField # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -320,6 +321,51 @@ def modify_with_inline_path( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ObjectWithRequiredField] + """ + _response = self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + class AsyncRawParamsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -626,3 +672,48 @@ async def modify_with_inline_path( except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def upload_with_path( + self, + param: str, + *, + request: typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ObjectWithRequiredField]: + """ + POST bytes with path param returning object + + Parameters + ---------- + param : str + + request : typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ObjectWithRequiredField] + """ + _response = await self._client_wrapper.httpx_client.request( + f"params/path/{jsonable_encoder(param)}", + method="POST", + content=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ObjectWithRequiredField, + parse_obj_as( + type_=ObjectWithRequiredField, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/seed/python-sdk/exhaustive/wire-tests-custom-client-name/wiremock/wiremock-mappings.json b/seed/python-sdk/exhaustive/wire-tests-custom-client-name/wiremock/wiremock-mappings.json index 4916bea9a994..8ffc6a19b116 100644 --- a/seed/python-sdk/exhaustive/wire-tests-custom-client-name/wiremock/wiremock-mappings.json +++ b/seed/python-sdk/exhaustive/wire-tests-custom-client-name/wiremock/wiremock-mappings.json @@ -1 +1 @@ -{"mappings":[{"id":"864d58e6-0c08-4f38-b7e1-2638f8d7fd6d","name":"getAndReturnListOfPrimitives - default","request":{"urlPathTemplate":"/container/list-of-primitives","method":"POST"},"response":{"status":200,"body":"[\n \"string\",\n \"string\"\n]","headers":{"Content-Type":"application/json"}},"uuid":"864d58e6-0c08-4f38-b7e1-2638f8d7fd6d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"ac1d4c4f-a8a7-4c27-ae64-7fc977cfa122","name":"getAndReturnListOfObjects - default","request":{"urlPathTemplate":"/container/list-of-objects","method":"POST"},"response":{"status":200,"body":"[\n {\n \"string\": \"string\"\n },\n {\n \"string\": \"string\"\n }\n]","headers":{"Content-Type":"application/json"}},"uuid":"ac1d4c4f-a8a7-4c27-ae64-7fc977cfa122","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"591d5c48-a536-452b-8a2e-ad7c23c38298","name":"getAndReturnSetOfPrimitives - default","request":{"urlPathTemplate":"/container/set-of-primitives","method":"POST"},"response":{"status":200,"body":"[\n \"string\"\n]","headers":{"Content-Type":"application/json"}},"uuid":"591d5c48-a536-452b-8a2e-ad7c23c38298","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e1d5f52b-7a51-464f-ac8f-83c0345a3a35","name":"getAndReturnSetOfObjects - default","request":{"urlPathTemplate":"/container/set-of-objects","method":"POST"},"response":{"status":200,"body":"[\n {\n \"string\": \"string\"\n }\n]","headers":{"Content-Type":"application/json"}},"uuid":"e1d5f52b-7a51-464f-ac8f-83c0345a3a35","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4b1d33b3-ca7d-462a-a2e3-23d531ae2922","name":"getAndReturnMapPrimToPrim - default","request":{"urlPathTemplate":"/container/map-prim-to-prim","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"4b1d33b3-ca7d-462a-a2e3-23d531ae2922","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b01ac2b9-3470-48aa-badc-57d331bb5a49","name":"getAndReturnMapOfPrimToObject - default","request":{"urlPathTemplate":"/container/map-prim-to-object","method":"POST"},"response":{"status":200,"body":"{\n \"string\": {\n \"string\": \"string\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"b01ac2b9-3470-48aa-badc-57d331bb5a49","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"eaf9315d-55c4-4434-8004-a70c25af5656","name":"getAndReturnMapOfPrimToUndiscriminatedUnion - default","request":{"urlPathTemplate":"/container/map-prim-to-union","method":"POST"},"response":{"status":200,"body":"{\n \"string\": 1.1\n}","headers":{"Content-Type":"application/json"}},"uuid":"eaf9315d-55c4-4434-8004-a70c25af5656","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e5271904-de0a-425f-940d-d6f6bde34755","name":"getAndReturnOptional - default","request":{"urlPathTemplate":"/container/opt-objects","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"e5271904-de0a-425f-940d-d6f6bde34755","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d19e1fbe-79cc-465c-962e-e1866ca2361b","name":"postJsonPatchContentType - default","request":{"urlPathTemplate":"/foo/bar","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"d19e1fbe-79cc-465c-962e-e1866ca2361b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4a107cf5-6284-48f8-9ddb-99d944ba989b","name":"postJsonPatchContentWithCharsetType - default","request":{"urlPathTemplate":"/foo/baz","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"4a107cf5-6284-48f8-9ddb-99d944ba989b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"60fd3c8a-3983-41b9-8178-f42997388900","name":"getAndReturnEnum - default","request":{"urlPathTemplate":"/enum","method":"POST"},"response":{"status":200,"body":"\"SUNNY\"","headers":{"Content-Type":"application/json"}},"uuid":"60fd3c8a-3983-41b9-8178-f42997388900","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"3d95c052-db6e-4eff-95f2-895666a5af54","name":"testGet - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"GET","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"3d95c052-db6e-4eff-95f2-895666a5af54","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"2552873f-9f1d-4557-a6c7-6c6b7a55b566","name":"testPost - default","request":{"urlPathTemplate":"/http-methods","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"2552873f-9f1d-4557-a6c7-6c6b7a55b566","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"db42fbdf-5426-41b7-b7ea-28ed39a38e82","name":"testPut - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"PUT","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"db42fbdf-5426-41b7-b7ea-28ed39a38e82","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c9def317-32a0-4bc0-b9b6-5efae8ca44a6","name":"testPatch - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"PATCH","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"c9def317-32a0-4bc0-b9b6-5efae8ca44a6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"05333635-c9ce-4c1a-bb6d-95e8a0fb80dc","name":"testDelete - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"DELETE","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"05333635-c9ce-4c1a-bb6d-95e8a0fb80dc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"41cdef0e-040f-4d08-8426-87b19e60f7d7","name":"getAndReturnWithOptionalField - default","request":{"urlPathTemplate":"/object/get-and-return-with-optional-field","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"41cdef0e-040f-4d08-8426-87b19e60f7d7","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"f74df550-df8c-4503-b202-0b9b3165c1a7","name":"getAndReturnWithRequiredField - default","request":{"urlPathTemplate":"/object/get-and-return-with-required-field","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"f74df550-df8c-4503-b202-0b9b3165c1a7","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0c904dbb-ce54-48a2-8364-13a4989be7f2","name":"getAndReturnWithMapOfMap - default","request":{"urlPathTemplate":"/object/get-and-return-with-map-of-map","method":"POST"},"response":{"status":200,"body":"{\n \"map\": {\n \"map\": {\n \"map\": \"map\"\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"0c904dbb-ce54-48a2-8364-13a4989be7f2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"67e969fc-ff81-4a67-9858-66a29ffc9b72","name":"getAndReturnNestedWithOptionalField - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-optional-field","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"67e969fc-ff81-4a67-9858-66a29ffc9b72","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"169827d0-4247-4236-8cef-34b94d2659de","name":"getAndReturnNestedWithRequiredField - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-required-field/{string}","method":"POST","pathParameters":{"string":{"equalTo":"string"}}},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"169827d0-4247-4236-8cef-34b94d2659de","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6d9ed308-5724-4bbc-a4f7-28dc264d188f","name":"getAndReturnNestedWithRequiredFieldAsList - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-required-field-list","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"6d9ed308-5724-4bbc-a4f7-28dc264d188f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e2cc3e92-6e37-4a50-9081-23952fdd73fe","name":"getAndReturnWithDatetimeLikeString - default","request":{"urlPathTemplate":"/object/get-and-return-with-datetime-like-string","method":"POST"},"response":{"status":200,"body":"{\n \"datetimeLikeString\": \"2023-08-31T14:15:22Z\",\n \"actualDatetime\": \"2023-08-31T14:15:22Z\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"e2cc3e92-6e37-4a50-9081-23952fdd73fe","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"f15b2079-2482-4ed2-951d-d2b5c4de9afd","name":"listItems - default","request":{"urlPathTemplate":"/pagination","method":"GET"},"response":{"status":200,"body":"{\n \"items\": [\n {\n \"string\": \"string\"\n },\n {\n \"string\": \"string\"\n }\n ],\n \"next\": \"next\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"f15b2079-2482-4ed2-951d-d2b5c4de9afd","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"97806fdb-f31f-4f90-84b8-f9cc1713d53d","name":"getWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"97806fdb-f31f-4f90-84b8-f9cc1713d53d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"711fc64f-4af9-4084-8c29-1e7a9e58be70","name":"getWithInlinePath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"711fc64f-4af9-4084-8c29-1e7a9e58be70","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8e5739b3-d75f-47d7-b6b8-a663d91a66b5","name":"getWithQuery - default","request":{"urlPathTemplate":"/params","method":"GET"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"8e5739b3-d75f-47d7-b6b8-a663d91a66b5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"4125d349-732b-4ff7-948d-1eeb977ed13b","name":"getWithAllowMultipleQuery - default","request":{"urlPathTemplate":"/params","method":"GET"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"4125d349-732b-4ff7-948d-1eeb977ed13b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"1933d96a-a1b9-4cf1-85ba-c1e8eff5bd56","name":"getWithPathAndQuery - default","request":{"urlPathTemplate":"/params/path-query/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"1933d96a-a1b9-4cf1-85ba-c1e8eff5bd56","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c4f5012a-fb3f-45ac-b695-beedc3353ad8","name":"getWithInlinePathAndQuery - default","request":{"urlPathTemplate":"/params/path-query/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"c4f5012a-fb3f-45ac-b695-beedc3353ad8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"046bf7d6-751b-48e9-bfc6-ff74a17e88e1","name":"modifyWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"PUT","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"046bf7d6-751b-48e9-bfc6-ff74a17e88e1","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6bfc7195-b99c-4449-bf47-7c4f74f6f33b","name":"modifyWithInlinePath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"PUT","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"6bfc7195-b99c-4449-bf47-7c4f74f6f33b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0506ae7c-87b7-4cd2-9e50-31d60f81893f","name":"getAndReturnString - default","request":{"urlPathTemplate":"/primitive/string","method":"POST"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"0506ae7c-87b7-4cd2-9e50-31d60f81893f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"67f875d1-e19e-440d-8f0c-7e1d24ef2619","name":"getAndReturnInt - default","request":{"urlPathTemplate":"/primitive/integer","method":"POST"},"response":{"status":200,"body":"1","headers":{"Content-Type":"application/json"}},"uuid":"67f875d1-e19e-440d-8f0c-7e1d24ef2619","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"5fadbebd-86c0-41f9-8be5-864e39eb5924","name":"getAndReturnLong - default","request":{"urlPathTemplate":"/primitive/long","method":"POST"},"response":{"status":200,"body":"1000000","headers":{"Content-Type":"application/json"}},"uuid":"5fadbebd-86c0-41f9-8be5-864e39eb5924","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e03872b6-86b1-490b-9195-86e5d3a014f2","name":"getAndReturnDouble - default","request":{"urlPathTemplate":"/primitive/double","method":"POST"},"response":{"status":200,"body":"1.1","headers":{"Content-Type":"application/json"}},"uuid":"e03872b6-86b1-490b-9195-86e5d3a014f2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"442e632f-890a-4105-9448-f7015127e3b4","name":"getAndReturnBool - default","request":{"urlPathTemplate":"/primitive/boolean","method":"POST"},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"442e632f-890a-4105-9448-f7015127e3b4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"ad76fa81-1e63-43fd-9a99-1d5e4339d98c","name":"getAndReturnDatetime - default","request":{"urlPathTemplate":"/primitive/datetime","method":"POST"},"response":{"status":200,"body":"\"2024-01-15T09:30:00Z\"","headers":{"Content-Type":"application/json"}},"uuid":"ad76fa81-1e63-43fd-9a99-1d5e4339d98c","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"afb524b1-54ab-4674-8446-fdd574c368cc","name":"getAndReturnDate - default","request":{"urlPathTemplate":"/primitive/date","method":"POST"},"response":{"status":200,"body":"\"2023-01-15\"","headers":{"Content-Type":"application/json"}},"uuid":"afb524b1-54ab-4674-8446-fdd574c368cc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"969c0f3a-218a-45b7-b17f-64b1a9307d43","name":"getAndReturnUUID - default","request":{"urlPathTemplate":"/primitive/uuid","method":"POST"},"response":{"status":200,"body":"\"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\"","headers":{"Content-Type":"application/json"}},"uuid":"969c0f3a-218a-45b7-b17f-64b1a9307d43","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"04baa20d-d318-40e6-9784-c40158c16acd","name":"getAndReturnBase64 - default","request":{"urlPathTemplate":"/primitive/base64","method":"POST"},"response":{"status":200,"body":"\"SGVsbG8gd29ybGQh\"","headers":{"Content-Type":"application/json"}},"uuid":"04baa20d-d318-40e6-9784-c40158c16acd","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"fddd5aaf-ab0d-4ef6-949c-5d329f6d7eb2","name":"Put - default","request":{"urlPathTemplate":"/{id}","method":"PUT","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\n \"errors\": [\n {\n \"category\": \"API_ERROR\",\n \"code\": \"INTERNAL_SERVER_ERROR\",\n \"detail\": \"detail\",\n \"field\": \"field\"\n },\n {\n \"category\": \"API_ERROR\",\n \"code\": \"INTERNAL_SERVER_ERROR\",\n \"detail\": \"detail\",\n \"field\": \"field\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"fddd5aaf-ab0d-4ef6-949c-5d329f6d7eb2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d29330f9-49ff-49c4-9081-7c83dbad315b","name":"getAndReturnUnion - default","request":{"urlPathTemplate":"/union","method":"POST"},"response":{"status":200,"body":"{\n \"animal\": \"dog\",\n \"name\": \"name\",\n \"likesToWoof\": true\n}","headers":{"Content-Type":"application/json"}},"uuid":"d29330f9-49ff-49c4-9081-7c83dbad315b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"343f71f3-36ce-4684-b762-d60a086b43a4","name":"withMixedCase - default","request":{"urlPathTemplate":"/urls/MixedCase","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"343f71f3-36ce-4684-b762-d60a086b43a4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"48f36314-b2b7-4910-9e1d-5b05f3346a60","name":"noEndingSlash - default","request":{"urlPathTemplate":"/urls/no-ending-slash","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"48f36314-b2b7-4910-9e1d-5b05f3346a60","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"f7b95029-2f25-4f70-8b4c-0855712747d8","name":"withEndingSlash - default","request":{"urlPathTemplate":"/urls/with-ending-slash/","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"f7b95029-2f25-4f70-8b4c-0855712747d8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"2f9671a5-e6da-43a8-be27-d0be0191dcbf","name":"withUnderscores - default","request":{"urlPathTemplate":"/urls/with_underscores","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"2f9671a5-e6da-43a8-be27-d0be0191dcbf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"4a6d0aaa-05f2-47bb-9f40-6e3743b9d2bf","name":"postWithObjectBodyandResponse - default","request":{"urlPathTemplate":"/req-bodies/object","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"4a6d0aaa-05f2-47bb-9f40-6e3743b9d2bf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c6e7cc6c-b76f-4860-a9ad-52dc8f55b6e6","name":"postWithNoAuth - default","request":{"urlPathTemplate":"/no-auth","method":"POST"},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"c6e7cc6c-b76f-4860-a9ad-52dc8f55b6e6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8098eeea-bc6b-4068-9601-566c2092f83f","name":"getWithNoRequestBody - default","request":{"urlPathTemplate":"/no-req-body","method":"GET"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"8098eeea-bc6b-4068-9601-566c2092f83f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"7dd9f944-1b35-42e9-a5ed-e48214ac8e91","name":"postWithNoRequestBody - default","request":{"urlPathTemplate":"/no-req-body","method":"POST"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"7dd9f944-1b35-42e9-a5ed-e48214ac8e91","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d7b54168-aef4-4d68-a9c5-446e97dee2fc","name":"getWithCustomHeader - default","request":{"urlPathTemplate":"/test-headers/custom-header","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"d7b54168-aef4-4d68-a9c5-446e97dee2fc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}}],"meta":{"total":52}} \ No newline at end of file +{"mappings":[{"id":"864d58e6-0c08-4f38-b7e1-2638f8d7fd6d","name":"getAndReturnListOfPrimitives - default","request":{"urlPathTemplate":"/container/list-of-primitives","method":"POST"},"response":{"status":200,"body":"[\n \"string\",\n \"string\"\n]","headers":{"Content-Type":"application/json"}},"uuid":"864d58e6-0c08-4f38-b7e1-2638f8d7fd6d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"ac1d4c4f-a8a7-4c27-ae64-7fc977cfa122","name":"getAndReturnListOfObjects - default","request":{"urlPathTemplate":"/container/list-of-objects","method":"POST"},"response":{"status":200,"body":"[\n {\n \"string\": \"string\"\n },\n {\n \"string\": \"string\"\n }\n]","headers":{"Content-Type":"application/json"}},"uuid":"ac1d4c4f-a8a7-4c27-ae64-7fc977cfa122","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"591d5c48-a536-452b-8a2e-ad7c23c38298","name":"getAndReturnSetOfPrimitives - default","request":{"urlPathTemplate":"/container/set-of-primitives","method":"POST"},"response":{"status":200,"body":"[\n \"string\"\n]","headers":{"Content-Type":"application/json"}},"uuid":"591d5c48-a536-452b-8a2e-ad7c23c38298","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e1d5f52b-7a51-464f-ac8f-83c0345a3a35","name":"getAndReturnSetOfObjects - default","request":{"urlPathTemplate":"/container/set-of-objects","method":"POST"},"response":{"status":200,"body":"[\n {\n \"string\": \"string\"\n }\n]","headers":{"Content-Type":"application/json"}},"uuid":"e1d5f52b-7a51-464f-ac8f-83c0345a3a35","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4b1d33b3-ca7d-462a-a2e3-23d531ae2922","name":"getAndReturnMapPrimToPrim - default","request":{"urlPathTemplate":"/container/map-prim-to-prim","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"4b1d33b3-ca7d-462a-a2e3-23d531ae2922","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b01ac2b9-3470-48aa-badc-57d331bb5a49","name":"getAndReturnMapOfPrimToObject - default","request":{"urlPathTemplate":"/container/map-prim-to-object","method":"POST"},"response":{"status":200,"body":"{\n \"string\": {\n \"string\": \"string\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"b01ac2b9-3470-48aa-badc-57d331bb5a49","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"eaf9315d-55c4-4434-8004-a70c25af5656","name":"getAndReturnMapOfPrimToUndiscriminatedUnion - default","request":{"urlPathTemplate":"/container/map-prim-to-union","method":"POST"},"response":{"status":200,"body":"{\n \"string\": 1.1\n}","headers":{"Content-Type":"application/json"}},"uuid":"eaf9315d-55c4-4434-8004-a70c25af5656","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e5271904-de0a-425f-940d-d6f6bde34755","name":"getAndReturnOptional - default","request":{"urlPathTemplate":"/container/opt-objects","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"e5271904-de0a-425f-940d-d6f6bde34755","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d19e1fbe-79cc-465c-962e-e1866ca2361b","name":"postJsonPatchContentType - default","request":{"urlPathTemplate":"/foo/bar","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"d19e1fbe-79cc-465c-962e-e1866ca2361b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4a107cf5-6284-48f8-9ddb-99d944ba989b","name":"postJsonPatchContentWithCharsetType - default","request":{"urlPathTemplate":"/foo/baz","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"4a107cf5-6284-48f8-9ddb-99d944ba989b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"60fd3c8a-3983-41b9-8178-f42997388900","name":"getAndReturnEnum - default","request":{"urlPathTemplate":"/enum","method":"POST"},"response":{"status":200,"body":"\"SUNNY\"","headers":{"Content-Type":"application/json"}},"uuid":"60fd3c8a-3983-41b9-8178-f42997388900","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"3d95c052-db6e-4eff-95f2-895666a5af54","name":"testGet - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"GET","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"3d95c052-db6e-4eff-95f2-895666a5af54","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"2552873f-9f1d-4557-a6c7-6c6b7a55b566","name":"testPost - default","request":{"urlPathTemplate":"/http-methods","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"2552873f-9f1d-4557-a6c7-6c6b7a55b566","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"db42fbdf-5426-41b7-b7ea-28ed39a38e82","name":"testPut - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"PUT","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"db42fbdf-5426-41b7-b7ea-28ed39a38e82","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c9def317-32a0-4bc0-b9b6-5efae8ca44a6","name":"testPatch - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"PATCH","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"c9def317-32a0-4bc0-b9b6-5efae8ca44a6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"05333635-c9ce-4c1a-bb6d-95e8a0fb80dc","name":"testDelete - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"DELETE","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"05333635-c9ce-4c1a-bb6d-95e8a0fb80dc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"41cdef0e-040f-4d08-8426-87b19e60f7d7","name":"getAndReturnWithOptionalField - default","request":{"urlPathTemplate":"/object/get-and-return-with-optional-field","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"41cdef0e-040f-4d08-8426-87b19e60f7d7","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"f74df550-df8c-4503-b202-0b9b3165c1a7","name":"getAndReturnWithRequiredField - default","request":{"urlPathTemplate":"/object/get-and-return-with-required-field","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"f74df550-df8c-4503-b202-0b9b3165c1a7","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0c904dbb-ce54-48a2-8364-13a4989be7f2","name":"getAndReturnWithMapOfMap - default","request":{"urlPathTemplate":"/object/get-and-return-with-map-of-map","method":"POST"},"response":{"status":200,"body":"{\n \"map\": {\n \"map\": {\n \"map\": \"map\"\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"0c904dbb-ce54-48a2-8364-13a4989be7f2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"67e969fc-ff81-4a67-9858-66a29ffc9b72","name":"getAndReturnNestedWithOptionalField - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-optional-field","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"67e969fc-ff81-4a67-9858-66a29ffc9b72","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"169827d0-4247-4236-8cef-34b94d2659de","name":"getAndReturnNestedWithRequiredField - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-required-field/{string}","method":"POST","pathParameters":{"string":{"equalTo":"string"}}},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"169827d0-4247-4236-8cef-34b94d2659de","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6d9ed308-5724-4bbc-a4f7-28dc264d188f","name":"getAndReturnNestedWithRequiredFieldAsList - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-required-field-list","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"6d9ed308-5724-4bbc-a4f7-28dc264d188f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e2cc3e92-6e37-4a50-9081-23952fdd73fe","name":"getAndReturnWithDatetimeLikeString - default","request":{"urlPathTemplate":"/object/get-and-return-with-datetime-like-string","method":"POST"},"response":{"status":200,"body":"{\n \"datetimeLikeString\": \"2023-08-31T14:15:22Z\",\n \"actualDatetime\": \"2023-08-31T14:15:22Z\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"e2cc3e92-6e37-4a50-9081-23952fdd73fe","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"f15b2079-2482-4ed2-951d-d2b5c4de9afd","name":"listItems - default","request":{"urlPathTemplate":"/pagination","method":"GET"},"response":{"status":200,"body":"{\n \"items\": [\n {\n \"string\": \"string\"\n },\n {\n \"string\": \"string\"\n }\n ],\n \"next\": \"next\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"f15b2079-2482-4ed2-951d-d2b5c4de9afd","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"97806fdb-f31f-4f90-84b8-f9cc1713d53d","name":"getWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"97806fdb-f31f-4f90-84b8-f9cc1713d53d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"711fc64f-4af9-4084-8c29-1e7a9e58be70","name":"getWithInlinePath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"711fc64f-4af9-4084-8c29-1e7a9e58be70","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8e5739b3-d75f-47d7-b6b8-a663d91a66b5","name":"getWithQuery - default","request":{"urlPathTemplate":"/params","method":"GET"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"8e5739b3-d75f-47d7-b6b8-a663d91a66b5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"4125d349-732b-4ff7-948d-1eeb977ed13b","name":"getWithAllowMultipleQuery - default","request":{"urlPathTemplate":"/params","method":"GET"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"4125d349-732b-4ff7-948d-1eeb977ed13b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"1933d96a-a1b9-4cf1-85ba-c1e8eff5bd56","name":"getWithPathAndQuery - default","request":{"urlPathTemplate":"/params/path-query/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"1933d96a-a1b9-4cf1-85ba-c1e8eff5bd56","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c4f5012a-fb3f-45ac-b695-beedc3353ad8","name":"getWithInlinePathAndQuery - default","request":{"urlPathTemplate":"/params/path-query/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"c4f5012a-fb3f-45ac-b695-beedc3353ad8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"046bf7d6-751b-48e9-bfc6-ff74a17e88e1","name":"modifyWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"PUT","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"046bf7d6-751b-48e9-bfc6-ff74a17e88e1","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6bfc7195-b99c-4449-bf47-7c4f74f6f33b","name":"modifyWithInlinePath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"PUT","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"6bfc7195-b99c-4449-bf47-7c4f74f6f33b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"658d6145-14f6-48d3-9c66-c63e01eb6fd2","name":"uploadWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"POST","pathParameters":{"param":{"equalTo":"upload-path"}}},"response":{"status":200,"body":"{\n \"string\": \"uploaded\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"658d6145-14f6-48d3-9c66-c63e01eb6fd2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0506ae7c-87b7-4cd2-9e50-31d60f81893f","name":"getAndReturnString - default","request":{"urlPathTemplate":"/primitive/string","method":"POST"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"0506ae7c-87b7-4cd2-9e50-31d60f81893f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"67f875d1-e19e-440d-8f0c-7e1d24ef2619","name":"getAndReturnInt - default","request":{"urlPathTemplate":"/primitive/integer","method":"POST"},"response":{"status":200,"body":"1","headers":{"Content-Type":"application/json"}},"uuid":"67f875d1-e19e-440d-8f0c-7e1d24ef2619","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"5fadbebd-86c0-41f9-8be5-864e39eb5924","name":"getAndReturnLong - default","request":{"urlPathTemplate":"/primitive/long","method":"POST"},"response":{"status":200,"body":"1000000","headers":{"Content-Type":"application/json"}},"uuid":"5fadbebd-86c0-41f9-8be5-864e39eb5924","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e03872b6-86b1-490b-9195-86e5d3a014f2","name":"getAndReturnDouble - default","request":{"urlPathTemplate":"/primitive/double","method":"POST"},"response":{"status":200,"body":"1.1","headers":{"Content-Type":"application/json"}},"uuid":"e03872b6-86b1-490b-9195-86e5d3a014f2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"442e632f-890a-4105-9448-f7015127e3b4","name":"getAndReturnBool - default","request":{"urlPathTemplate":"/primitive/boolean","method":"POST"},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"442e632f-890a-4105-9448-f7015127e3b4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"ad76fa81-1e63-43fd-9a99-1d5e4339d98c","name":"getAndReturnDatetime - default","request":{"urlPathTemplate":"/primitive/datetime","method":"POST"},"response":{"status":200,"body":"\"2024-01-15T09:30:00Z\"","headers":{"Content-Type":"application/json"}},"uuid":"ad76fa81-1e63-43fd-9a99-1d5e4339d98c","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"afb524b1-54ab-4674-8446-fdd574c368cc","name":"getAndReturnDate - default","request":{"urlPathTemplate":"/primitive/date","method":"POST"},"response":{"status":200,"body":"\"2023-01-15\"","headers":{"Content-Type":"application/json"}},"uuid":"afb524b1-54ab-4674-8446-fdd574c368cc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"969c0f3a-218a-45b7-b17f-64b1a9307d43","name":"getAndReturnUUID - default","request":{"urlPathTemplate":"/primitive/uuid","method":"POST"},"response":{"status":200,"body":"\"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\"","headers":{"Content-Type":"application/json"}},"uuid":"969c0f3a-218a-45b7-b17f-64b1a9307d43","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"04baa20d-d318-40e6-9784-c40158c16acd","name":"getAndReturnBase64 - default","request":{"urlPathTemplate":"/primitive/base64","method":"POST"},"response":{"status":200,"body":"\"SGVsbG8gd29ybGQh\"","headers":{"Content-Type":"application/json"}},"uuid":"04baa20d-d318-40e6-9784-c40158c16acd","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"fddd5aaf-ab0d-4ef6-949c-5d329f6d7eb2","name":"Put - default","request":{"urlPathTemplate":"/{id}","method":"PUT","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\n \"errors\": [\n {\n \"category\": \"API_ERROR\",\n \"code\": \"INTERNAL_SERVER_ERROR\",\n \"detail\": \"detail\",\n \"field\": \"field\"\n },\n {\n \"category\": \"API_ERROR\",\n \"code\": \"INTERNAL_SERVER_ERROR\",\n \"detail\": \"detail\",\n \"field\": \"field\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"fddd5aaf-ab0d-4ef6-949c-5d329f6d7eb2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d29330f9-49ff-49c4-9081-7c83dbad315b","name":"getAndReturnUnion - default","request":{"urlPathTemplate":"/union","method":"POST"},"response":{"status":200,"body":"{\n \"animal\": \"dog\",\n \"name\": \"name\",\n \"likesToWoof\": true\n}","headers":{"Content-Type":"application/json"}},"uuid":"d29330f9-49ff-49c4-9081-7c83dbad315b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"343f71f3-36ce-4684-b762-d60a086b43a4","name":"withMixedCase - default","request":{"urlPathTemplate":"/urls/MixedCase","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"343f71f3-36ce-4684-b762-d60a086b43a4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"48f36314-b2b7-4910-9e1d-5b05f3346a60","name":"noEndingSlash - default","request":{"urlPathTemplate":"/urls/no-ending-slash","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"48f36314-b2b7-4910-9e1d-5b05f3346a60","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"f7b95029-2f25-4f70-8b4c-0855712747d8","name":"withEndingSlash - default","request":{"urlPathTemplate":"/urls/with-ending-slash/","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"f7b95029-2f25-4f70-8b4c-0855712747d8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"2f9671a5-e6da-43a8-be27-d0be0191dcbf","name":"withUnderscores - default","request":{"urlPathTemplate":"/urls/with_underscores","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"2f9671a5-e6da-43a8-be27-d0be0191dcbf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"4a6d0aaa-05f2-47bb-9f40-6e3743b9d2bf","name":"postWithObjectBodyandResponse - default","request":{"urlPathTemplate":"/req-bodies/object","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"4a6d0aaa-05f2-47bb-9f40-6e3743b9d2bf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c6e7cc6c-b76f-4860-a9ad-52dc8f55b6e6","name":"postWithNoAuth - default","request":{"urlPathTemplate":"/no-auth","method":"POST"},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"c6e7cc6c-b76f-4860-a9ad-52dc8f55b6e6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8098eeea-bc6b-4068-9601-566c2092f83f","name":"getWithNoRequestBody - default","request":{"urlPathTemplate":"/no-req-body","method":"GET"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"8098eeea-bc6b-4068-9601-566c2092f83f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"7dd9f944-1b35-42e9-a5ed-e48214ac8e91","name":"postWithNoRequestBody - default","request":{"urlPathTemplate":"/no-req-body","method":"POST"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"7dd9f944-1b35-42e9-a5ed-e48214ac8e91","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d7b54168-aef4-4d68-a9c5-446e97dee2fc","name":"getWithCustomHeader - default","request":{"urlPathTemplate":"/test-headers/custom-header","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"d7b54168-aef4-4d68-a9c5-446e97dee2fc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}}],"meta":{"total":53}} \ No newline at end of file From 0b519aa9930f2886d1a56cf0f630c36a668d432a Mon Sep 17 00:00:00 2001 From: Anar Kafkas <36949216+kafkas@users.noreply.github.com> Date: Wed, 25 Feb 2026 04:29:33 +0300 Subject: [PATCH 06/20] feat(cli): add `collapsible` + `collapsed-by-default` to docs.yml navigation (#12726) * Update docs-yml definition * Update cli types * Update navigationUtils * Update DocsDefinitionResolver * Update parsing logic * Add tests for new fields * Update json schemas * Add changelog entry * Mark collapsed attrs as availability: deprecated in Fern definition Co-Authored-By: Sandeep Dinesh --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Sandeep Dinesh --- docs-yml.schema.json | 40 ++++++++ fern/apis/docs-yml/definition/docs.yml | 32 ++++++- packages/cli/cli/versions.yml | 9 ++ .../navigation/FolderConfigurationSchema.ts | 2 + .../navigation/SectionConfigurationSchema.ts | 2 + .../collapsibleNavigationConfig.test.ts | 96 +++++++++++++++++++ .../src/docs-yml/navigationUtils.ts | 2 + .../src/docs-yml/parseDocsConfiguration.ts | 47 +++++++++ .../src/docs-yml/ParsedDocsConfiguration.ts | 2 + .../docs/types/FolderConfiguration.ts | 5 + .../docs/types/SectionConfiguration.ts | 5 + .../docs/types/FolderConfiguration.ts | 7 ++ .../docs/types/SectionConfiguration.ts | 7 ++ .../src/DocsDefinitionResolver.ts | 19 +++- .../workspace/loader/src/docs-yml.schema.json | 40 ++++++++ .../src/docsAst/products-yml.schema.json | 40 ++++++++ .../src/docsAst/versions-yml.schema.json | 40 ++++++++ product-yml.schema.json | 40 ++++++++ version-yml.schema.json | 40 ++++++++ 19 files changed, 470 insertions(+), 5 deletions(-) create mode 100644 packages/cli/configuration-loader/src/docs-yml/__test__/collapsibleNavigationConfig.test.ts diff --git a/docs-yml.schema.json b/docs-yml.schema.json index c0b3298b3414..cb079acec9de 100644 --- a/docs-yml.schema.json +++ b/docs-yml.schema.json @@ -1497,6 +1497,26 @@ } ] }, + "collapsible": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "collapsed-by-default": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, "slug": { "oneOf": [ { @@ -2776,6 +2796,26 @@ } ] }, + "collapsible": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "collapsed-by-default": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, "availability": { "oneOf": [ { diff --git a/fern/apis/docs-yml/definition/docs.yml b/fern/apis/docs-yml/definition/docs.yml index 75923f157c29..195df4be34ec 100644 --- a/fern/apis/docs-yml/definition/docs.yml +++ b/fern/apis/docs-yml/definition/docs.yml @@ -1093,7 +1093,19 @@ types: docs: | The relative path to the markdown file that will be displayed when the section is clicked. contents: list - collapsed: optional + collapsed: + type: optional + docs: | + Deprecated. Use `collapsible` and `collapsed-by-default` instead. + availability: deprecated + collapsible: + type: optional + docs: | + Whether the section can be expanded/collapsed by the user. + collapsed-by-default: + type: optional + docs: | + Whether the section starts collapsed. Only meaningful when `collapsible` is true. Defaults to false (starts open). slug: optional icon: optional hidden: optional @@ -1119,7 +1131,19 @@ types: icon: optional hidden: optional skip-slug: optional - collapsed: optional + collapsed: + type: optional + docs: | + Deprecated. Use `collapsible` and `collapsed-by-default` instead. + availability: deprecated + collapsible: + type: optional + docs: | + Whether the section can be expanded/collapsed by the user. + collapsed-by-default: + type: optional + docs: | + Whether the section starts collapsed. Only meaningful when `collapsible` is true. Defaults to false (starts open). availability: optional TitleSource: @@ -1156,7 +1180,9 @@ types: type: optional> docs: | Advanced usage: when specified, this object will be used to customize the order that your API endpoints are displayed in the docs site, including subpackages, and additional markdown pages (to be rendered in between API endpoints). If not specified, the order will be inferred from the OpenAPI Spec or Fern Definition. - collapsed: optional + collapsed: + type: optional + availability: deprecated icon: optional slug: optional hidden: optional diff --git a/packages/cli/cli/versions.yml b/packages/cli/cli/versions.yml index 971601c186cd..d6d7a05d9352 100644 --- a/packages/cli/cli/versions.yml +++ b/packages/cli/cli/versions.yml @@ -1,4 +1,13 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json + +- version: 3.87.0 + changelogEntry: + - summary: | + Added `collapsible` and `collapsed-by-default` options for `docs.yml` navigation sections/folders, preserving the legacy `collapsed` behavior while adding validation to prevent invalid configurations. + type: feat + createdAt: "2026-02-24" + irVersion: 65 + - version: 3.86.1 changelogEntry: - summary: | diff --git a/packages/cli/config/src/schemas/docs/navigation/FolderConfigurationSchema.ts b/packages/cli/config/src/schemas/docs/navigation/FolderConfigurationSchema.ts index 184bd3cd61f7..84bd302d6885 100644 --- a/packages/cli/config/src/schemas/docs/navigation/FolderConfigurationSchema.ts +++ b/packages/cli/config/src/schemas/docs/navigation/FolderConfigurationSchema.ts @@ -16,6 +16,8 @@ export const FolderConfigurationSchema = z.object({ icon: z.string().optional(), hidden: z.boolean().optional(), collapsed: z.boolean().optional(), + collapsible: z.boolean().optional(), + collapsedByDefault: z.boolean().optional(), path: z.string().optional(), // WithPermissions viewers: RoleSchema.optional(), diff --git a/packages/cli/config/src/schemas/docs/navigation/SectionConfigurationSchema.ts b/packages/cli/config/src/schemas/docs/navigation/SectionConfigurationSchema.ts index ce87ffe632cc..5ac3787ccfcc 100644 --- a/packages/cli/config/src/schemas/docs/navigation/SectionConfigurationSchema.ts +++ b/packages/cli/config/src/schemas/docs/navigation/SectionConfigurationSchema.ts @@ -29,6 +29,8 @@ export const SectionConfigurationSchema = z.object({ hidden: z.boolean().optional(), skipSlug: z.boolean().optional(), collapsed: z.boolean().optional(), + collapsible: z.boolean().optional(), + collapsedByDefault: z.boolean().optional(), flattened: z.boolean().optional(), path: z.string().optional(), availability: AvailabilitySchema.optional(), diff --git a/packages/cli/configuration-loader/src/docs-yml/__test__/collapsibleNavigationConfig.test.ts b/packages/cli/configuration-loader/src/docs-yml/__test__/collapsibleNavigationConfig.test.ts new file mode 100644 index 000000000000..1ae13372e345 --- /dev/null +++ b/packages/cli/configuration-loader/src/docs-yml/__test__/collapsibleNavigationConfig.test.ts @@ -0,0 +1,96 @@ +import { docsYml } from "@fern-api/configuration"; +import { AbsoluteFilePath } from "@fern-api/fs-utils"; +import { createMockTaskContext, FernCliError } from "@fern-api/task-context"; +import { describe, expect, it } from "vitest"; + +import { parseDocsConfiguration } from "../parseDocsConfiguration.js"; + +describe("docs.yml navigation collapsible config", () => { + it("should throw if collapsed-by-default is set without collapsible: true", async () => { + const context = createMockTaskContext(); + + const rawDocsConfiguration: docsYml.RawSchemas.DocsConfiguration = { + instances: [], + title: "Test", + navigation: [ + { + section: "Section", + contents: [], + collapsedByDefault: true + } + ] + }; + + await expect( + parseDocsConfiguration({ + rawDocsConfiguration, + // These paths shouldn't be used by this test case. + absolutePathToFernFolder: AbsoluteFilePath.of("/tmp"), + absoluteFilepathToDocsConfig: AbsoluteFilePath.of("/tmp/docs.yml"), + context + }) + ).rejects.toBeInstanceOf(FernCliError); + }); + + it("should throw if collapsible is used alongside deprecated collapsed", async () => { + const context = createMockTaskContext(); + + const rawDocsConfiguration: docsYml.RawSchemas.DocsConfiguration = { + instances: [], + title: "Test", + navigation: [ + { + section: "Section", + contents: [], + collapsible: true, + collapsed: true + } + ] + }; + + await expect( + parseDocsConfiguration({ + rawDocsConfiguration, + absolutePathToFernFolder: AbsoluteFilePath.of("/tmp"), + absoluteFilepathToDocsConfig: AbsoluteFilePath.of("/tmp/docs.yml"), + context + }) + ).rejects.toBeInstanceOf(FernCliError); + }); + + it("should pass through collapsible + collapsedByDefault on sections", async () => { + const context = createMockTaskContext(); + + const rawDocsConfiguration: docsYml.RawSchemas.DocsConfiguration = { + instances: [], + title: "Test", + navigation: [ + { + section: "Section", + contents: [], + collapsible: true, + collapsedByDefault: true + } + ] + }; + + const parsed = await parseDocsConfiguration({ + rawDocsConfiguration, + absolutePathToFernFolder: AbsoluteFilePath.of("/tmp"), + absoluteFilepathToDocsConfig: AbsoluteFilePath.of("/tmp/docs.yml"), + context + }); + + expect(parsed.navigation).toMatchObject({ + type: "untabbed", + items: [ + { + type: "section", + title: "Section", + collapsible: true, + collapsedByDefault: true + } + ] + }); + }); +}); diff --git a/packages/cli/configuration-loader/src/docs-yml/navigationUtils.ts b/packages/cli/configuration-loader/src/docs-yml/navigationUtils.ts index c6984aff176b..3048a374a38f 100644 --- a/packages/cli/configuration-loader/src/docs-yml/navigationUtils.ts +++ b/packages/cli/configuration-loader/src/docs-yml/navigationUtils.ts @@ -177,6 +177,8 @@ export async function buildNavigationForDirectory({ icon: undefined, contents: filteredContents, collapsed: undefined, + collapsible: undefined, + collapsedByDefault: undefined, hidden: undefined, skipUrlSlug: false, overviewAbsolutePath: indexPage?.type === "page" ? indexPage.absolutePath : undefined, diff --git a/packages/cli/configuration-loader/src/docs-yml/parseDocsConfiguration.ts b/packages/cli/configuration-loader/src/docs-yml/parseDocsConfiguration.ts index ef94677a5a73..070a9eb723b2 100644 --- a/packages/cli/configuration-loader/src/docs-yml/parseDocsConfiguration.ts +++ b/packages/cli/configuration-loader/src/docs-yml/parseDocsConfiguration.ts @@ -1007,6 +1007,14 @@ async function expandFolderConfiguration({ context.failAndThrow(`Folder not found: ${rawConfig.folder}`); } + validateCollapsibleConfig({ + context, + sectionTitle: rawConfig.folder, + collapsed: rawConfig.collapsed ?? undefined, + collapsible: rawConfig.collapsible ?? undefined, + collapsedByDefault: rawConfig.collapsedByDefault ?? undefined + }); + const effectiveTitleSource = rawConfig.titleSource ?? folderTitleSource; const contents = await buildNavigationForDirectory({ @@ -1039,6 +1047,8 @@ async function expandFolderConfiguration({ contents: filteredContents, slug, collapsed: rawConfig.collapsed ?? undefined, + collapsible: rawConfig.collapsible ?? undefined, + collapsedByDefault: rawConfig.collapsedByDefault ?? undefined, hidden: rawConfig.hidden ?? undefined, skipUrlSlug: rawConfig.skipSlug ?? false, overviewAbsolutePath: indexPage?.type === "page" ? indexPage.absolutePath : undefined, @@ -1066,6 +1076,13 @@ async function convertNavigationItem({ return parsePageConfig(rawConfig, absolutePathToConfig); } if (isRawSectionConfig(rawConfig)) { + validateCollapsibleConfig({ + context, + sectionTitle: rawConfig.section, + collapsed: rawConfig.collapsed ?? undefined, + collapsible: rawConfig.collapsible ?? undefined, + collapsedByDefault: rawConfig.collapsedByDefault ?? undefined + }); return { type: "section", title: rawConfig.section, @@ -1083,6 +1100,8 @@ async function convertNavigationItem({ ), slug: rawConfig.slug ?? undefined, collapsed: rawConfig.collapsed ?? undefined, + collapsible: rawConfig.collapsible ?? undefined, + collapsedByDefault: rawConfig.collapsedByDefault ?? undefined, hidden: rawConfig.hidden ?? undefined, skipUrlSlug: rawConfig.skipSlug ?? false, overviewAbsolutePath: resolveFilepath(rawConfig.path, absolutePathToConfig), @@ -1636,3 +1655,31 @@ export function parseAudiences(raw: string | string[] | undefined): string[] | u return raw; } + +function validateCollapsibleConfig({ + context, + sectionTitle, + collapsed, + collapsible, + collapsedByDefault +}: { + context: TaskContext; + sectionTitle: string; + collapsed: boolean | undefined; + collapsible: boolean | undefined; + collapsedByDefault: boolean | undefined; +}): void { + if (collapsible != null && collapsed != null) { + context.failAndThrow( + `Section "${sectionTitle}": cannot use both "collapsible" and the deprecated "collapsed" property. ` + + `Please use "collapsible" and "collapsed-by-default" instead.` + ); + } + + if (collapsedByDefault != null && collapsible !== true) { + context.failAndThrow( + `Section "${sectionTitle}": "collapsed-by-default" requires "collapsible: true". ` + + `"collapsed-by-default" has no effect on a non-collapsible section.` + ); + } +} diff --git a/packages/cli/configuration/src/docs-yml/ParsedDocsConfiguration.ts b/packages/cli/configuration/src/docs-yml/ParsedDocsConfiguration.ts index 3474f00cc338..7ce8fdb18f1c 100644 --- a/packages/cli/configuration/src/docs-yml/ParsedDocsConfiguration.ts +++ b/packages/cli/configuration/src/docs-yml/ParsedDocsConfiguration.ts @@ -324,6 +324,8 @@ export declare namespace DocsNavigationItem { icon: string | AbsoluteFilePath | undefined; contents: DocsNavigationItem[]; collapsed: boolean | undefined; + collapsible: boolean | undefined; + collapsedByDefault: boolean | undefined; slug: string | undefined; hidden: boolean | undefined; skipUrlSlug: boolean | undefined; diff --git a/packages/cli/configuration/src/docs-yml/schemas/sdk/api/resources/docs/types/FolderConfiguration.ts b/packages/cli/configuration/src/docs-yml/schemas/sdk/api/resources/docs/types/FolderConfiguration.ts index ff2bc47bc178..9fa893eb7441 100644 --- a/packages/cli/configuration/src/docs-yml/schemas/sdk/api/resources/docs/types/FolderConfiguration.ts +++ b/packages/cli/configuration/src/docs-yml/schemas/sdk/api/resources/docs/types/FolderConfiguration.ts @@ -13,6 +13,11 @@ export interface FolderConfiguration extends FernDocsConfig.WithPermissions, Fer icon?: string; hidden?: boolean; skipSlug?: boolean; + /** Deprecated. Use `collapsible` and `collapsed-by-default` instead. */ collapsed?: boolean; + /** Whether the section can be expanded/collapsed by the user. */ + collapsible?: boolean; + /** Whether the section starts collapsed. Only meaningful when `collapsible` is true. Defaults to false (starts open). */ + collapsedByDefault?: boolean; availability?: FernDocsConfig.Availability; } diff --git a/packages/cli/configuration/src/docs-yml/schemas/sdk/api/resources/docs/types/SectionConfiguration.ts b/packages/cli/configuration/src/docs-yml/schemas/sdk/api/resources/docs/types/SectionConfiguration.ts index 16af9b761813..3dcea7efa2cb 100644 --- a/packages/cli/configuration/src/docs-yml/schemas/sdk/api/resources/docs/types/SectionConfiguration.ts +++ b/packages/cli/configuration/src/docs-yml/schemas/sdk/api/resources/docs/types/SectionConfiguration.ts @@ -7,7 +7,12 @@ export interface SectionConfiguration extends FernDocsConfig.WithPermissions, Fe /** The relative path to the markdown file that will be displayed when the section is clicked. */ path?: string; contents: FernDocsConfig.NavigationItem[]; + /** Deprecated. Use `collapsible` and `collapsed-by-default` instead. */ collapsed?: boolean; + /** Whether the section can be expanded/collapsed by the user. */ + collapsible?: boolean; + /** Whether the section starts collapsed. Only meaningful when `collapsible` is true. Defaults to false (starts open). */ + collapsedByDefault?: boolean; slug?: string; icon?: string; hidden?: boolean; diff --git a/packages/cli/configuration/src/docs-yml/schemas/sdk/serialization/resources/docs/types/FolderConfiguration.ts b/packages/cli/configuration/src/docs-yml/schemas/sdk/serialization/resources/docs/types/FolderConfiguration.ts index 2c7c3d87013c..49a1e97af175 100644 --- a/packages/cli/configuration/src/docs-yml/schemas/sdk/serialization/resources/docs/types/FolderConfiguration.ts +++ b/packages/cli/configuration/src/docs-yml/schemas/sdk/serialization/resources/docs/types/FolderConfiguration.ts @@ -21,6 +21,11 @@ export const FolderConfiguration: core.serialization.ObjectSchema< hidden: core.serialization.boolean().optional(), skipSlug: core.serialization.property("skip-slug", core.serialization.boolean().optional()), collapsed: core.serialization.boolean().optional(), + collapsible: core.serialization.boolean().optional(), + collapsedByDefault: core.serialization.property( + "collapsed-by-default", + core.serialization.boolean().optional(), + ), availability: Availability.optional(), }) .extend(WithPermissions) @@ -36,6 +41,8 @@ export declare namespace FolderConfiguration { hidden?: boolean | null; "skip-slug"?: boolean | null; collapsed?: boolean | null; + collapsible?: boolean | null; + "collapsed-by-default"?: boolean | null; availability?: Availability.Raw | null; } } diff --git a/packages/cli/configuration/src/docs-yml/schemas/sdk/serialization/resources/docs/types/SectionConfiguration.ts b/packages/cli/configuration/src/docs-yml/schemas/sdk/serialization/resources/docs/types/SectionConfiguration.ts index 4702823f9a97..a2dc2af6cdb6 100644 --- a/packages/cli/configuration/src/docs-yml/schemas/sdk/serialization/resources/docs/types/SectionConfiguration.ts +++ b/packages/cli/configuration/src/docs-yml/schemas/sdk/serialization/resources/docs/types/SectionConfiguration.ts @@ -16,6 +16,11 @@ export const SectionConfiguration: core.serialization.ObjectSchema< path: core.serialization.string().optional(), contents: core.serialization.list(core.serialization.lazy(() => serializers.NavigationItem)), collapsed: core.serialization.boolean().optional(), + collapsible: core.serialization.boolean().optional(), + collapsedByDefault: core.serialization.property( + "collapsed-by-default", + core.serialization.boolean().optional(), + ), slug: core.serialization.string().optional(), icon: core.serialization.string().optional(), hidden: core.serialization.boolean().optional(), @@ -31,6 +36,8 @@ export declare namespace SectionConfiguration { path?: string | null; contents: serializers.NavigationItem.Raw[]; collapsed?: boolean | null; + collapsible?: boolean | null; + "collapsed-by-default"?: boolean | null; slug?: string | null; icon?: string | null; hidden?: boolean | null; diff --git a/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts b/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts index 0611d5767650..a9b4cde11ff5 100644 --- a/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts +++ b/packages/cli/docs-resolver/src/DocsDefinitionResolver.ts @@ -27,6 +27,12 @@ import matter from "gray-matter"; import jsYaml from "js-yaml"; import { camelCase, kebabCase } from "lodash-es"; +// TODO: Remove this when the new fdr-sdk is integrated +type SectionNodeWithNewCollapsibleConfig = FernNavigation.V1.SectionNode & { + collapsible?: boolean; + collapsedByDefault?: boolean; +}; + /** * Shape of navigation nodes in `_navigation.yml`, generated by `library-docs-generator`. * @@ -1139,7 +1145,13 @@ export class DocsDefinitionResolver { return; } - if (child.type === "section" && !child.collapsed) { + // Temporary coercion to satisfy type checker until new fdr-sdk is integrated + const isCollapsible = + child.type === "section" && + ((child as SectionNodeWithNewCollapsibleConfig).collapsible === true || + ((child as SectionNodeWithNewCollapsibleConfig).collapsible == null && child.collapsed === true)); + + if (child.type === "section" && !isCollapsible) { grouped.push(child); return; } @@ -1846,6 +1858,8 @@ export class DocsDefinitionResolver { : item.title, icon: this.resolveIconFileId(item.icon), collapsed: item.collapsed, + collapsible: item.collapsible ?? (item.collapsed === true ? true : undefined), + collapsedByDefault: item.collapsedByDefault ?? (item.collapsed === true ? true : undefined), hidden: hiddenSection, viewers: item.viewers, orphaned: item.orphaned, @@ -1855,7 +1869,8 @@ export class DocsDefinitionResolver { noindex, featureFlags: item.featureFlags, availability: frontmatterAvailability ?? item.availability ?? parentAvailability - }; + // Temporary coercion to satisfy type checker until new fdr-sdk is integrated + } as SectionNodeWithNewCollapsibleConfig; } private async convertTabbedNavigation( diff --git a/packages/cli/workspace/loader/src/docs-yml.schema.json b/packages/cli/workspace/loader/src/docs-yml.schema.json index c0b3298b3414..cb079acec9de 100644 --- a/packages/cli/workspace/loader/src/docs-yml.schema.json +++ b/packages/cli/workspace/loader/src/docs-yml.schema.json @@ -1497,6 +1497,26 @@ } ] }, + "collapsible": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "collapsed-by-default": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, "slug": { "oneOf": [ { @@ -2776,6 +2796,26 @@ } ] }, + "collapsible": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "collapsed-by-default": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, "availability": { "oneOf": [ { diff --git a/packages/cli/yaml/docs-validator/src/docsAst/products-yml.schema.json b/packages/cli/yaml/docs-validator/src/docsAst/products-yml.schema.json index 453afc0bfae7..f547cc81c775 100644 --- a/packages/cli/yaml/docs-validator/src/docsAst/products-yml.schema.json +++ b/packages/cli/yaml/docs-validator/src/docsAst/products-yml.schema.json @@ -407,6 +407,26 @@ } ] }, + "collapsible": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "collapsed-by-default": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, "slug": { "oneOf": [ { @@ -1702,6 +1722,26 @@ } ] }, + "collapsible": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "collapsed-by-default": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, "availability": { "oneOf": [ { diff --git a/packages/cli/yaml/docs-validator/src/docsAst/versions-yml.schema.json b/packages/cli/yaml/docs-validator/src/docsAst/versions-yml.schema.json index 453afc0bfae7..f547cc81c775 100644 --- a/packages/cli/yaml/docs-validator/src/docsAst/versions-yml.schema.json +++ b/packages/cli/yaml/docs-validator/src/docsAst/versions-yml.schema.json @@ -407,6 +407,26 @@ } ] }, + "collapsible": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "collapsed-by-default": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, "slug": { "oneOf": [ { @@ -1702,6 +1722,26 @@ } ] }, + "collapsible": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "collapsed-by-default": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, "availability": { "oneOf": [ { diff --git a/product-yml.schema.json b/product-yml.schema.json index 453afc0bfae7..f547cc81c775 100644 --- a/product-yml.schema.json +++ b/product-yml.schema.json @@ -407,6 +407,26 @@ } ] }, + "collapsible": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "collapsed-by-default": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, "slug": { "oneOf": [ { @@ -1702,6 +1722,26 @@ } ] }, + "collapsible": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "collapsed-by-default": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, "availability": { "oneOf": [ { diff --git a/version-yml.schema.json b/version-yml.schema.json index 453afc0bfae7..f547cc81c775 100644 --- a/version-yml.schema.json +++ b/version-yml.schema.json @@ -407,6 +407,26 @@ } ] }, + "collapsible": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "collapsed-by-default": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, "slug": { "oneOf": [ { @@ -1702,6 +1722,26 @@ } ] }, + "collapsible": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "collapsed-by-default": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, "availability": { "oneOf": [ { From a2505d30ca15f67e66d8cb489a59c59dc46eb13f Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 24 Feb 2026 21:02:23 -0500 Subject: [PATCH 07/20] chore(php): update php-sdk seed (#12738) Co-authored-by: amckinney --- .../exhaustive/no-custom-config/reference.md | 57 +++++++++++++++++++ .../src/Endpoints/Params/ParamsClient.php | 46 +++++++++++++++ .../dynamic-snippets/example33/snippet.php | 5 +- .../dynamic-snippets/example34/snippet.php | 4 +- .../dynamic-snippets/example35/snippet.php | 4 +- .../dynamic-snippets/example36/snippet.php | 4 +- .../dynamic-snippets/example37/snippet.php | 4 +- .../dynamic-snippets/example38/snippet.php | 5 +- .../dynamic-snippets/example39/snippet.php | 4 +- .../dynamic-snippets/example40/snippet.php | 5 +- .../dynamic-snippets/example41/snippet.php | 4 +- .../dynamic-snippets/example42/snippet.php | 4 +- .../dynamic-snippets/example43/snippet.php | 9 +-- .../dynamic-snippets/example44/snippet.php | 9 ++- .../dynamic-snippets/example45/snippet.php | 2 +- .../dynamic-snippets/example46/snippet.php | 2 +- .../dynamic-snippets/example47/snippet.php | 2 +- .../dynamic-snippets/example48/snippet.php | 32 +---------- .../dynamic-snippets/example50/snippet.php | 34 +++++++++-- .../dynamic-snippets/example52/snippet.php | 6 +- .../dynamic-snippets/example53/snippet.php | 2 +- .../dynamic-snippets/example54/snippet.php | 9 +-- .../dynamic-snippets/example55/snippet.php | 20 +++++++ .../exhaustive/wire-tests/reference.md | 57 +++++++++++++++++++ .../src/Endpoints/Params/ParamsClient.php | 46 +++++++++++++++ .../dynamic-snippets/example33/snippet.php | 5 +- .../dynamic-snippets/example34/snippet.php | 4 +- .../dynamic-snippets/example35/snippet.php | 4 +- .../dynamic-snippets/example36/snippet.php | 4 +- .../dynamic-snippets/example37/snippet.php | 4 +- .../dynamic-snippets/example38/snippet.php | 5 +- .../dynamic-snippets/example39/snippet.php | 4 +- .../dynamic-snippets/example40/snippet.php | 5 +- .../dynamic-snippets/example41/snippet.php | 4 +- .../dynamic-snippets/example42/snippet.php | 4 +- .../dynamic-snippets/example43/snippet.php | 9 +-- .../dynamic-snippets/example44/snippet.php | 9 ++- .../dynamic-snippets/example45/snippet.php | 2 +- .../dynamic-snippets/example46/snippet.php | 2 +- .../dynamic-snippets/example47/snippet.php | 2 +- .../dynamic-snippets/example48/snippet.php | 32 +---------- .../dynamic-snippets/example50/snippet.php | 34 +++++++++-- .../dynamic-snippets/example52/snippet.php | 6 +- .../dynamic-snippets/example53/snippet.php | 2 +- .../dynamic-snippets/example54/snippet.php | 9 +-- .../dynamic-snippets/example55/snippet.php | 20 +++++++ .../tests/Wire/EndpointsParamsWireTest.php | 22 +++++++ .../wiremock/wiremock-mappings.json | 33 ++++++++++- 48 files changed, 446 insertions(+), 155 deletions(-) create mode 100644 seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example55/snippet.php create mode 100644 seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example55/snippet.php diff --git a/seed/php-sdk/exhaustive/no-custom-config/reference.md b/seed/php-sdk/exhaustive/no-custom-config/reference.md index 5dde965101ff..7488b8757196 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/reference.md +++ b/seed/php-sdk/exhaustive/no-custom-config/reference.md @@ -1812,6 +1812,63 @@ $client->endpoints->params->modifyWithPath( + + + + +
$client->endpoints->params->uploadWithPath($param) -> ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```php +$client->endpoints->params->uploadWithPath( + 'upload-path', + , +); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**$param:** `string` + +
+
+
+
+ +
diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/ParamsClient.php b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/ParamsClient.php index 7d8c705eb873..c8d971fce789 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/ParamsClient.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/Endpoints/Params/ParamsClient.php @@ -16,6 +16,7 @@ use Seed\Endpoints\Params\Requests\GetWithPathAndQuery; use Seed\Endpoints\Params\Requests\GetWithInlinePathAndQuery; use Seed\Endpoints\Params\Requests\ModifyResourceAtInlinedPath; +use Seed\Types\Object\Types\ObjectWithRequiredField; class ParamsClient { @@ -416,4 +417,49 @@ public function modifyWithInlinePath(string $param, ModifyResourceAtInlinedPath body: $response->getBody()->getContents(), ); } + + /** + * POST bytes with path param returning object + * + * @param string $param + * @param ?array{ + * baseUrl?: string, + * maxRetries?: int, + * timeout?: float, + * headers?: array, + * queryParameters?: array, + * bodyProperties?: array, + * } $options + * @return ObjectWithRequiredField + * @throws SeedException + * @throws SeedApiException + */ + public function uploadWithPath(string $param, ?array $options = null): ObjectWithRequiredField + { + $options = array_merge($this->options, $options ?? []); + try { + $response = $this->client->sendRequest( + new JsonApiRequest( + baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '', + path: "/params/path/{$param}", + method: HttpMethod::POST, + ), + $options, + ); + $statusCode = $response->getStatusCode(); + if ($statusCode >= 200 && $statusCode < 400) { + $json = $response->getBody()->getContents(); + return ObjectWithRequiredField::fromJson($json); + } + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (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/no-custom-config/src/dynamic-snippets/example33/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example33/snippet.php index 0c0fa0145287..ae2372c4b0e1 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example33/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example33/snippet.php @@ -10,6 +10,7 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnString( - 'string', +$client->endpoints->params->uploadWithPath( + 'upload-path', + , ); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example34/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example34/snippet.php index ee82f953084c..0c0fa0145287 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example34/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example34/snippet.php @@ -10,6 +10,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnInt( - 1, +$client->endpoints->primitive->getAndReturnString( + 'string', ); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example35/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example35/snippet.php index 57cf71178518..ee82f953084c 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example35/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example35/snippet.php @@ -10,6 +10,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnLong( - 1000000, +$client->endpoints->primitive->getAndReturnInt( + 1, ); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example36/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example36/snippet.php index e8cdfee5a715..57cf71178518 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example36/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example36/snippet.php @@ -10,6 +10,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnDouble( - 1.1, +$client->endpoints->primitive->getAndReturnLong( + 1000000, ); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example37/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example37/snippet.php index 2e162ac5266a..e8cdfee5a715 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example37/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example37/snippet.php @@ -10,6 +10,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnBool( - true, +$client->endpoints->primitive->getAndReturnDouble( + 1.1, ); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example38/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example38/snippet.php index 83a2ba148257..2e162ac5266a 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example38/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example38/snippet.php @@ -3,7 +3,6 @@ namespace Example; use Seed\SeedClient; -use DateTime; $client = new SeedClient( token: '', @@ -11,6 +10,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnDatetime( - new DateTime('2024-01-15T09:30:00Z'), +$client->endpoints->primitive->getAndReturnBool( + true, ); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example39/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example39/snippet.php index 3c58d2e1edc1..83a2ba148257 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example39/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example39/snippet.php @@ -11,6 +11,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnDate( - new DateTime('2023-01-15'), +$client->endpoints->primitive->getAndReturnDatetime( + new DateTime('2024-01-15T09:30:00Z'), ); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example40/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example40/snippet.php index 18cfb7bf946d..3c58d2e1edc1 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example40/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example40/snippet.php @@ -3,6 +3,7 @@ namespace Example; use Seed\SeedClient; +use DateTime; $client = new SeedClient( token: '', @@ -10,6 +11,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnUuid( - 'd5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32', +$client->endpoints->primitive->getAndReturnDate( + new DateTime('2023-01-15'), ); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example41/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example41/snippet.php index 2bbc774f833c..18cfb7bf946d 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example41/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example41/snippet.php @@ -10,6 +10,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnBase64( - 'SGVsbG8gd29ybGQh', +$client->endpoints->primitive->getAndReturnUuid( + 'd5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32', ); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example42/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example42/snippet.php index c49cc8af4b8a..2bbc774f833c 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example42/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example42/snippet.php @@ -10,6 +10,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->put->add( - 'id', +$client->endpoints->primitive->getAndReturnBase64( + 'SGVsbG8gd29ybGQh', ); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example43/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example43/snippet.php index a62ee99ec201..c49cc8af4b8a 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example43/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example43/snippet.php @@ -3,8 +3,6 @@ namespace Example; use Seed\SeedClient; -use Seed\Types\Union\Types\Animal; -use Seed\Types\Union\Types\Dog; $client = new SeedClient( token: '', @@ -12,9 +10,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->union->getAndReturnUnion( - Animal::dog(new Dog([ - 'name' => 'name', - 'likesToWoof' => true, - ])), +$client->endpoints->put->add( + 'id', ); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example44/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example44/snippet.php index a3347704fac8..a62ee99ec201 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example44/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example44/snippet.php @@ -3,6 +3,8 @@ namespace Example; use Seed\SeedClient; +use Seed\Types\Union\Types\Animal; +use Seed\Types\Union\Types\Dog; $client = new SeedClient( token: '', @@ -10,4 +12,9 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->urls->withMixedCase(); +$client->endpoints->union->getAndReturnUnion( + Animal::dog(new Dog([ + 'name' => 'name', + 'likesToWoof' => true, + ])), +); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example45/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example45/snippet.php index 64f8cfcd08db..a3347704fac8 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example45/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example45/snippet.php @@ -10,4 +10,4 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->urls->noEndingSlash(); +$client->endpoints->urls->withMixedCase(); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example46/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example46/snippet.php index 396eeda12609..64f8cfcd08db 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example46/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example46/snippet.php @@ -10,4 +10,4 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->urls->withEndingSlash(); +$client->endpoints->urls->noEndingSlash(); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example47/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example47/snippet.php index a641c1546d94..396eeda12609 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example47/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example47/snippet.php @@ -10,4 +10,4 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->urls->withUnderscores(); +$client->endpoints->urls->withEndingSlash(); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example48/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example48/snippet.php index 499d61eef1a7..a641c1546d94 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example48/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example48/snippet.php @@ -3,9 +3,6 @@ namespace Example; use Seed\SeedClient; -use Seed\InlinedRequests\Requests\PostWithObjectBody; -use Seed\Types\Object\Types\ObjectWithOptionalField; -use DateTime; $client = new SeedClient( token: '', @@ -13,31 +10,4 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->inlinedRequests->postWithObjectBodyandResponse( - new PostWithObjectBody([ - 'string' => 'string', - 'integer' => 1, - 'nestedObject' => new ObjectWithOptionalField([ - 'string' => 'string', - 'integer' => 1, - 'long' => 1000000, - 'double' => 1.1, - 'bool' => true, - 'datetime' => new DateTime('2024-01-15T09:30:00Z'), - 'date' => new DateTime('2023-01-15'), - 'uuid' => 'd5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32', - 'base64' => 'SGVsbG8gd29ybGQh', - 'list' => [ - 'list', - 'list', - ], - 'set' => [ - 'set', - ], - 'map' => [ - 1 => 'map', - ], - 'bigint' => '1000000', - ]), - ]), -); +$client->endpoints->urls->withUnderscores(); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example50/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example50/snippet.php index 2e37fef3bec3..499d61eef1a7 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example50/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example50/snippet.php @@ -3,6 +3,9 @@ namespace Example; use Seed\SeedClient; +use Seed\InlinedRequests\Requests\PostWithObjectBody; +use Seed\Types\Object\Types\ObjectWithOptionalField; +use DateTime; $client = new SeedClient( token: '', @@ -10,8 +13,31 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->noAuth->postWithNoAuth( - [ - 'key' => "value", - ], +$client->inlinedRequests->postWithObjectBodyandResponse( + new PostWithObjectBody([ + 'string' => 'string', + 'integer' => 1, + 'nestedObject' => new ObjectWithOptionalField([ + 'string' => 'string', + 'integer' => 1, + 'long' => 1000000, + 'double' => 1.1, + 'bool' => true, + 'datetime' => new DateTime('2024-01-15T09:30:00Z'), + 'date' => new DateTime('2023-01-15'), + 'uuid' => 'd5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32', + 'base64' => 'SGVsbG8gd29ybGQh', + 'list' => [ + 'list', + 'list', + ], + 'set' => [ + 'set', + ], + 'map' => [ + 1 => 'map', + ], + 'bigint' => '1000000', + ]), + ]), ); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example52/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example52/snippet.php index c22a9090adf8..2e37fef3bec3 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example52/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example52/snippet.php @@ -10,4 +10,8 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->noReqBody->getWithNoRequestBody(); +$client->noAuth->postWithNoAuth( + [ + 'key' => "value", + ], +); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example53/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example53/snippet.php index ba387851a5c7..c22a9090adf8 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example53/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example53/snippet.php @@ -10,4 +10,4 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->noReqBody->postWithNoRequestBody(); +$client->noReqBody->getWithNoRequestBody(); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example54/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example54/snippet.php index e36d7f24d7ec..ba387851a5c7 100644 --- a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example54/snippet.php +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example54/snippet.php @@ -3,7 +3,6 @@ namespace Example; use Seed\SeedClient; -use Seed\ReqWithHeaders\Requests\ReqWithHeaders; $client = new SeedClient( token: '', @@ -11,10 +10,4 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->reqWithHeaders->getWithCustomHeader( - new ReqWithHeaders([ - 'xTestServiceHeader' => 'X-TEST-SERVICE-HEADER', - 'xTestEndpointHeader' => 'X-TEST-ENDPOINT-HEADER', - 'body' => 'string', - ]), -); +$client->noReqBody->postWithNoRequestBody(); diff --git a/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example55/snippet.php b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example55/snippet.php new file mode 100644 index 000000000000..e36d7f24d7ec --- /dev/null +++ b/seed/php-sdk/exhaustive/no-custom-config/src/dynamic-snippets/example55/snippet.php @@ -0,0 +1,20 @@ +', + options: [ + 'baseUrl' => 'https://api.fern.com', + ], +); +$client->reqWithHeaders->getWithCustomHeader( + new ReqWithHeaders([ + 'xTestServiceHeader' => 'X-TEST-SERVICE-HEADER', + 'xTestEndpointHeader' => 'X-TEST-ENDPOINT-HEADER', + 'body' => 'string', + ]), +); diff --git a/seed/php-sdk/exhaustive/wire-tests/reference.md b/seed/php-sdk/exhaustive/wire-tests/reference.md index 5dde965101ff..7488b8757196 100644 --- a/seed/php-sdk/exhaustive/wire-tests/reference.md +++ b/seed/php-sdk/exhaustive/wire-tests/reference.md @@ -1812,6 +1812,63 @@ $client->endpoints->params->modifyWithPath( + + + + +
$client->endpoints->params->uploadWithPath($param) -> ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```php +$client->endpoints->params->uploadWithPath( + 'upload-path', + , +); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**$param:** `string` + +
+
+
+
+ +
diff --git a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/ParamsClient.php b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/ParamsClient.php index 7d8c705eb873..c8d971fce789 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/ParamsClient.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/Endpoints/Params/ParamsClient.php @@ -16,6 +16,7 @@ use Seed\Endpoints\Params\Requests\GetWithPathAndQuery; use Seed\Endpoints\Params\Requests\GetWithInlinePathAndQuery; use Seed\Endpoints\Params\Requests\ModifyResourceAtInlinedPath; +use Seed\Types\Object\Types\ObjectWithRequiredField; class ParamsClient { @@ -416,4 +417,49 @@ public function modifyWithInlinePath(string $param, ModifyResourceAtInlinedPath body: $response->getBody()->getContents(), ); } + + /** + * POST bytes with path param returning object + * + * @param string $param + * @param ?array{ + * baseUrl?: string, + * maxRetries?: int, + * timeout?: float, + * headers?: array, + * queryParameters?: array, + * bodyProperties?: array, + * } $options + * @return ObjectWithRequiredField + * @throws SeedException + * @throws SeedApiException + */ + public function uploadWithPath(string $param, ?array $options = null): ObjectWithRequiredField + { + $options = array_merge($this->options, $options ?? []); + try { + $response = $this->client->sendRequest( + new JsonApiRequest( + baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '', + path: "/params/path/{$param}", + method: HttpMethod::POST, + ), + $options, + ); + $statusCode = $response->getStatusCode(); + if ($statusCode >= 200 && $statusCode < 400) { + $json = $response->getBody()->getContents(); + return ObjectWithRequiredField::fromJson($json); + } + } catch (JsonException $e) { + throw new SeedException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e); + } catch (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/wire-tests/src/dynamic-snippets/example33/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example33/snippet.php index 0c0fa0145287..ae2372c4b0e1 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example33/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example33/snippet.php @@ -10,6 +10,7 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnString( - 'string', +$client->endpoints->params->uploadWithPath( + 'upload-path', + , ); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example34/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example34/snippet.php index ee82f953084c..0c0fa0145287 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example34/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example34/snippet.php @@ -10,6 +10,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnInt( - 1, +$client->endpoints->primitive->getAndReturnString( + 'string', ); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example35/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example35/snippet.php index 57cf71178518..ee82f953084c 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example35/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example35/snippet.php @@ -10,6 +10,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnLong( - 1000000, +$client->endpoints->primitive->getAndReturnInt( + 1, ); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example36/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example36/snippet.php index e8cdfee5a715..57cf71178518 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example36/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example36/snippet.php @@ -10,6 +10,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnDouble( - 1.1, +$client->endpoints->primitive->getAndReturnLong( + 1000000, ); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example37/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example37/snippet.php index 2e162ac5266a..e8cdfee5a715 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example37/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example37/snippet.php @@ -10,6 +10,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnBool( - true, +$client->endpoints->primitive->getAndReturnDouble( + 1.1, ); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example38/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example38/snippet.php index 83a2ba148257..2e162ac5266a 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example38/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example38/snippet.php @@ -3,7 +3,6 @@ namespace Example; use Seed\SeedClient; -use DateTime; $client = new SeedClient( token: '', @@ -11,6 +10,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnDatetime( - new DateTime('2024-01-15T09:30:00Z'), +$client->endpoints->primitive->getAndReturnBool( + true, ); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example39/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example39/snippet.php index 3c58d2e1edc1..83a2ba148257 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example39/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example39/snippet.php @@ -11,6 +11,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnDate( - new DateTime('2023-01-15'), +$client->endpoints->primitive->getAndReturnDatetime( + new DateTime('2024-01-15T09:30:00Z'), ); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example40/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example40/snippet.php index 18cfb7bf946d..3c58d2e1edc1 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example40/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example40/snippet.php @@ -3,6 +3,7 @@ namespace Example; use Seed\SeedClient; +use DateTime; $client = new SeedClient( token: '', @@ -10,6 +11,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnUuid( - 'd5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32', +$client->endpoints->primitive->getAndReturnDate( + new DateTime('2023-01-15'), ); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example41/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example41/snippet.php index 2bbc774f833c..18cfb7bf946d 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example41/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example41/snippet.php @@ -10,6 +10,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->primitive->getAndReturnBase64( - 'SGVsbG8gd29ybGQh', +$client->endpoints->primitive->getAndReturnUuid( + 'd5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32', ); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example42/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example42/snippet.php index c49cc8af4b8a..2bbc774f833c 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example42/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example42/snippet.php @@ -10,6 +10,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->put->add( - 'id', +$client->endpoints->primitive->getAndReturnBase64( + 'SGVsbG8gd29ybGQh', ); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example43/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example43/snippet.php index a62ee99ec201..c49cc8af4b8a 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example43/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example43/snippet.php @@ -3,8 +3,6 @@ namespace Example; use Seed\SeedClient; -use Seed\Types\Union\Types\Animal; -use Seed\Types\Union\Types\Dog; $client = new SeedClient( token: '', @@ -12,9 +10,6 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->union->getAndReturnUnion( - Animal::dog(new Dog([ - 'name' => 'name', - 'likesToWoof' => true, - ])), +$client->endpoints->put->add( + 'id', ); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example44/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example44/snippet.php index a3347704fac8..a62ee99ec201 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example44/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example44/snippet.php @@ -3,6 +3,8 @@ namespace Example; use Seed\SeedClient; +use Seed\Types\Union\Types\Animal; +use Seed\Types\Union\Types\Dog; $client = new SeedClient( token: '', @@ -10,4 +12,9 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->urls->withMixedCase(); +$client->endpoints->union->getAndReturnUnion( + Animal::dog(new Dog([ + 'name' => 'name', + 'likesToWoof' => true, + ])), +); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example45/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example45/snippet.php index 64f8cfcd08db..a3347704fac8 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example45/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example45/snippet.php @@ -10,4 +10,4 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->urls->noEndingSlash(); +$client->endpoints->urls->withMixedCase(); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example46/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example46/snippet.php index 396eeda12609..64f8cfcd08db 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example46/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example46/snippet.php @@ -10,4 +10,4 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->urls->withEndingSlash(); +$client->endpoints->urls->noEndingSlash(); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example47/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example47/snippet.php index a641c1546d94..396eeda12609 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example47/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example47/snippet.php @@ -10,4 +10,4 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->endpoints->urls->withUnderscores(); +$client->endpoints->urls->withEndingSlash(); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example48/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example48/snippet.php index 499d61eef1a7..a641c1546d94 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example48/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example48/snippet.php @@ -3,9 +3,6 @@ namespace Example; use Seed\SeedClient; -use Seed\InlinedRequests\Requests\PostWithObjectBody; -use Seed\Types\Object\Types\ObjectWithOptionalField; -use DateTime; $client = new SeedClient( token: '', @@ -13,31 +10,4 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->inlinedRequests->postWithObjectBodyandResponse( - new PostWithObjectBody([ - 'string' => 'string', - 'integer' => 1, - 'nestedObject' => new ObjectWithOptionalField([ - 'string' => 'string', - 'integer' => 1, - 'long' => 1000000, - 'double' => 1.1, - 'bool' => true, - 'datetime' => new DateTime('2024-01-15T09:30:00Z'), - 'date' => new DateTime('2023-01-15'), - 'uuid' => 'd5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32', - 'base64' => 'SGVsbG8gd29ybGQh', - 'list' => [ - 'list', - 'list', - ], - 'set' => [ - 'set', - ], - 'map' => [ - 1 => 'map', - ], - 'bigint' => '1000000', - ]), - ]), -); +$client->endpoints->urls->withUnderscores(); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example50/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example50/snippet.php index 2e37fef3bec3..499d61eef1a7 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example50/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example50/snippet.php @@ -3,6 +3,9 @@ namespace Example; use Seed\SeedClient; +use Seed\InlinedRequests\Requests\PostWithObjectBody; +use Seed\Types\Object\Types\ObjectWithOptionalField; +use DateTime; $client = new SeedClient( token: '', @@ -10,8 +13,31 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->noAuth->postWithNoAuth( - [ - 'key' => "value", - ], +$client->inlinedRequests->postWithObjectBodyandResponse( + new PostWithObjectBody([ + 'string' => 'string', + 'integer' => 1, + 'nestedObject' => new ObjectWithOptionalField([ + 'string' => 'string', + 'integer' => 1, + 'long' => 1000000, + 'double' => 1.1, + 'bool' => true, + 'datetime' => new DateTime('2024-01-15T09:30:00Z'), + 'date' => new DateTime('2023-01-15'), + 'uuid' => 'd5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32', + 'base64' => 'SGVsbG8gd29ybGQh', + 'list' => [ + 'list', + 'list', + ], + 'set' => [ + 'set', + ], + 'map' => [ + 1 => 'map', + ], + 'bigint' => '1000000', + ]), + ]), ); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example52/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example52/snippet.php index c22a9090adf8..2e37fef3bec3 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example52/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example52/snippet.php @@ -10,4 +10,8 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->noReqBody->getWithNoRequestBody(); +$client->noAuth->postWithNoAuth( + [ + 'key' => "value", + ], +); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example53/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example53/snippet.php index ba387851a5c7..c22a9090adf8 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example53/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example53/snippet.php @@ -10,4 +10,4 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->noReqBody->postWithNoRequestBody(); +$client->noReqBody->getWithNoRequestBody(); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example54/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example54/snippet.php index e36d7f24d7ec..ba387851a5c7 100644 --- a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example54/snippet.php +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example54/snippet.php @@ -3,7 +3,6 @@ namespace Example; use Seed\SeedClient; -use Seed\ReqWithHeaders\Requests\ReqWithHeaders; $client = new SeedClient( token: '', @@ -11,10 +10,4 @@ 'baseUrl' => 'https://api.fern.com', ], ); -$client->reqWithHeaders->getWithCustomHeader( - new ReqWithHeaders([ - 'xTestServiceHeader' => 'X-TEST-SERVICE-HEADER', - 'xTestEndpointHeader' => 'X-TEST-ENDPOINT-HEADER', - 'body' => 'string', - ]), -); +$client->noReqBody->postWithNoRequestBody(); diff --git a/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example55/snippet.php b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example55/snippet.php new file mode 100644 index 000000000000..e36d7f24d7ec --- /dev/null +++ b/seed/php-sdk/exhaustive/wire-tests/src/dynamic-snippets/example55/snippet.php @@ -0,0 +1,20 @@ +', + options: [ + 'baseUrl' => 'https://api.fern.com', + ], +); +$client->reqWithHeaders->getWithCustomHeader( + new ReqWithHeaders([ + 'xTestServiceHeader' => 'X-TEST-SERVICE-HEADER', + 'xTestEndpointHeader' => 'X-TEST-ENDPOINT-HEADER', + 'body' => 'string', + ]), +); diff --git a/seed/php-sdk/exhaustive/wire-tests/tests/Wire/EndpointsParamsWireTest.php b/seed/php-sdk/exhaustive/wire-tests/tests/Wire/EndpointsParamsWireTest.php index 5fb1c98ee7fe..0c258872a8d8 100644 --- a/seed/php-sdk/exhaustive/wire-tests/tests/Wire/EndpointsParamsWireTest.php +++ b/seed/php-sdk/exhaustive/wire-tests/tests/Wire/EndpointsParamsWireTest.php @@ -196,6 +196,28 @@ public function testModifyWithInlinePath(): void { ); } + /** + */ + public function testUploadWithPath(): void { + $testId = 'endpoints.params.upload_with_path.0'; + $this->client->endpoints->params->uploadWithPath( + 'upload-path', + , + [ + 'headers' => [ + 'X-Test-Id' => 'endpoints.params.upload_with_path.0', + ], + ], + ); + $this->verifyRequestCount( + $testId, + "POST", + "/params/path/upload-path", + null, + 1 + ); + } + /** */ protected function setUp(): void { diff --git a/seed/php-sdk/exhaustive/wire-tests/wiremock/wiremock-mappings.json b/seed/php-sdk/exhaustive/wire-tests/wiremock/wiremock-mappings.json index a1cfc7be04cb..12cc9e8a7b78 100644 --- a/seed/php-sdk/exhaustive/wire-tests/wiremock/wiremock-mappings.json +++ b/seed/php-sdk/exhaustive/wire-tests/wiremock/wiremock-mappings.json @@ -890,6 +890,37 @@ } } }, + { + "id": "658d6145-14f6-48d3-9c66-c63e01eb6fd2", + "name": "uploadWithPath - default", + "request": { + "urlPathTemplate": "/params/path/{param}", + "method": "POST", + "pathParameters": { + "param": { + "equalTo": "upload-path" + } + } + }, + "response": { + "status": 200, + "body": "{\n \"string\": \"uploaded\"\n}", + "headers": { + "Content-Type": "application/json" + } + }, + "uuid": "658d6145-14f6-48d3-9c66-c63e01eb6fd2", + "persistent": true, + "priority": 3, + "metadata": { + "mocklab": { + "created": { + "at": "2020-01-01T00:00:00.000Z", + "via": "SYSTEM" + } + } + } + }, { "id": "0506ae7c-87b7-4cd2-9e50-31d60f81893f", "name": "getAndReturnString - default", @@ -1422,6 +1453,6 @@ } ], "meta": { - "total": 52 + "total": 53 } } \ No newline at end of file From 04a9d7ea8edf3d27dcbd11a2ed9e3d0af88c9c9b Mon Sep 17 00:00:00 2001 From: Alex McKinney Date: Tue, 24 Feb 2026 21:02:41 -0500 Subject: [PATCH 08/20] chore(cli): Remove console.debug logs (#12733) --- .../core/src/services/fdrGeneratorsSdk.ts | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/packages/core/src/services/fdrGeneratorsSdk.ts b/packages/core/src/services/fdrGeneratorsSdk.ts index 1ba8dbfc8482..35e5fd3b427c 100644 --- a/packages/core/src/services/fdrGeneratorsSdk.ts +++ b/packages/core/src/services/fdrGeneratorsSdk.ts @@ -7,8 +7,6 @@ export function createFdrGeneratorsSdkService({ environment?: string; token: (() => string) | string | undefined; }): FdrClient { - // biome-ignore lint/suspicious/noConsole: intentional debug logging for FDR registry diagnostics - console.debug(`[FDR] Creating generators SDK service with environment: ${environment}`); return new FdrClient({ environment, token @@ -22,42 +20,22 @@ export interface GeneratorInfo { export async function getIrVersionForGenerator(invocation: GeneratorInfo): Promise { const fdr = createFdrGeneratorsSdkService({ token: undefined }); - // biome-ignore lint/suspicious/noConsole: intentional debug logging for FDR registry diagnostics - console.debug(`[FDR] getIrVersionForGenerator: looking up generator by image: ${invocation.name}`); const generatorEntity = await fdr.generators.getGeneratorByImage({ dockerImage: invocation.name }); // Again, this is to allow for offline usage, and other transient errors if (!generatorEntity.ok || generatorEntity.body == null) { - // biome-ignore lint/suspicious/noConsole: intentional debug logging for FDR registry diagnostics - console.debug( - `[FDR] getIrVersionForGenerator: getGeneratorByImage failed for ${invocation.name}`, - generatorEntity - ); return undefined; } - // biome-ignore lint/suspicious/noConsole: intentional debug logging for FDR registry diagnostics - console.debug( - `[FDR] getIrVersionForGenerator: found generator id=${generatorEntity.body.id}, fetching release version=${invocation.version}` - ); const generatorRelease = await fdr.generators.versions.getGeneratorRelease( generatorEntity.body.id, invocation.version ); if (generatorRelease.ok) { - // biome-ignore lint/suspicious/noConsole: intentional debug logging for FDR registry diagnostics - console.debug( - `[FDR] getIrVersionForGenerator: got release for ${invocation.name}@${invocation.version}, irVersion=${generatorRelease.body.irVersion}` - ); // We've pulled the generator release, let's get it's IR version return generatorRelease.body.irVersion; } - // biome-ignore lint/suspicious/noConsole: intentional debug logging for FDR registry diagnostics - console.debug( - `[FDR] getIrVersionForGenerator: getGeneratorRelease failed for ${generatorEntity.body.id}@${invocation.version}`, - generatorRelease - ); return undefined; } From cb0c7d5fd9ff2220c1763239ef3337d43b072085 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 24 Feb 2026 21:02:56 -0500 Subject: [PATCH 09/20] chore(swift): update swift-sdk seed (#12745) Co-authored-by: amckinney --- .../exhaustive/Snippets/Example33.swift | 5 +- .../exhaustive/Snippets/Example34.swift | 2 +- .../exhaustive/Snippets/Example35.swift | 2 +- .../exhaustive/Snippets/Example36.swift | 2 +- .../exhaustive/Snippets/Example37.swift | 2 +- .../exhaustive/Snippets/Example38.swift | 2 +- .../exhaustive/Snippets/Example39.swift | 2 +- .../exhaustive/Snippets/Example40.swift | 2 +- .../exhaustive/Snippets/Example41.swift | 2 +- .../exhaustive/Snippets/Example42.swift | 2 +- .../exhaustive/Snippets/Example43.swift | 7 +- .../exhaustive/Snippets/Example44.swift | 7 +- .../exhaustive/Snippets/Example45.swift | 2 +- .../exhaustive/Snippets/Example46.swift | 2 +- .../exhaustive/Snippets/Example47.swift | 2 +- .../exhaustive/Snippets/Example48.swift | 23 +----- .../exhaustive/Snippets/Example50.swift | 25 +++++- .../exhaustive/Snippets/Example52.swift | 4 +- .../exhaustive/Snippets/Example53.swift | 2 +- .../exhaustive/Snippets/Example54.swift | 2 +- .../exhaustive/Snippets/Example55.swift | 13 +++ .../Endpoints/Params/ParamsClient.swift | 14 ++++ .../Params/ParamsClientWireTests.swift | 27 ++++++ seed/swift-sdk/exhaustive/reference.md | 82 +++++++++++++++++++ 24 files changed, 187 insertions(+), 48 deletions(-) create mode 100644 seed/swift-sdk/exhaustive/Snippets/Example55.swift diff --git a/seed/swift-sdk/exhaustive/Snippets/Example33.swift b/seed/swift-sdk/exhaustive/Snippets/Example33.swift index a5f8cfd7e1b0..6b547f957b34 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example33.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example33.swift @@ -7,7 +7,10 @@ private func main() async throws { token: "" ) - _ = try await client.endpoints.primitive.getAndReturnString(request: "string") + _ = try await client.endpoints.params.uploadWithPath( + param: "upload-path", + request: Data("data".utf8) + ) } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example34.swift b/seed/swift-sdk/exhaustive/Snippets/Example34.swift index 734915449618..a5f8cfd7e1b0 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example34.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example34.swift @@ -7,7 +7,7 @@ private func main() async throws { token: "" ) - _ = try await client.endpoints.primitive.getAndReturnInt(request: 1) + _ = try await client.endpoints.primitive.getAndReturnString(request: "string") } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example35.swift b/seed/swift-sdk/exhaustive/Snippets/Example35.swift index cf39fe1f4945..734915449618 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example35.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example35.swift @@ -7,7 +7,7 @@ private func main() async throws { token: "" ) - _ = try await client.endpoints.primitive.getAndReturnLong(request: 1000000) + _ = try await client.endpoints.primitive.getAndReturnInt(request: 1) } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example36.swift b/seed/swift-sdk/exhaustive/Snippets/Example36.swift index f0d8498db856..cf39fe1f4945 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example36.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example36.swift @@ -7,7 +7,7 @@ private func main() async throws { token: "" ) - _ = try await client.endpoints.primitive.getAndReturnDouble(request: 1.1) + _ = try await client.endpoints.primitive.getAndReturnLong(request: 1000000) } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example37.swift b/seed/swift-sdk/exhaustive/Snippets/Example37.swift index ad5a70feed7e..f0d8498db856 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example37.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example37.swift @@ -7,7 +7,7 @@ private func main() async throws { token: "" ) - _ = try await client.endpoints.primitive.getAndReturnBool(request: true) + _ = try await client.endpoints.primitive.getAndReturnDouble(request: 1.1) } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example38.swift b/seed/swift-sdk/exhaustive/Snippets/Example38.swift index 524142d224e7..ad5a70feed7e 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example38.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example38.swift @@ -7,7 +7,7 @@ private func main() async throws { token: "" ) - _ = try await client.endpoints.primitive.getAndReturnDatetime(request: try! Date("2024-01-15T09:30:00Z", strategy: .iso8601)) + _ = try await client.endpoints.primitive.getAndReturnBool(request: true) } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example39.swift b/seed/swift-sdk/exhaustive/Snippets/Example39.swift index e85723b0e849..524142d224e7 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example39.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example39.swift @@ -7,7 +7,7 @@ private func main() async throws { token: "" ) - _ = try await client.endpoints.primitive.getAndReturnDate(request: CalendarDate("2023-01-15")!) + _ = try await client.endpoints.primitive.getAndReturnDatetime(request: try! Date("2024-01-15T09:30:00Z", strategy: .iso8601)) } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example40.swift b/seed/swift-sdk/exhaustive/Snippets/Example40.swift index 197db759d8ad..e85723b0e849 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example40.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example40.swift @@ -7,7 +7,7 @@ private func main() async throws { token: "" ) - _ = try await client.endpoints.primitive.getAndReturnUuid(request: UUID(uuidString: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")!) + _ = try await client.endpoints.primitive.getAndReturnDate(request: CalendarDate("2023-01-15")!) } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example41.swift b/seed/swift-sdk/exhaustive/Snippets/Example41.swift index 5df4d0390da0..197db759d8ad 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example41.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example41.swift @@ -7,7 +7,7 @@ private func main() async throws { token: "" ) - _ = try await client.endpoints.primitive.getAndReturnBase64(request: "SGVsbG8gd29ybGQh") + _ = try await client.endpoints.primitive.getAndReturnUuid(request: UUID(uuidString: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")!) } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example42.swift b/seed/swift-sdk/exhaustive/Snippets/Example42.swift index b12d23a6c228..5df4d0390da0 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example42.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example42.swift @@ -7,7 +7,7 @@ private func main() async throws { token: "" ) - _ = try await client.endpoints.put.add(id: "id") + _ = try await client.endpoints.primitive.getAndReturnBase64(request: "SGVsbG8gd29ybGQh") } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example43.swift b/seed/swift-sdk/exhaustive/Snippets/Example43.swift index 7f4adb78e70e..b12d23a6c228 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example43.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example43.swift @@ -7,12 +7,7 @@ private func main() async throws { token: "" ) - _ = try await client.endpoints.union.getAndReturnUnion(request: Animal.dog( - .init( - name: "name", - likesToWoof: true - ) - )) + _ = try await client.endpoints.put.add(id: "id") } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example44.swift b/seed/swift-sdk/exhaustive/Snippets/Example44.swift index 3ffb8341d7e7..7f4adb78e70e 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example44.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example44.swift @@ -7,7 +7,12 @@ private func main() async throws { token: "" ) - _ = try await client.endpoints.urls.withMixedCase() + _ = try await client.endpoints.union.getAndReturnUnion(request: Animal.dog( + .init( + name: "name", + likesToWoof: true + ) + )) } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example45.swift b/seed/swift-sdk/exhaustive/Snippets/Example45.swift index 0c6267220325..3ffb8341d7e7 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example45.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example45.swift @@ -7,7 +7,7 @@ private func main() async throws { token: "" ) - _ = try await client.endpoints.urls.noEndingSlash() + _ = try await client.endpoints.urls.withMixedCase() } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example46.swift b/seed/swift-sdk/exhaustive/Snippets/Example46.swift index 62a82e6db058..0c6267220325 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example46.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example46.swift @@ -7,7 +7,7 @@ private func main() async throws { token: "" ) - _ = try await client.endpoints.urls.withEndingSlash() + _ = try await client.endpoints.urls.noEndingSlash() } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example47.swift b/seed/swift-sdk/exhaustive/Snippets/Example47.swift index 841664719f03..62a82e6db058 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example47.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example47.swift @@ -7,7 +7,7 @@ private func main() async throws { token: "" ) - _ = try await client.endpoints.urls.withUnderscores() + _ = try await client.endpoints.urls.withEndingSlash() } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example48.swift b/seed/swift-sdk/exhaustive/Snippets/Example48.swift index d369f169565f..841664719f03 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example48.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example48.swift @@ -7,28 +7,7 @@ private func main() async throws { token: "" ) - _ = try await client.inlinedRequests.postWithObjectBodyandResponse(request: .init( - string: "string", - integer: 1, - nestedObject: ObjectWithOptionalField( - string: "string", - integer: 1, - long: 1000000, - double: 1.1, - bool: true, - datetime: try! Date("2024-01-15T09:30:00Z", strategy: .iso8601), - date: CalendarDate("2023-01-15")!, - uuid: UUID(uuidString: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")!, - base64: "SGVsbG8gd29ybGQh", - list: [ - "list", - "list" - ], - map: [ - 1: "map" - ] - ) - )) + _ = try await client.endpoints.urls.withUnderscores() } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example50.swift b/seed/swift-sdk/exhaustive/Snippets/Example50.swift index 5093beecde12..d369f169565f 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example50.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example50.swift @@ -7,9 +7,28 @@ private func main() async throws { token: "" ) - _ = try await client.noAuth.postWithNoAuth(request: .object([ - "key": .string("value") - ])) + _ = try await client.inlinedRequests.postWithObjectBodyandResponse(request: .init( + string: "string", + integer: 1, + nestedObject: ObjectWithOptionalField( + string: "string", + integer: 1, + long: 1000000, + double: 1.1, + bool: true, + datetime: try! Date("2024-01-15T09:30:00Z", strategy: .iso8601), + date: CalendarDate("2023-01-15")!, + uuid: UUID(uuidString: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")!, + base64: "SGVsbG8gd29ybGQh", + list: [ + "list", + "list" + ], + map: [ + 1: "map" + ] + ) + )) } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example52.swift b/seed/swift-sdk/exhaustive/Snippets/Example52.swift index 528e0c41f194..5093beecde12 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example52.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example52.swift @@ -7,7 +7,9 @@ private func main() async throws { token: "" ) - _ = try await client.noReqBody.getWithNoRequestBody() + _ = try await client.noAuth.postWithNoAuth(request: .object([ + "key": .string("value") + ])) } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example53.swift b/seed/swift-sdk/exhaustive/Snippets/Example53.swift index d4cf4a4172cd..528e0c41f194 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example53.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example53.swift @@ -7,7 +7,7 @@ private func main() async throws { token: "" ) - _ = try await client.noReqBody.postWithNoRequestBody() + _ = try await client.noReqBody.getWithNoRequestBody() } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example54.swift b/seed/swift-sdk/exhaustive/Snippets/Example54.swift index 6e790f64e501..d4cf4a4172cd 100644 --- a/seed/swift-sdk/exhaustive/Snippets/Example54.swift +++ b/seed/swift-sdk/exhaustive/Snippets/Example54.swift @@ -7,7 +7,7 @@ private func main() async throws { token: "" ) - _ = try await client.reqWithHeaders.getWithCustomHeader(request: .init(body: "string")) + _ = try await client.noReqBody.postWithNoRequestBody() } try await main() diff --git a/seed/swift-sdk/exhaustive/Snippets/Example55.swift b/seed/swift-sdk/exhaustive/Snippets/Example55.swift new file mode 100644 index 000000000000..6e790f64e501 --- /dev/null +++ b/seed/swift-sdk/exhaustive/Snippets/Example55.swift @@ -0,0 +1,13 @@ +import Foundation +import Exhaustive + +private func main() async throws { + let client = ExhaustiveClient( + baseURL: "https://api.fern.com", + token: "" + ) + + _ = try await client.reqWithHeaders.getWithCustomHeader(request: .init(body: "string")) +} + +try await main() diff --git a/seed/swift-sdk/exhaustive/Sources/Resources/Endpoints/Params/ParamsClient.swift b/seed/swift-sdk/exhaustive/Sources/Resources/Endpoints/Params/ParamsClient.swift index 606e346b6acd..0f62b527ce88 100644 --- a/seed/swift-sdk/exhaustive/Sources/Resources/Endpoints/Params/ParamsClient.swift +++ b/seed/swift-sdk/exhaustive/Sources/Resources/Endpoints/Params/ParamsClient.swift @@ -114,4 +114,18 @@ public final class ParamsClient: Sendable { responseType: String.self ) } + + /// POST bytes with path param returning object + /// + /// - Parameter requestOptions: Additional options for configuring the request, such as custom headers or timeout settings. + public func uploadWithPath(param: String, request: Data, requestOptions: RequestOptions? = nil) async throws -> ObjectWithRequiredField { + return try await httpClient.performRequest( + method: .post, + path: "/params/path/\(param)", + contentType: .applicationOctetStream, + body: request, + requestOptions: requestOptions, + responseType: ObjectWithRequiredField.self + ) + } } \ No newline at end of file diff --git a/seed/swift-sdk/exhaustive/Tests/Wire/Resources/Endpoints/Params/ParamsClientWireTests.swift b/seed/swift-sdk/exhaustive/Tests/Wire/Resources/Endpoints/Params/ParamsClientWireTests.swift index 2d17ff7d8bdc..1bda25a1a558 100644 --- a/seed/swift-sdk/exhaustive/Tests/Wire/Resources/Endpoints/Params/ParamsClientWireTests.swift +++ b/seed/swift-sdk/exhaustive/Tests/Wire/Resources/Endpoints/Params/ParamsClientWireTests.swift @@ -92,4 +92,31 @@ import Exhaustive ) try #require(response == expectedResponse) } + + @Test func uploadWithPath1() async throws -> Void { + let stub = HTTPStub() + stub.setResponse( + body: Data( + """ + { + "string": "uploaded" + } + """.utf8 + ) + ) + let client = ExhaustiveClient( + baseURL: "https://api.fern.com", + token: "", + urlSession: stub.urlSession + ) + let expectedResponse = ObjectWithRequiredField( + string: "uploaded" + ) + let response = try await client.endpoints.params.uploadWithPath( + param: "upload-path", + request: Data("data".utf8), + requestOptions: RequestOptions(additionalHeaders: stub.headers) + ) + try #require(response == expectedResponse) + } } \ No newline at end of file diff --git a/seed/swift-sdk/exhaustive/reference.md b/seed/swift-sdk/exhaustive/reference.md index 58169293628c..87186409fbee 100644 --- a/seed/swift-sdk/exhaustive/reference.md +++ b/seed/swift-sdk/exhaustive/reference.md @@ -2266,6 +2266,88 @@ try await main() + + + + +
client.endpoints.params.uploadWithPath(param: String, request: Data, requestOptions: RequestOptions?) -> ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```swift +import Foundation +import Exhaustive + +private func main() async throws { + let client = ExhaustiveClient(token: "") + + _ = try await client.endpoints.params.uploadWithPath( + param: "upload-path", + request: Data("data".utf8) + ) +} + +try await main() +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `String` + +
+
+ +
+
+ +**request:** `Data` + +
+
+ +
+
+ +**requestOptions:** `RequestOptions?` — Additional options for configuring the request, such as custom headers or timeout settings. + +
+
+
+
+ +
From c251d419b759b35cfbc554657a9ccf929135baf0 Mon Sep 17 00:00:00 2001 From: Alex McKinney Date: Tue, 24 Feb 2026 21:03:12 -0500 Subject: [PATCH 10/20] chore(cli): Exclude typescript in v2 tests (#12743) --- .../v2/fixtures/fern-definition/fern.yml | 5 ++++ .../ete-tests/src/tests/v2/generate.test.ts | 25 ++++++++----------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/cli/ete-tests/src/tests/v2/fixtures/fern-definition/fern.yml b/packages/cli/ete-tests/src/tests/v2/fixtures/fern-definition/fern.yml index a615993a4110..bb057e47b8a7 100644 --- a/packages/cli/ete-tests/src/tests/v2/fixtures/fern-definition/fern.yml +++ b/packages/cli/ete-tests/src/tests/v2/fixtures/fern-definition/fern.yml @@ -8,6 +8,11 @@ api: sdks: targets: + go: + version: 1.24.0 + output: + path: ./sdks/go + typescript: version: 3.45.1 output: diff --git a/packages/cli/ete-tests/src/tests/v2/generate.test.ts b/packages/cli/ete-tests/src/tests/v2/generate.test.ts index e6fba461e219..8d35e8bff432 100644 --- a/packages/cli/ete-tests/src/tests/v2/generate.test.ts +++ b/packages/cli/ete-tests/src/tests/v2/generate.test.ts @@ -10,11 +10,6 @@ const FIXTURES = { describe("fern sdk generate", () => { describe("basic generation", () => { - it("typescript", async () => { - const result = await cliV2.generate(FIXTURES.petstore, "typescript"); - expect(result.exitCode).toBe(0); - }, 60_000); - it("python", async () => { const result = await cliV2.generate(FIXTURES.petstore, "python"); expect(result.exitCode).toBe(0); @@ -27,7 +22,7 @@ describe("fern sdk generate", () => { }); describe("local generation", () => { - it.each([["typescript"], ["python"], ["go"]])("should generate %s SDK with --local flag", async (target) => { + it.each([["python"], ["go"]])("should generate %s SDK with --local flag", async (target) => { const result = await runCliV2({ args: ["sdk", "generate", "--target", target, "--local"], fixture: FIXTURES.petstore, @@ -73,7 +68,7 @@ describe("fern sdk generate", () => { describe("fern definition", () => { it("should generate SDK from fern definition", async () => { - const result = await cliV2.generate(FIXTURES.fernDefinition, "typescript"); + const result = await cliV2.generate(FIXTURES.fernDefinition, "go"); expect(result.exitCode).toBe(0); }, 60_000); }); @@ -91,10 +86,10 @@ api: sdks: targets: - typescript: - version: 3.45.1 + go: + version: 1.24.0 output: - path: ./sdks/typescript + path: ./sdks/go `, "openapi.yml": `openapi: 3.0.3 info: @@ -130,7 +125,7 @@ paths: try { const result = await runCliV2({ - args: ["sdk", "generate", "--target", "typescript", "--audience", "public", "--local"], + args: ["sdk", "generate", "--target", "go", "--audience", "public", "--local"], cwd: fixture.path, timeout: 60_000 }); @@ -152,10 +147,10 @@ api: sdks: targets: - typescript: - version: 3.45.1 + go: + version: 1.24.0 output: - path: ./sdks/typescript + path: ./sdks/go `, "openapi.yml": `openapi: 3.0.3 info: @@ -177,7 +172,7 @@ paths: "sdk", "generate", "--target", - "typescript", + "go", "--audience", "public", "--audience", From c7fa7b7c4b56599199562c62f2b642f9aabf8408 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 24 Feb 2026 21:04:30 -0500 Subject: [PATCH 11/20] chore(python): update python-sdk seed (#12746) Co-authored-by: amckinney From 9e25f9694a6ff2797a8a5769ac959db03fe6e519 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 24 Feb 2026 22:07:40 -0500 Subject: [PATCH 12/20] chore(go): update go-sdk seed (#12737) Co-authored-by: thesandlord --- seed/go-sdk/bytes-upload/README.md | 6 +++++- .../bytes-upload/dynamic-snippets/example0/snippet.go | 6 +++++- seed/go-sdk/bytes-upload/reference.md | 2 +- .../dynamic-snippets/example4/snippet.go | 1 + .../oauth-client-credentials-with-variables/reference.md | 1 + 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/seed/go-sdk/bytes-upload/README.md b/seed/go-sdk/bytes-upload/README.md index c0d5c57f0acc..7be19e60d326 100644 --- a/seed/go-sdk/bytes-upload/README.md +++ b/seed/go-sdk/bytes-upload/README.md @@ -31,14 +31,18 @@ package example import ( client "github.com/bytes-upload/fern/client" + bytes "bytes" context "context" ) func do() { client := client.NewClient() + request := bytes.NewReader( + []byte(""), + ) client.Service.Upload( context.TODO(), - nil, + request, ) } ``` diff --git a/seed/go-sdk/bytes-upload/dynamic-snippets/example0/snippet.go b/seed/go-sdk/bytes-upload/dynamic-snippets/example0/snippet.go index 75c2dc1551d4..7bca0a90d68d 100644 --- a/seed/go-sdk/bytes-upload/dynamic-snippets/example0/snippet.go +++ b/seed/go-sdk/bytes-upload/dynamic-snippets/example0/snippet.go @@ -3,6 +3,7 @@ package example import ( client "github.com/bytes-upload/fern/client" option "github.com/bytes-upload/fern/option" + bytes "bytes" context "context" ) @@ -12,8 +13,11 @@ func do() { "https://api.fern.com", ), ) + request := bytes.NewReader( + []byte(""), + ) client.Service.Upload( context.TODO(), - nil, + request, ) } diff --git a/seed/go-sdk/bytes-upload/reference.md b/seed/go-sdk/bytes-upload/reference.md index 09d8c25a2ff6..83a1a4b0575e 100644 --- a/seed/go-sdk/bytes-upload/reference.md +++ b/seed/go-sdk/bytes-upload/reference.md @@ -15,7 +15,7 @@ ```go client.Service.Upload( context.TODO(), - nil, + request, ) } ``` diff --git a/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example4/snippet.go b/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example4/snippet.go index a7c04f737f9c..8313b01033aa 100644 --- a/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example4/snippet.go +++ b/seed/go-sdk/oauth-client-credentials-with-variables/dynamic-snippets/example4/snippet.go @@ -18,5 +18,6 @@ func do() { ) client.Service.Post( context.TODO(), + "", ) } diff --git a/seed/go-sdk/oauth-client-credentials-with-variables/reference.md b/seed/go-sdk/oauth-client-credentials-with-variables/reference.md index 1173273be74d..41b28437d6a1 100644 --- a/seed/go-sdk/oauth-client-credentials-with-variables/reference.md +++ b/seed/go-sdk/oauth-client-credentials-with-variables/reference.md @@ -249,6 +249,7 @@ client.Nested.Api.GetSomething( ```go client.Service.Post( context.TODO(), + "", ) } ``` From 7e0c236b052881e0f99a1ea48d0ab38a62d3a053 Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 24 Feb 2026 22:08:14 -0500 Subject: [PATCH 13/20] chore(rust): update rust-sdk seed (#12739) Co-authored-by: thesandlord --- .../exhaustive/dynamic-snippets/example33.rs | 8 ++- .../exhaustive/dynamic-snippets/example34.rs | 2 +- .../exhaustive/dynamic-snippets/example35.rs | 2 +- .../exhaustive/dynamic-snippets/example36.rs | 2 +- .../exhaustive/dynamic-snippets/example37.rs | 2 +- .../exhaustive/dynamic-snippets/example38.rs | 5 +- .../exhaustive/dynamic-snippets/example39.rs | 4 +- .../exhaustive/dynamic-snippets/example40.rs | 4 +- .../exhaustive/dynamic-snippets/example41.rs | 6 +- .../exhaustive/dynamic-snippets/example42.rs | 11 ++- .../exhaustive/dynamic-snippets/example43.rs | 14 +--- .../exhaustive/dynamic-snippets/example44.rs | 14 +++- .../exhaustive/dynamic-snippets/example45.rs | 2 +- .../exhaustive/dynamic-snippets/example46.rs | 2 +- .../exhaustive/dynamic-snippets/example47.rs | 2 +- .../exhaustive/dynamic-snippets/example48.rs | 30 +------- .../exhaustive/dynamic-snippets/example50.rs | 29 +++++++- .../exhaustive/dynamic-snippets/example52.rs | 5 +- .../exhaustive/dynamic-snippets/example53.rs | 2 +- .../exhaustive/dynamic-snippets/example54.rs | 12 +--- .../exhaustive/dynamic-snippets/example55.rs | 22 ++++++ seed/rust-sdk/exhaustive/reference.md | 72 +++++++++++++++++++ .../endpoints/params/endpoints_params.rs | 19 +++++ .../exhaustive/tests/endpoints_params_test.rs | 30 ++++++++ .../wiremock/wiremock-mappings.json | 2 +- 25 files changed, 222 insertions(+), 81 deletions(-) create mode 100644 seed/rust-sdk/exhaustive/dynamic-snippets/example55.rs diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example33.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example33.rs index 0e0a92292c97..e2e70ab9e70e 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example33.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example33.rs @@ -10,7 +10,11 @@ async fn main() { let client = ExhaustiveClient::new(config).expect("Failed to build client"); client .endpoints - .primitive - .get_and_return_string(&"string".to_string(), None) + .params + .upload_with_path( + &"upload-path".to_string(), + &todo!("Invalid bytes value"), + None, + ) .await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example34.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example34.rs index 4aa61f05025b..0e0a92292c97 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example34.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example34.rs @@ -11,6 +11,6 @@ async fn main() { client .endpoints .primitive - .get_and_return_int(&1, None) + .get_and_return_string(&"string".to_string(), None) .await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example35.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example35.rs index 42130b807992..4aa61f05025b 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example35.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example35.rs @@ -11,6 +11,6 @@ async fn main() { client .endpoints .primitive - .get_and_return_long(&1000000, None) + .get_and_return_int(&1, None) .await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example36.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example36.rs index 4c61f954cb42..42130b807992 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example36.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example36.rs @@ -11,6 +11,6 @@ async fn main() { client .endpoints .primitive - .get_and_return_double(&1.1, None) + .get_and_return_long(&1000000, None) .await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example37.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example37.rs index c35d09b056f8..4c61f954cb42 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example37.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example37.rs @@ -11,6 +11,6 @@ async fn main() { client .endpoints .primitive - .get_and_return_bool(&true, None) + .get_and_return_double(&1.1, None) .await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example38.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example38.rs index c7a2667e5571..c35d09b056f8 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example38.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example38.rs @@ -11,9 +11,6 @@ async fn main() { client .endpoints .primitive - .get_and_return_datetime( - &DateTime::parse_from_rfc3339("2024-01-15T09:30:00Z").unwrap(), - None, - ) + .get_and_return_bool(&true, None) .await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example39.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example39.rs index 3940486b4b95..c7a2667e5571 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example39.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example39.rs @@ -11,8 +11,8 @@ async fn main() { client .endpoints .primitive - .get_and_return_date( - &NaiveDate::parse_from_str("2023-01-15", "%Y-%m-%d").unwrap(), + .get_and_return_datetime( + &DateTime::parse_from_rfc3339("2024-01-15T09:30:00Z").unwrap(), None, ) .await; diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example40.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example40.rs index 5973ea7373f2..3940486b4b95 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example40.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example40.rs @@ -11,8 +11,8 @@ async fn main() { client .endpoints .primitive - .get_and_return_uuid( - &Uuid::parse_str("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32").unwrap(), + .get_and_return_date( + &NaiveDate::parse_from_str("2023-01-15", "%Y-%m-%d").unwrap(), None, ) .await; diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example41.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example41.rs index b13a0e77b689..5973ea7373f2 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example41.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example41.rs @@ -11,10 +11,8 @@ async fn main() { client .endpoints .primitive - .get_and_return_base_64( - &base64::engine::general_purpose::STANDARD - .decode("SGVsbG8gd29ybGQh") - .unwrap(), + .get_and_return_uuid( + &Uuid::parse_str("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32").unwrap(), None, ) .await; diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example42.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example42.rs index ab3bf256d7f1..b13a0e77b689 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example42.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example42.rs @@ -8,5 +8,14 @@ async fn main() { ..Default::default() }; let client = ExhaustiveClient::new(config).expect("Failed to build client"); - client.endpoints.put.add(&"id".to_string(), None).await; + client + .endpoints + .primitive + .get_and_return_base_64( + &base64::engine::general_purpose::STANDARD + .decode("SGVsbG8gd29ybGQh") + .unwrap(), + None, + ) + .await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example43.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example43.rs index 02722777858c..ab3bf256d7f1 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example43.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example43.rs @@ -8,17 +8,5 @@ async fn main() { ..Default::default() }; let client = ExhaustiveClient::new(config).expect("Failed to build client"); - client - .endpoints - .union_ - .get_and_return_union( - &Animal::Dog { - data: Dog { - name: "name".to_string(), - likes_to_woof: true, - }, - }, - None, - ) - .await; + client.endpoints.put.add(&"id".to_string(), None).await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example44.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example44.rs index b6a634f51710..02722777858c 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example44.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example44.rs @@ -8,5 +8,17 @@ async fn main() { ..Default::default() }; let client = ExhaustiveClient::new(config).expect("Failed to build client"); - client.endpoints.urls.with_mixed_case(None).await; + client + .endpoints + .union_ + .get_and_return_union( + &Animal::Dog { + data: Dog { + name: "name".to_string(), + likes_to_woof: true, + }, + }, + None, + ) + .await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example45.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example45.rs index 85585e6d86ce..b6a634f51710 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example45.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example45.rs @@ -8,5 +8,5 @@ async fn main() { ..Default::default() }; let client = ExhaustiveClient::new(config).expect("Failed to build client"); - client.endpoints.urls.no_ending_slash(None).await; + client.endpoints.urls.with_mixed_case(None).await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example46.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example46.rs index 0da6e86038a7..85585e6d86ce 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example46.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example46.rs @@ -8,5 +8,5 @@ async fn main() { ..Default::default() }; let client = ExhaustiveClient::new(config).expect("Failed to build client"); - client.endpoints.urls.with_ending_slash(None).await; + client.endpoints.urls.no_ending_slash(None).await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example47.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example47.rs index b9e91a0faba0..0da6e86038a7 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example47.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example47.rs @@ -8,5 +8,5 @@ async fn main() { ..Default::default() }; let client = ExhaustiveClient::new(config).expect("Failed to build client"); - client.endpoints.urls.with_underscores(None).await; + client.endpoints.urls.with_ending_slash(None).await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example48.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example48.rs index a6a837128879..b9e91a0faba0 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example48.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example48.rs @@ -8,33 +8,5 @@ async fn main() { ..Default::default() }; let client = ExhaustiveClient::new(config).expect("Failed to build client"); - client - .inlined_requests - .post_with_object_bodyand_response( - &PostWithObjectBody { - string: "string".to_string(), - integer: 1, - nested_object: ObjectWithOptionalField { - string: Some("string".to_string()), - integer: Some(1), - long: Some(1000000), - double: Some(1.1), - bool: Some(true), - datetime: Some(DateTime::parse_from_rfc3339("2024-01-15T09:30:00Z").unwrap()), - date: Some(NaiveDate::parse_from_str("2023-01-15", "%Y-%m-%d").unwrap()), - uuid: Some(Uuid::parse_str("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32").unwrap()), - base_64: Some( - base64::engine::general_purpose::STANDARD - .decode("SGVsbG8gd29ybGQh") - .unwrap(), - ), - list: Some(vec!["list".to_string(), "list".to_string()]), - set: Some(HashSet::from(["set".to_string()])), - map: Some(HashMap::from([(1, "map".to_string())])), - bigint: Some(BigInt::parse_bytes("1000000".as_bytes(), 10).unwrap()), - }, - }, - None, - ) - .await; + client.endpoints.urls.with_underscores(None).await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example50.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example50.rs index 7271c3638f1c..a6a837128879 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example50.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example50.rs @@ -9,7 +9,32 @@ async fn main() { }; let client = ExhaustiveClient::new(config).expect("Failed to build client"); client - .no_auth - .post_with_no_auth(&serde_json::json!({"key":"value"}), None) + .inlined_requests + .post_with_object_bodyand_response( + &PostWithObjectBody { + string: "string".to_string(), + integer: 1, + nested_object: ObjectWithOptionalField { + string: Some("string".to_string()), + integer: Some(1), + long: Some(1000000), + double: Some(1.1), + bool: Some(true), + datetime: Some(DateTime::parse_from_rfc3339("2024-01-15T09:30:00Z").unwrap()), + date: Some(NaiveDate::parse_from_str("2023-01-15", "%Y-%m-%d").unwrap()), + uuid: Some(Uuid::parse_str("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32").unwrap()), + base_64: Some( + base64::engine::general_purpose::STANDARD + .decode("SGVsbG8gd29ybGQh") + .unwrap(), + ), + list: Some(vec!["list".to_string(), "list".to_string()]), + set: Some(HashSet::from(["set".to_string()])), + map: Some(HashMap::from([(1, "map".to_string())])), + bigint: Some(BigInt::parse_bytes("1000000".as_bytes(), 10).unwrap()), + }, + }, + None, + ) .await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example52.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example52.rs index 43ffea035bf6..7271c3638f1c 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example52.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example52.rs @@ -8,5 +8,8 @@ async fn main() { ..Default::default() }; let client = ExhaustiveClient::new(config).expect("Failed to build client"); - client.no_req_body.get_with_no_request_body(None).await; + client + .no_auth + .post_with_no_auth(&serde_json::json!({"key":"value"}), None) + .await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example53.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example53.rs index 32a381d14a77..43ffea035bf6 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example53.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example53.rs @@ -8,5 +8,5 @@ async fn main() { ..Default::default() }; let client = ExhaustiveClient::new(config).expect("Failed to build client"); - client.no_req_body.post_with_no_request_body(None).await; + client.no_req_body.get_with_no_request_body(None).await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example54.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example54.rs index ba55812c6887..32a381d14a77 100644 --- a/seed/rust-sdk/exhaustive/dynamic-snippets/example54.rs +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example54.rs @@ -8,15 +8,5 @@ async fn main() { ..Default::default() }; let client = ExhaustiveClient::new(config).expect("Failed to build client"); - client - .req_with_headers - .get_with_custom_header( - &"string".to_string(), - Some( - RequestOptions::new() - .additional_header("X-TEST-SERVICE-HEADER", "X-TEST-SERVICE-HEADER") - .additional_header("X-TEST-ENDPOINT-HEADER", "X-TEST-ENDPOINT-HEADER"), - ), - ) - .await; + client.no_req_body.post_with_no_request_body(None).await; } diff --git a/seed/rust-sdk/exhaustive/dynamic-snippets/example55.rs b/seed/rust-sdk/exhaustive/dynamic-snippets/example55.rs new file mode 100644 index 000000000000..ba55812c6887 --- /dev/null +++ b/seed/rust-sdk/exhaustive/dynamic-snippets/example55.rs @@ -0,0 +1,22 @@ +use seed_exhaustive::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + base_url: "https://api.fern.com".to_string(), + token: Some("".to_string()), + ..Default::default() + }; + let client = ExhaustiveClient::new(config).expect("Failed to build client"); + client + .req_with_headers + .get_with_custom_header( + &"string".to_string(), + Some( + RequestOptions::new() + .additional_header("X-TEST-SERVICE-HEADER", "X-TEST-SERVICE-HEADER") + .additional_header("X-TEST-ENDPOINT-HEADER", "X-TEST-ENDPOINT-HEADER"), + ), + ) + .await; +} diff --git a/seed/rust-sdk/exhaustive/reference.md b/seed/rust-sdk/exhaustive/reference.md index 87be90a34c36..0fe9fdc1a754 100644 --- a/seed/rust-sdk/exhaustive/reference.md +++ b/seed/rust-sdk/exhaustive/reference.md @@ -1926,6 +1926,78 @@ async fn main() { + + + + +
client.endpoints().params.upload_with_path(param: String) -> Result<ObjectWithRequiredField, ApiError> +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```rust +use seed_exhaustive::prelude::*; + +#[tokio::main] +async fn main() { + let config = ClientConfig { + token: Some("".to_string()), + ..Default::default() + }; + let client = ExhaustiveClient::new(config).expect("Failed to build client"); + client + .endpoints + .params + .upload_with_path( + &"upload-path".to_string(), + &todo!("Invalid bytes value"), + None, + ) + .await; +} +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `String` + +
+
+
+
+ +
diff --git a/seed/rust-sdk/exhaustive/src/api/resources/endpoints/params/endpoints_params.rs b/seed/rust-sdk/exhaustive/src/api/resources/endpoints/params/endpoints_params.rs index c8e181724d56..092f3ccb784c 100644 --- a/seed/rust-sdk/exhaustive/src/api/resources/endpoints/params/endpoints_params.rs +++ b/seed/rust-sdk/exhaustive/src/api/resources/endpoints/params/endpoints_params.rs @@ -169,5 +169,24 @@ impl ParamsClient { ).await } + /// POST bytes with path param returning object + /// + /// # Arguments + /// + /// * `options` - Additional request options such as headers, timeout, etc. + /// + /// # Returns + /// + /// JSON response from the API + pub async fn upload_with_path(&self, param: &String, request: &Vec, options: Option) -> Result { + self.http_client.execute_request( + Method::POST, + &format!("/params/path/{}", param), + Some(serde_json::to_value(request).unwrap_or_default()), + None, + options, + ).await + } + } diff --git a/seed/rust-sdk/exhaustive/tests/endpoints_params_test.rs b/seed/rust-sdk/exhaustive/tests/endpoints_params_test.rs index a4cc9e3a5482..66c6cd19267c 100644 --- a/seed/rust-sdk/exhaustive/tests/endpoints_params_test.rs +++ b/seed/rust-sdk/exhaustive/tests/endpoints_params_test.rs @@ -259,3 +259,33 @@ async fn test_endpoints_params_modify_with_inline_path_with_wiremock() { .await .unwrap(); } + +#[tokio::test] +#[allow(unused_variables, unreachable_code)] +async fn test_endpoints_params_upload_with_path_with_wiremock() { + wire_test_utils::reset_wiremock_requests().await.unwrap(); + let wiremock_base_url = wire_test_utils::WIREMOCK_BASE_URL; + + let mut config = ClientConfig { + token: Some("".to_string()), + ..Default::default() + }; + config.base_url = wiremock_base_url.to_string(); + let client = ExhaustiveClient::new(config).expect("Failed to build client"); + + let result = client + .endpoints + .params + .upload_with_path( + &"upload-path".to_string(), + &todo!("Invalid bytes value"), + None, + ) + .await; + + assert!(result.is_ok(), "Client method call should succeed"); + + wire_test_utils::verify_request_count("POST", "/params/path/upload-path", None, 1) + .await + .unwrap(); +} diff --git a/seed/rust-sdk/exhaustive/wiremock/wiremock-mappings.json b/seed/rust-sdk/exhaustive/wiremock/wiremock-mappings.json index 5f50de5ef2ff..eca3354326b5 100644 --- a/seed/rust-sdk/exhaustive/wiremock/wiremock-mappings.json +++ b/seed/rust-sdk/exhaustive/wiremock/wiremock-mappings.json @@ -1 +1 @@ -{"mappings":[{"id":"864d58e6-0c08-4f38-b7e1-2638f8d7fd6d","name":"getAndReturnListOfPrimitives - default","request":{"urlPathTemplate":"/container/list-of-primitives","method":"POST"},"response":{"status":200,"body":"[\n \"string\",\n \"string\"\n]","headers":{"Content-Type":"application/json"}},"uuid":"864d58e6-0c08-4f38-b7e1-2638f8d7fd6d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"ac1d4c4f-a8a7-4c27-ae64-7fc977cfa122","name":"getAndReturnListOfObjects - default","request":{"urlPathTemplate":"/container/list-of-objects","method":"POST"},"response":{"status":200,"body":"[\n {\n \"string\": \"string\"\n },\n {\n \"string\": \"string\"\n }\n]","headers":{"Content-Type":"application/json"}},"uuid":"ac1d4c4f-a8a7-4c27-ae64-7fc977cfa122","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"591d5c48-a536-452b-8a2e-ad7c23c38298","name":"getAndReturnSetOfPrimitives - default","request":{"urlPathTemplate":"/container/set-of-primitives","method":"POST"},"response":{"status":200,"body":"[\n \"string\"\n]","headers":{"Content-Type":"application/json"}},"uuid":"591d5c48-a536-452b-8a2e-ad7c23c38298","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e1d5f52b-7a51-464f-ac8f-83c0345a3a35","name":"getAndReturnSetOfObjects - default","request":{"urlPathTemplate":"/container/set-of-objects","method":"POST"},"response":{"status":200,"body":"[\n {\n \"string\": \"string\"\n }\n]","headers":{"Content-Type":"application/json"}},"uuid":"e1d5f52b-7a51-464f-ac8f-83c0345a3a35","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4b1d33b3-ca7d-462a-a2e3-23d531ae2922","name":"getAndReturnMapPrimToPrim - default","request":{"urlPathTemplate":"/container/map-prim-to-prim","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"4b1d33b3-ca7d-462a-a2e3-23d531ae2922","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b01ac2b9-3470-48aa-badc-57d331bb5a49","name":"getAndReturnMapOfPrimToObject - default","request":{"urlPathTemplate":"/container/map-prim-to-object","method":"POST"},"response":{"status":200,"body":"{\n \"string\": {\n \"string\": \"string\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"b01ac2b9-3470-48aa-badc-57d331bb5a49","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"eaf9315d-55c4-4434-8004-a70c25af5656","name":"getAndReturnMapOfPrimToUndiscriminatedUnion - default","request":{"urlPathTemplate":"/container/map-prim-to-union","method":"POST"},"response":{"status":200,"body":"{\n \"string\": 1.1\n}","headers":{"Content-Type":"application/json"}},"uuid":"eaf9315d-55c4-4434-8004-a70c25af5656","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e5271904-de0a-425f-940d-d6f6bde34755","name":"getAndReturnOptional - default","request":{"urlPathTemplate":"/container/opt-objects","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"e5271904-de0a-425f-940d-d6f6bde34755","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d19e1fbe-79cc-465c-962e-e1866ca2361b","name":"postJsonPatchContentType - default","request":{"urlPathTemplate":"/foo/bar","method":"POST"},"response":{"status":200,"body":"null","headers":{"Content-Type":"application/json"}},"uuid":"d19e1fbe-79cc-465c-962e-e1866ca2361b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4a107cf5-6284-48f8-9ddb-99d944ba989b","name":"postJsonPatchContentWithCharsetType - default","request":{"urlPathTemplate":"/foo/baz","method":"POST"},"response":{"status":200,"body":"null","headers":{"Content-Type":"application/json"}},"uuid":"4a107cf5-6284-48f8-9ddb-99d944ba989b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"60fd3c8a-3983-41b9-8178-f42997388900","name":"getAndReturnEnum - default","request":{"urlPathTemplate":"/enum","method":"POST"},"response":{"status":200,"body":"\"SUNNY\"","headers":{"Content-Type":"application/json"}},"uuid":"60fd3c8a-3983-41b9-8178-f42997388900","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"3d95c052-db6e-4eff-95f2-895666a5af54","name":"testGet - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"GET","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"3d95c052-db6e-4eff-95f2-895666a5af54","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"2552873f-9f1d-4557-a6c7-6c6b7a55b566","name":"testPost - default","request":{"urlPathTemplate":"/http-methods","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"2552873f-9f1d-4557-a6c7-6c6b7a55b566","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"db42fbdf-5426-41b7-b7ea-28ed39a38e82","name":"testPut - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"PUT","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"db42fbdf-5426-41b7-b7ea-28ed39a38e82","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c9def317-32a0-4bc0-b9b6-5efae8ca44a6","name":"testPatch - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"PATCH","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"c9def317-32a0-4bc0-b9b6-5efae8ca44a6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"05333635-c9ce-4c1a-bb6d-95e8a0fb80dc","name":"testDelete - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"DELETE","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"05333635-c9ce-4c1a-bb6d-95e8a0fb80dc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"41cdef0e-040f-4d08-8426-87b19e60f7d7","name":"getAndReturnWithOptionalField - default","request":{"urlPathTemplate":"/object/get-and-return-with-optional-field","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"41cdef0e-040f-4d08-8426-87b19e60f7d7","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"f74df550-df8c-4503-b202-0b9b3165c1a7","name":"getAndReturnWithRequiredField - default","request":{"urlPathTemplate":"/object/get-and-return-with-required-field","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"f74df550-df8c-4503-b202-0b9b3165c1a7","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0c904dbb-ce54-48a2-8364-13a4989be7f2","name":"getAndReturnWithMapOfMap - default","request":{"urlPathTemplate":"/object/get-and-return-with-map-of-map","method":"POST"},"response":{"status":200,"body":"{\n \"map\": {\n \"map\": {\n \"map\": \"map\"\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"0c904dbb-ce54-48a2-8364-13a4989be7f2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"67e969fc-ff81-4a67-9858-66a29ffc9b72","name":"getAndReturnNestedWithOptionalField - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-optional-field","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"67e969fc-ff81-4a67-9858-66a29ffc9b72","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"169827d0-4247-4236-8cef-34b94d2659de","name":"getAndReturnNestedWithRequiredField - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-required-field/{string}","method":"POST","pathParameters":{"string":{"equalTo":"string"}}},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"169827d0-4247-4236-8cef-34b94d2659de","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6d9ed308-5724-4bbc-a4f7-28dc264d188f","name":"getAndReturnNestedWithRequiredFieldAsList - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-required-field-list","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"6d9ed308-5724-4bbc-a4f7-28dc264d188f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e2cc3e92-6e37-4a50-9081-23952fdd73fe","name":"getAndReturnWithDatetimeLikeString - default","request":{"urlPathTemplate":"/object/get-and-return-with-datetime-like-string","method":"POST"},"response":{"status":200,"body":"{\n \"datetimeLikeString\": \"2023-08-31T14:15:22Z\",\n \"actualDatetime\": \"2023-08-31T14:15:22Z\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"e2cc3e92-6e37-4a50-9081-23952fdd73fe","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"f15b2079-2482-4ed2-951d-d2b5c4de9afd","name":"listItems - default","request":{"urlPathTemplate":"/pagination","method":"GET"},"response":{"status":200,"body":"{\n \"items\": [\n {\n \"string\": \"string\"\n },\n {\n \"string\": \"string\"\n }\n ],\n \"next\": \"next\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"f15b2079-2482-4ed2-951d-d2b5c4de9afd","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"97806fdb-f31f-4f90-84b8-f9cc1713d53d","name":"getWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"97806fdb-f31f-4f90-84b8-f9cc1713d53d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"711fc64f-4af9-4084-8c29-1e7a9e58be70","name":"getWithInlinePath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"711fc64f-4af9-4084-8c29-1e7a9e58be70","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8e5739b3-d75f-47d7-b6b8-a663d91a66b5","name":"getWithQuery - default","request":{"urlPathTemplate":"/params","method":"GET"},"response":{"status":200,"body":"null","headers":{"Content-Type":"application/json"}},"uuid":"8e5739b3-d75f-47d7-b6b8-a663d91a66b5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"4125d349-732b-4ff7-948d-1eeb977ed13b","name":"getWithAllowMultipleQuery - default","request":{"urlPathTemplate":"/params","method":"GET"},"response":{"status":200,"body":"null","headers":{"Content-Type":"application/json"}},"uuid":"4125d349-732b-4ff7-948d-1eeb977ed13b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"1933d96a-a1b9-4cf1-85ba-c1e8eff5bd56","name":"getWithPathAndQuery - default","request":{"urlPathTemplate":"/params/path-query/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"null","headers":{"Content-Type":"application/json"}},"uuid":"1933d96a-a1b9-4cf1-85ba-c1e8eff5bd56","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c4f5012a-fb3f-45ac-b695-beedc3353ad8","name":"getWithInlinePathAndQuery - default","request":{"urlPathTemplate":"/params/path-query/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"null","headers":{"Content-Type":"application/json"}},"uuid":"c4f5012a-fb3f-45ac-b695-beedc3353ad8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"046bf7d6-751b-48e9-bfc6-ff74a17e88e1","name":"modifyWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"PUT","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"046bf7d6-751b-48e9-bfc6-ff74a17e88e1","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6bfc7195-b99c-4449-bf47-7c4f74f6f33b","name":"modifyWithInlinePath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"PUT","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"6bfc7195-b99c-4449-bf47-7c4f74f6f33b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0506ae7c-87b7-4cd2-9e50-31d60f81893f","name":"getAndReturnString - default","request":{"urlPathTemplate":"/primitive/string","method":"POST"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"0506ae7c-87b7-4cd2-9e50-31d60f81893f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"67f875d1-e19e-440d-8f0c-7e1d24ef2619","name":"getAndReturnInt - default","request":{"urlPathTemplate":"/primitive/integer","method":"POST"},"response":{"status":200,"body":"1","headers":{"Content-Type":"application/json"}},"uuid":"67f875d1-e19e-440d-8f0c-7e1d24ef2619","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"5fadbebd-86c0-41f9-8be5-864e39eb5924","name":"getAndReturnLong - default","request":{"urlPathTemplate":"/primitive/long","method":"POST"},"response":{"status":200,"body":"1000000","headers":{"Content-Type":"application/json"}},"uuid":"5fadbebd-86c0-41f9-8be5-864e39eb5924","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e03872b6-86b1-490b-9195-86e5d3a014f2","name":"getAndReturnDouble - default","request":{"urlPathTemplate":"/primitive/double","method":"POST"},"response":{"status":200,"body":"1.1","headers":{"Content-Type":"application/json"}},"uuid":"e03872b6-86b1-490b-9195-86e5d3a014f2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"442e632f-890a-4105-9448-f7015127e3b4","name":"getAndReturnBool - default","request":{"urlPathTemplate":"/primitive/boolean","method":"POST"},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"442e632f-890a-4105-9448-f7015127e3b4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"ad76fa81-1e63-43fd-9a99-1d5e4339d98c","name":"getAndReturnDatetime - default","request":{"urlPathTemplate":"/primitive/datetime","method":"POST"},"response":{"status":200,"body":"\"2024-01-15T09:30:00Z\"","headers":{"Content-Type":"application/json"}},"uuid":"ad76fa81-1e63-43fd-9a99-1d5e4339d98c","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"afb524b1-54ab-4674-8446-fdd574c368cc","name":"getAndReturnDate - default","request":{"urlPathTemplate":"/primitive/date","method":"POST"},"response":{"status":200,"body":"\"2023-01-15\"","headers":{"Content-Type":"application/json"}},"uuid":"afb524b1-54ab-4674-8446-fdd574c368cc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"969c0f3a-218a-45b7-b17f-64b1a9307d43","name":"getAndReturnUUID - default","request":{"urlPathTemplate":"/primitive/uuid","method":"POST"},"response":{"status":200,"body":"\"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\"","headers":{"Content-Type":"application/json"}},"uuid":"969c0f3a-218a-45b7-b17f-64b1a9307d43","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"04baa20d-d318-40e6-9784-c40158c16acd","name":"getAndReturnBase64 - default","request":{"urlPathTemplate":"/primitive/base64","method":"POST"},"response":{"status":200,"body":"\"SGVsbG8gd29ybGQh\"","headers":{"Content-Type":"application/json"}},"uuid":"04baa20d-d318-40e6-9784-c40158c16acd","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"fddd5aaf-ab0d-4ef6-949c-5d329f6d7eb2","name":"Put - default","request":{"urlPathTemplate":"/{id}","method":"PUT","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\n \"errors\": [\n {\n \"category\": \"API_ERROR\",\n \"code\": \"INTERNAL_SERVER_ERROR\",\n \"detail\": \"detail\",\n \"field\": \"field\"\n },\n {\n \"category\": \"API_ERROR\",\n \"code\": \"INTERNAL_SERVER_ERROR\",\n \"detail\": \"detail\",\n \"field\": \"field\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"fddd5aaf-ab0d-4ef6-949c-5d329f6d7eb2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d29330f9-49ff-49c4-9081-7c83dbad315b","name":"getAndReturnUnion - default","request":{"urlPathTemplate":"/union","method":"POST"},"response":{"status":200,"body":"{\n \"animal\": \"dog\",\n \"name\": \"name\",\n \"likesToWoof\": true\n}","headers":{"Content-Type":"application/json"}},"uuid":"d29330f9-49ff-49c4-9081-7c83dbad315b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"343f71f3-36ce-4684-b762-d60a086b43a4","name":"withMixedCase - default","request":{"urlPathTemplate":"/urls/MixedCase","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"343f71f3-36ce-4684-b762-d60a086b43a4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"48f36314-b2b7-4910-9e1d-5b05f3346a60","name":"noEndingSlash - default","request":{"urlPathTemplate":"/urls/no-ending-slash","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"48f36314-b2b7-4910-9e1d-5b05f3346a60","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"f7b95029-2f25-4f70-8b4c-0855712747d8","name":"withEndingSlash - default","request":{"urlPathTemplate":"/urls/with-ending-slash/","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"f7b95029-2f25-4f70-8b4c-0855712747d8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"2f9671a5-e6da-43a8-be27-d0be0191dcbf","name":"withUnderscores - default","request":{"urlPathTemplate":"/urls/with_underscores","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"2f9671a5-e6da-43a8-be27-d0be0191dcbf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"4a6d0aaa-05f2-47bb-9f40-6e3743b9d2bf","name":"postWithObjectBodyandResponse - default","request":{"urlPathTemplate":"/req-bodies/object","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"4a6d0aaa-05f2-47bb-9f40-6e3743b9d2bf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c6e7cc6c-b76f-4860-a9ad-52dc8f55b6e6","name":"postWithNoAuth - default","request":{"urlPathTemplate":"/no-auth","method":"POST"},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"c6e7cc6c-b76f-4860-a9ad-52dc8f55b6e6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8098eeea-bc6b-4068-9601-566c2092f83f","name":"getWithNoRequestBody - default","request":{"urlPathTemplate":"/no-req-body","method":"GET"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"8098eeea-bc6b-4068-9601-566c2092f83f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"7dd9f944-1b35-42e9-a5ed-e48214ac8e91","name":"postWithNoRequestBody - default","request":{"urlPathTemplate":"/no-req-body","method":"POST"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"7dd9f944-1b35-42e9-a5ed-e48214ac8e91","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d7b54168-aef4-4d68-a9c5-446e97dee2fc","name":"getWithCustomHeader - default","request":{"urlPathTemplate":"/test-headers/custom-header","method":"POST"},"response":{"status":200,"body":"null","headers":{"Content-Type":"application/json"}},"uuid":"d7b54168-aef4-4d68-a9c5-446e97dee2fc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}}],"meta":{"total":52}} \ No newline at end of file +{"mappings":[{"id":"864d58e6-0c08-4f38-b7e1-2638f8d7fd6d","name":"getAndReturnListOfPrimitives - default","request":{"urlPathTemplate":"/container/list-of-primitives","method":"POST"},"response":{"status":200,"body":"[\n \"string\",\n \"string\"\n]","headers":{"Content-Type":"application/json"}},"uuid":"864d58e6-0c08-4f38-b7e1-2638f8d7fd6d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"ac1d4c4f-a8a7-4c27-ae64-7fc977cfa122","name":"getAndReturnListOfObjects - default","request":{"urlPathTemplate":"/container/list-of-objects","method":"POST"},"response":{"status":200,"body":"[\n {\n \"string\": \"string\"\n },\n {\n \"string\": \"string\"\n }\n]","headers":{"Content-Type":"application/json"}},"uuid":"ac1d4c4f-a8a7-4c27-ae64-7fc977cfa122","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"591d5c48-a536-452b-8a2e-ad7c23c38298","name":"getAndReturnSetOfPrimitives - default","request":{"urlPathTemplate":"/container/set-of-primitives","method":"POST"},"response":{"status":200,"body":"[\n \"string\"\n]","headers":{"Content-Type":"application/json"}},"uuid":"591d5c48-a536-452b-8a2e-ad7c23c38298","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e1d5f52b-7a51-464f-ac8f-83c0345a3a35","name":"getAndReturnSetOfObjects - default","request":{"urlPathTemplate":"/container/set-of-objects","method":"POST"},"response":{"status":200,"body":"[\n {\n \"string\": \"string\"\n }\n]","headers":{"Content-Type":"application/json"}},"uuid":"e1d5f52b-7a51-464f-ac8f-83c0345a3a35","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4b1d33b3-ca7d-462a-a2e3-23d531ae2922","name":"getAndReturnMapPrimToPrim - default","request":{"urlPathTemplate":"/container/map-prim-to-prim","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"4b1d33b3-ca7d-462a-a2e3-23d531ae2922","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b01ac2b9-3470-48aa-badc-57d331bb5a49","name":"getAndReturnMapOfPrimToObject - default","request":{"urlPathTemplate":"/container/map-prim-to-object","method":"POST"},"response":{"status":200,"body":"{\n \"string\": {\n \"string\": \"string\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"b01ac2b9-3470-48aa-badc-57d331bb5a49","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"eaf9315d-55c4-4434-8004-a70c25af5656","name":"getAndReturnMapOfPrimToUndiscriminatedUnion - default","request":{"urlPathTemplate":"/container/map-prim-to-union","method":"POST"},"response":{"status":200,"body":"{\n \"string\": 1.1\n}","headers":{"Content-Type":"application/json"}},"uuid":"eaf9315d-55c4-4434-8004-a70c25af5656","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e5271904-de0a-425f-940d-d6f6bde34755","name":"getAndReturnOptional - default","request":{"urlPathTemplate":"/container/opt-objects","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"e5271904-de0a-425f-940d-d6f6bde34755","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d19e1fbe-79cc-465c-962e-e1866ca2361b","name":"postJsonPatchContentType - default","request":{"urlPathTemplate":"/foo/bar","method":"POST"},"response":{"status":200,"body":"null","headers":{"Content-Type":"application/json"}},"uuid":"d19e1fbe-79cc-465c-962e-e1866ca2361b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4a107cf5-6284-48f8-9ddb-99d944ba989b","name":"postJsonPatchContentWithCharsetType - default","request":{"urlPathTemplate":"/foo/baz","method":"POST"},"response":{"status":200,"body":"null","headers":{"Content-Type":"application/json"}},"uuid":"4a107cf5-6284-48f8-9ddb-99d944ba989b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"60fd3c8a-3983-41b9-8178-f42997388900","name":"getAndReturnEnum - default","request":{"urlPathTemplate":"/enum","method":"POST"},"response":{"status":200,"body":"\"SUNNY\"","headers":{"Content-Type":"application/json"}},"uuid":"60fd3c8a-3983-41b9-8178-f42997388900","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"3d95c052-db6e-4eff-95f2-895666a5af54","name":"testGet - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"GET","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"3d95c052-db6e-4eff-95f2-895666a5af54","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"2552873f-9f1d-4557-a6c7-6c6b7a55b566","name":"testPost - default","request":{"urlPathTemplate":"/http-methods","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"2552873f-9f1d-4557-a6c7-6c6b7a55b566","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"db42fbdf-5426-41b7-b7ea-28ed39a38e82","name":"testPut - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"PUT","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"db42fbdf-5426-41b7-b7ea-28ed39a38e82","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c9def317-32a0-4bc0-b9b6-5efae8ca44a6","name":"testPatch - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"PATCH","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"c9def317-32a0-4bc0-b9b6-5efae8ca44a6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"05333635-c9ce-4c1a-bb6d-95e8a0fb80dc","name":"testDelete - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"DELETE","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"05333635-c9ce-4c1a-bb6d-95e8a0fb80dc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"41cdef0e-040f-4d08-8426-87b19e60f7d7","name":"getAndReturnWithOptionalField - default","request":{"urlPathTemplate":"/object/get-and-return-with-optional-field","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"41cdef0e-040f-4d08-8426-87b19e60f7d7","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"f74df550-df8c-4503-b202-0b9b3165c1a7","name":"getAndReturnWithRequiredField - default","request":{"urlPathTemplate":"/object/get-and-return-with-required-field","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"f74df550-df8c-4503-b202-0b9b3165c1a7","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0c904dbb-ce54-48a2-8364-13a4989be7f2","name":"getAndReturnWithMapOfMap - default","request":{"urlPathTemplate":"/object/get-and-return-with-map-of-map","method":"POST"},"response":{"status":200,"body":"{\n \"map\": {\n \"map\": {\n \"map\": \"map\"\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"0c904dbb-ce54-48a2-8364-13a4989be7f2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"67e969fc-ff81-4a67-9858-66a29ffc9b72","name":"getAndReturnNestedWithOptionalField - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-optional-field","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"67e969fc-ff81-4a67-9858-66a29ffc9b72","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"169827d0-4247-4236-8cef-34b94d2659de","name":"getAndReturnNestedWithRequiredField - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-required-field/{string}","method":"POST","pathParameters":{"string":{"equalTo":"string"}}},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"169827d0-4247-4236-8cef-34b94d2659de","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6d9ed308-5724-4bbc-a4f7-28dc264d188f","name":"getAndReturnNestedWithRequiredFieldAsList - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-required-field-list","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"6d9ed308-5724-4bbc-a4f7-28dc264d188f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e2cc3e92-6e37-4a50-9081-23952fdd73fe","name":"getAndReturnWithDatetimeLikeString - default","request":{"urlPathTemplate":"/object/get-and-return-with-datetime-like-string","method":"POST"},"response":{"status":200,"body":"{\n \"datetimeLikeString\": \"2023-08-31T14:15:22Z\",\n \"actualDatetime\": \"2023-08-31T14:15:22Z\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"e2cc3e92-6e37-4a50-9081-23952fdd73fe","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"f15b2079-2482-4ed2-951d-d2b5c4de9afd","name":"listItems - default","request":{"urlPathTemplate":"/pagination","method":"GET"},"response":{"status":200,"body":"{\n \"items\": [\n {\n \"string\": \"string\"\n },\n {\n \"string\": \"string\"\n }\n ],\n \"next\": \"next\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"f15b2079-2482-4ed2-951d-d2b5c4de9afd","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"97806fdb-f31f-4f90-84b8-f9cc1713d53d","name":"getWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"97806fdb-f31f-4f90-84b8-f9cc1713d53d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"711fc64f-4af9-4084-8c29-1e7a9e58be70","name":"getWithInlinePath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"711fc64f-4af9-4084-8c29-1e7a9e58be70","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8e5739b3-d75f-47d7-b6b8-a663d91a66b5","name":"getWithQuery - default","request":{"urlPathTemplate":"/params","method":"GET"},"response":{"status":200,"body":"null","headers":{"Content-Type":"application/json"}},"uuid":"8e5739b3-d75f-47d7-b6b8-a663d91a66b5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"4125d349-732b-4ff7-948d-1eeb977ed13b","name":"getWithAllowMultipleQuery - default","request":{"urlPathTemplate":"/params","method":"GET"},"response":{"status":200,"body":"null","headers":{"Content-Type":"application/json"}},"uuid":"4125d349-732b-4ff7-948d-1eeb977ed13b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"1933d96a-a1b9-4cf1-85ba-c1e8eff5bd56","name":"getWithPathAndQuery - default","request":{"urlPathTemplate":"/params/path-query/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"null","headers":{"Content-Type":"application/json"}},"uuid":"1933d96a-a1b9-4cf1-85ba-c1e8eff5bd56","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c4f5012a-fb3f-45ac-b695-beedc3353ad8","name":"getWithInlinePathAndQuery - default","request":{"urlPathTemplate":"/params/path-query/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"null","headers":{"Content-Type":"application/json"}},"uuid":"c4f5012a-fb3f-45ac-b695-beedc3353ad8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"046bf7d6-751b-48e9-bfc6-ff74a17e88e1","name":"modifyWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"PUT","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"046bf7d6-751b-48e9-bfc6-ff74a17e88e1","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6bfc7195-b99c-4449-bf47-7c4f74f6f33b","name":"modifyWithInlinePath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"PUT","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"6bfc7195-b99c-4449-bf47-7c4f74f6f33b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"658d6145-14f6-48d3-9c66-c63e01eb6fd2","name":"uploadWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"POST","pathParameters":{"param":{"equalTo":"upload-path"}}},"response":{"status":200,"body":"{\n \"string\": \"uploaded\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"658d6145-14f6-48d3-9c66-c63e01eb6fd2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0506ae7c-87b7-4cd2-9e50-31d60f81893f","name":"getAndReturnString - default","request":{"urlPathTemplate":"/primitive/string","method":"POST"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"0506ae7c-87b7-4cd2-9e50-31d60f81893f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"67f875d1-e19e-440d-8f0c-7e1d24ef2619","name":"getAndReturnInt - default","request":{"urlPathTemplate":"/primitive/integer","method":"POST"},"response":{"status":200,"body":"1","headers":{"Content-Type":"application/json"}},"uuid":"67f875d1-e19e-440d-8f0c-7e1d24ef2619","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"5fadbebd-86c0-41f9-8be5-864e39eb5924","name":"getAndReturnLong - default","request":{"urlPathTemplate":"/primitive/long","method":"POST"},"response":{"status":200,"body":"1000000","headers":{"Content-Type":"application/json"}},"uuid":"5fadbebd-86c0-41f9-8be5-864e39eb5924","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e03872b6-86b1-490b-9195-86e5d3a014f2","name":"getAndReturnDouble - default","request":{"urlPathTemplate":"/primitive/double","method":"POST"},"response":{"status":200,"body":"1.1","headers":{"Content-Type":"application/json"}},"uuid":"e03872b6-86b1-490b-9195-86e5d3a014f2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"442e632f-890a-4105-9448-f7015127e3b4","name":"getAndReturnBool - default","request":{"urlPathTemplate":"/primitive/boolean","method":"POST"},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"442e632f-890a-4105-9448-f7015127e3b4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"ad76fa81-1e63-43fd-9a99-1d5e4339d98c","name":"getAndReturnDatetime - default","request":{"urlPathTemplate":"/primitive/datetime","method":"POST"},"response":{"status":200,"body":"\"2024-01-15T09:30:00Z\"","headers":{"Content-Type":"application/json"}},"uuid":"ad76fa81-1e63-43fd-9a99-1d5e4339d98c","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"afb524b1-54ab-4674-8446-fdd574c368cc","name":"getAndReturnDate - default","request":{"urlPathTemplate":"/primitive/date","method":"POST"},"response":{"status":200,"body":"\"2023-01-15\"","headers":{"Content-Type":"application/json"}},"uuid":"afb524b1-54ab-4674-8446-fdd574c368cc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"969c0f3a-218a-45b7-b17f-64b1a9307d43","name":"getAndReturnUUID - default","request":{"urlPathTemplate":"/primitive/uuid","method":"POST"},"response":{"status":200,"body":"\"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\"","headers":{"Content-Type":"application/json"}},"uuid":"969c0f3a-218a-45b7-b17f-64b1a9307d43","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"04baa20d-d318-40e6-9784-c40158c16acd","name":"getAndReturnBase64 - default","request":{"urlPathTemplate":"/primitive/base64","method":"POST"},"response":{"status":200,"body":"\"SGVsbG8gd29ybGQh\"","headers":{"Content-Type":"application/json"}},"uuid":"04baa20d-d318-40e6-9784-c40158c16acd","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"fddd5aaf-ab0d-4ef6-949c-5d329f6d7eb2","name":"Put - default","request":{"urlPathTemplate":"/{id}","method":"PUT","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\n \"errors\": [\n {\n \"category\": \"API_ERROR\",\n \"code\": \"INTERNAL_SERVER_ERROR\",\n \"detail\": \"detail\",\n \"field\": \"field\"\n },\n {\n \"category\": \"API_ERROR\",\n \"code\": \"INTERNAL_SERVER_ERROR\",\n \"detail\": \"detail\",\n \"field\": \"field\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"fddd5aaf-ab0d-4ef6-949c-5d329f6d7eb2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d29330f9-49ff-49c4-9081-7c83dbad315b","name":"getAndReturnUnion - default","request":{"urlPathTemplate":"/union","method":"POST"},"response":{"status":200,"body":"{\n \"animal\": \"dog\",\n \"name\": \"name\",\n \"likesToWoof\": true\n}","headers":{"Content-Type":"application/json"}},"uuid":"d29330f9-49ff-49c4-9081-7c83dbad315b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"343f71f3-36ce-4684-b762-d60a086b43a4","name":"withMixedCase - default","request":{"urlPathTemplate":"/urls/MixedCase","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"343f71f3-36ce-4684-b762-d60a086b43a4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"48f36314-b2b7-4910-9e1d-5b05f3346a60","name":"noEndingSlash - default","request":{"urlPathTemplate":"/urls/no-ending-slash","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"48f36314-b2b7-4910-9e1d-5b05f3346a60","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"f7b95029-2f25-4f70-8b4c-0855712747d8","name":"withEndingSlash - default","request":{"urlPathTemplate":"/urls/with-ending-slash/","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"f7b95029-2f25-4f70-8b4c-0855712747d8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"2f9671a5-e6da-43a8-be27-d0be0191dcbf","name":"withUnderscores - default","request":{"urlPathTemplate":"/urls/with_underscores","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"2f9671a5-e6da-43a8-be27-d0be0191dcbf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"4a6d0aaa-05f2-47bb-9f40-6e3743b9d2bf","name":"postWithObjectBodyandResponse - default","request":{"urlPathTemplate":"/req-bodies/object","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"4a6d0aaa-05f2-47bb-9f40-6e3743b9d2bf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c6e7cc6c-b76f-4860-a9ad-52dc8f55b6e6","name":"postWithNoAuth - default","request":{"urlPathTemplate":"/no-auth","method":"POST"},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"c6e7cc6c-b76f-4860-a9ad-52dc8f55b6e6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8098eeea-bc6b-4068-9601-566c2092f83f","name":"getWithNoRequestBody - default","request":{"urlPathTemplate":"/no-req-body","method":"GET"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"8098eeea-bc6b-4068-9601-566c2092f83f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"7dd9f944-1b35-42e9-a5ed-e48214ac8e91","name":"postWithNoRequestBody - default","request":{"urlPathTemplate":"/no-req-body","method":"POST"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"7dd9f944-1b35-42e9-a5ed-e48214ac8e91","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d7b54168-aef4-4d68-a9c5-446e97dee2fc","name":"getWithCustomHeader - default","request":{"urlPathTemplate":"/test-headers/custom-header","method":"POST"},"response":{"status":200,"body":"null","headers":{"Content-Type":"application/json"}},"uuid":"d7b54168-aef4-4d68-a9c5-446e97dee2fc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}}],"meta":{"total":53}} \ No newline at end of file From c999cc692ddbdf4977d363af88a47693231b4c6b Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 24 Feb 2026 22:08:27 -0500 Subject: [PATCH 14/20] chore(java): update java-sdk seed (#12742) Co-authored-by: thesandlord --- .../custom-client-class-name/reference.md | 54 ++++++++++++ .../custom-client-class-name/snippet.json | 13 +++ .../endpoints/params/AsyncParamsClient.java | 32 +++++++ .../params/AsyncRawParamsClient.java | 82 ++++++++++++++++++ .../endpoints/params/ParamsClient.java | 30 +++++++ .../endpoints/params/RawParamsClient.java | 68 +++++++++++++++ .../src/main/java/com/snippets/Example33.java | 2 +- .../src/main/java/com/snippets/Example34.java | 2 +- .../src/main/java/com/snippets/Example35.java | 2 +- .../src/main/java/com/snippets/Example36.java | 2 +- .../src/main/java/com/snippets/Example37.java | 2 +- .../src/main/java/com/snippets/Example38.java | 3 +- .../src/main/java/com/snippets/Example39.java | 3 +- .../src/main/java/com/snippets/Example40.java | 3 +- .../src/main/java/com/snippets/Example41.java | 3 +- .../src/main/java/com/snippets/Example42.java | 3 +- .../src/main/java/com/snippets/Example43.java | 8 +- .../src/main/java/com/snippets/Example44.java | 7 +- .../src/main/java/com/snippets/Example45.java | 2 +- .../src/main/java/com/snippets/Example46.java | 2 +- .../src/main/java/com/snippets/Example47.java | 2 +- .../src/main/java/com/snippets/Example48.java | 34 +------- .../src/main/java/com/snippets/Example50.java | 37 ++++++-- .../src/main/java/com/snippets/Example52.java | 7 +- .../src/main/java/com/snippets/Example53.java | 2 +- .../src/main/java/com/snippets/Example54.java | 8 +- .../src/main/java/com/snippets/Example55.java | 18 ++++ .../exhaustive/custom-dependency/reference.md | 54 ++++++++++++ .../exhaustive/custom-dependency/snippet.json | 13 +++ .../endpoints/params/AsyncParamsClient.java | 32 +++++++ .../params/AsyncRawParamsClient.java | 84 +++++++++++++++++++ .../endpoints/params/ParamsClient.java | 30 +++++++ .../endpoints/params/RawParamsClient.java | 68 +++++++++++++++ .../src/main/java/com/snippets/Example33.java | 2 +- .../src/main/java/com/snippets/Example34.java | 2 +- .../src/main/java/com/snippets/Example35.java | 2 +- .../src/main/java/com/snippets/Example36.java | 2 +- .../src/main/java/com/snippets/Example37.java | 2 +- .../src/main/java/com/snippets/Example38.java | 3 +- .../src/main/java/com/snippets/Example39.java | 3 +- .../src/main/java/com/snippets/Example40.java | 3 +- .../src/main/java/com/snippets/Example41.java | 3 +- .../src/main/java/com/snippets/Example42.java | 3 +- .../src/main/java/com/snippets/Example43.java | 8 +- .../src/main/java/com/snippets/Example44.java | 7 +- .../src/main/java/com/snippets/Example45.java | 2 +- .../src/main/java/com/snippets/Example46.java | 2 +- .../src/main/java/com/snippets/Example47.java | 2 +- .../src/main/java/com/snippets/Example48.java | 34 +------- .../src/main/java/com/snippets/Example50.java | 37 ++++++-- .../src/main/java/com/snippets/Example52.java | 7 +- .../src/main/java/com/snippets/Example53.java | 2 +- .../src/main/java/com/snippets/Example54.java | 8 +- .../src/main/java/com/snippets/Example55.java | 20 +++++ .../custom-error-names/reference.md | 54 ++++++++++++ .../custom-error-names/snippet.json | 13 +++ .../endpoints/params/AsyncParamsClient.java | 32 +++++++ .../params/AsyncRawParamsClient.java | 83 ++++++++++++++++++ .../endpoints/params/ParamsClient.java | 30 +++++++ .../endpoints/params/RawParamsClient.java | 68 +++++++++++++++ .../src/main/java/com/snippets/Example33.java | 2 +- .../src/main/java/com/snippets/Example34.java | 2 +- .../src/main/java/com/snippets/Example35.java | 2 +- .../src/main/java/com/snippets/Example36.java | 2 +- .../src/main/java/com/snippets/Example37.java | 2 +- .../src/main/java/com/snippets/Example38.java | 3 +- .../src/main/java/com/snippets/Example39.java | 3 +- .../src/main/java/com/snippets/Example40.java | 3 +- .../src/main/java/com/snippets/Example41.java | 3 +- .../src/main/java/com/snippets/Example42.java | 3 +- .../src/main/java/com/snippets/Example43.java | 8 +- .../src/main/java/com/snippets/Example44.java | 7 +- .../src/main/java/com/snippets/Example45.java | 2 +- .../src/main/java/com/snippets/Example46.java | 2 +- .../src/main/java/com/snippets/Example47.java | 2 +- .../src/main/java/com/snippets/Example48.java | 34 +------- .../src/main/java/com/snippets/Example50.java | 37 ++++++-- .../src/main/java/com/snippets/Example52.java | 7 +- .../src/main/java/com/snippets/Example53.java | 2 +- .../src/main/java/com/snippets/Example54.java | 8 +- .../src/main/java/com/snippets/Example55.java | 20 +++++ .../exhaustive/custom-license/reference.md | 54 ++++++++++++ .../exhaustive/custom-license/snippet.json | 13 +++ .../endpoints/params/AsyncParamsClient.java | 32 +++++++ .../params/AsyncRawParamsClient.java | 84 +++++++++++++++++++ .../endpoints/params/ParamsClient.java | 30 +++++++ .../endpoints/params/RawParamsClient.java | 68 +++++++++++++++ .../src/main/java/com/snippets/Example33.java | 2 +- .../src/main/java/com/snippets/Example34.java | 2 +- .../src/main/java/com/snippets/Example35.java | 2 +- .../src/main/java/com/snippets/Example36.java | 2 +- .../src/main/java/com/snippets/Example37.java | 2 +- .../src/main/java/com/snippets/Example38.java | 3 +- .../src/main/java/com/snippets/Example39.java | 3 +- .../src/main/java/com/snippets/Example40.java | 3 +- .../src/main/java/com/snippets/Example41.java | 3 +- .../src/main/java/com/snippets/Example42.java | 3 +- .../src/main/java/com/snippets/Example43.java | 8 +- .../src/main/java/com/snippets/Example44.java | 7 +- .../src/main/java/com/snippets/Example45.java | 2 +- .../src/main/java/com/snippets/Example46.java | 2 +- .../src/main/java/com/snippets/Example47.java | 2 +- .../src/main/java/com/snippets/Example48.java | 34 +------- .../src/main/java/com/snippets/Example50.java | 37 ++++++-- .../src/main/java/com/snippets/Example52.java | 7 +- .../src/main/java/com/snippets/Example53.java | 2 +- .../src/main/java/com/snippets/Example54.java | 8 +- .../src/main/java/com/snippets/Example55.java | 20 +++++ .../enable-public-constructors/reference.md | 54 ++++++++++++ .../enable-public-constructors/snippet.json | 13 +++ .../endpoints/params/AsyncParamsClient.java | 32 +++++++ .../params/AsyncRawParamsClient.java | 84 +++++++++++++++++++ .../endpoints/params/ParamsClient.java | 30 +++++++ .../endpoints/params/RawParamsClient.java | 68 +++++++++++++++ .../src/main/java/com/snippets/Example33.java | 2 +- .../src/main/java/com/snippets/Example34.java | 2 +- .../src/main/java/com/snippets/Example35.java | 2 +- .../src/main/java/com/snippets/Example36.java | 2 +- .../src/main/java/com/snippets/Example37.java | 2 +- .../src/main/java/com/snippets/Example38.java | 3 +- .../src/main/java/com/snippets/Example39.java | 3 +- .../src/main/java/com/snippets/Example40.java | 3 +- .../src/main/java/com/snippets/Example41.java | 3 +- .../src/main/java/com/snippets/Example42.java | 3 +- .../src/main/java/com/snippets/Example43.java | 8 +- .../src/main/java/com/snippets/Example44.java | 7 +- .../src/main/java/com/snippets/Example45.java | 2 +- .../src/main/java/com/snippets/Example46.java | 2 +- .../src/main/java/com/snippets/Example47.java | 2 +- .../src/main/java/com/snippets/Example48.java | 34 +------- .../src/main/java/com/snippets/Example50.java | 37 ++++++-- .../src/main/java/com/snippets/Example52.java | 7 +- .../src/main/java/com/snippets/Example53.java | 2 +- .../src/main/java/com/snippets/Example54.java | 8 +- .../src/main/java/com/snippets/Example55.java | 20 +++++ .../flat-package-layout/reference.md | 54 ++++++++++++ .../flat-package-layout/snippet.json | 13 +++ .../endpoints/AsyncParamsClient.java | 32 +++++++ .../endpoints/AsyncRawParamsClient.java | 84 +++++++++++++++++++ .../exhaustive/endpoints/ParamsClient.java | 30 +++++++ .../exhaustive/endpoints/RawParamsClient.java | 68 +++++++++++++++ .../src/main/java/com/snippets/Example33.java | 2 +- .../src/main/java/com/snippets/Example34.java | 2 +- .../src/main/java/com/snippets/Example35.java | 2 +- .../src/main/java/com/snippets/Example36.java | 2 +- .../src/main/java/com/snippets/Example37.java | 2 +- .../src/main/java/com/snippets/Example38.java | 3 +- .../src/main/java/com/snippets/Example39.java | 3 +- .../src/main/java/com/snippets/Example40.java | 3 +- .../src/main/java/com/snippets/Example41.java | 3 +- .../src/main/java/com/snippets/Example42.java | 3 +- .../src/main/java/com/snippets/Example43.java | 8 +- .../src/main/java/com/snippets/Example44.java | 7 +- .../src/main/java/com/snippets/Example45.java | 2 +- .../src/main/java/com/snippets/Example46.java | 2 +- .../src/main/java/com/snippets/Example47.java | 2 +- .../src/main/java/com/snippets/Example48.java | 34 +------- .../src/main/java/com/snippets/Example50.java | 37 ++++++-- .../src/main/java/com/snippets/Example52.java | 7 +- .../src/main/java/com/snippets/Example53.java | 2 +- .../src/main/java/com/snippets/Example54.java | 8 +- .../src/main/java/com/snippets/Example55.java | 20 +++++ .../forward-compatible-enums/reference.md | 54 ++++++++++++ .../forward-compatible-enums/snippet.json | 13 +++ .../endpoints/params/AsyncParamsClient.java | 32 +++++++ .../params/AsyncRawParamsClient.java | 84 +++++++++++++++++++ .../endpoints/params/ParamsClient.java | 30 +++++++ .../endpoints/params/RawParamsClient.java | 68 +++++++++++++++ .../src/main/java/com/snippets/Example33.java | 2 +- .../src/main/java/com/snippets/Example34.java | 2 +- .../src/main/java/com/snippets/Example35.java | 2 +- .../src/main/java/com/snippets/Example36.java | 2 +- .../src/main/java/com/snippets/Example37.java | 2 +- .../src/main/java/com/snippets/Example38.java | 3 +- .../src/main/java/com/snippets/Example39.java | 3 +- .../src/main/java/com/snippets/Example40.java | 3 +- .../src/main/java/com/snippets/Example41.java | 3 +- .../src/main/java/com/snippets/Example42.java | 3 +- .../src/main/java/com/snippets/Example43.java | 8 +- .../src/main/java/com/snippets/Example44.java | 7 +- .../src/main/java/com/snippets/Example45.java | 2 +- .../src/main/java/com/snippets/Example46.java | 2 +- .../src/main/java/com/snippets/Example47.java | 2 +- .../src/main/java/com/snippets/Example48.java | 34 +------- .../src/main/java/com/snippets/Example50.java | 37 ++++++-- .../src/main/java/com/snippets/Example52.java | 7 +- .../src/main/java/com/snippets/Example53.java | 2 +- .../src/main/java/com/snippets/Example54.java | 8 +- .../src/main/java/com/snippets/Example55.java | 20 +++++ .../json-include-non-empty/reference.md | 54 ++++++++++++ .../json-include-non-empty/snippet.json | 13 +++ .../endpoints/params/AsyncParamsClient.java | 32 +++++++ .../params/AsyncRawParamsClient.java | 84 +++++++++++++++++++ .../endpoints/params/ParamsClient.java | 30 +++++++ .../endpoints/params/RawParamsClient.java | 68 +++++++++++++++ .../src/main/java/com/snippets/Example33.java | 2 +- .../src/main/java/com/snippets/Example34.java | 2 +- .../src/main/java/com/snippets/Example35.java | 2 +- .../src/main/java/com/snippets/Example36.java | 2 +- .../src/main/java/com/snippets/Example37.java | 2 +- .../src/main/java/com/snippets/Example38.java | 3 +- .../src/main/java/com/snippets/Example39.java | 3 +- .../src/main/java/com/snippets/Example40.java | 3 +- .../src/main/java/com/snippets/Example41.java | 3 +- .../src/main/java/com/snippets/Example42.java | 3 +- .../src/main/java/com/snippets/Example43.java | 8 +- .../src/main/java/com/snippets/Example44.java | 7 +- .../src/main/java/com/snippets/Example45.java | 2 +- .../src/main/java/com/snippets/Example46.java | 2 +- .../src/main/java/com/snippets/Example47.java | 2 +- .../src/main/java/com/snippets/Example48.java | 34 +------- .../src/main/java/com/snippets/Example50.java | 37 ++++++-- .../src/main/java/com/snippets/Example52.java | 7 +- .../src/main/java/com/snippets/Example53.java | 2 +- .../src/main/java/com/snippets/Example54.java | 8 +- .../src/main/java/com/snippets/Example55.java | 20 +++++ .../exhaustive/local-files/reference.md | 54 ++++++++++++ .../exhaustive/local-files/snippet.json | 13 +++ .../src/main/java/com/snippets/Example33.java | 2 +- .../src/main/java/com/snippets/Example34.java | 2 +- .../src/main/java/com/snippets/Example35.java | 2 +- .../src/main/java/com/snippets/Example36.java | 2 +- .../src/main/java/com/snippets/Example37.java | 2 +- .../src/main/java/com/snippets/Example38.java | 3 +- .../src/main/java/com/snippets/Example39.java | 3 +- .../src/main/java/com/snippets/Example40.java | 3 +- .../src/main/java/com/snippets/Example41.java | 3 +- .../src/main/java/com/snippets/Example42.java | 8 +- .../src/main/java/com/snippets/Example43.java | 16 ++-- .../src/main/java/com/snippets/Example44.java | 12 ++- .../src/main/java/com/snippets/Example45.java | 2 +- .../src/main/java/com/snippets/Example46.java | 2 +- .../src/main/java/com/snippets/Example47.java | 2 +- .../src/main/java/com/snippets/Example48.java | 47 +---------- .../src/main/java/com/snippets/Example50.java | 48 ++++++++++- .../src/main/java/com/snippets/Example52.java | 5 +- .../src/main/java/com/snippets/Example53.java | 2 +- .../src/main/java/com/snippets/Example54.java | 10 +-- .../src/main/java/com/snippets/Example55.java | 23 +++++ .../endpoints/params/AsyncParamsClient.java | 33 ++++++++ .../params/AsyncRawParamsClient.java | 81 +++++++++++++++++- .../endpoints/params/ParamsClient.java | 32 +++++++ .../endpoints/params/RawParamsClient.java | 68 ++++++++++++++- .../exhaustive/no-custom-config/reference.md | 54 ++++++++++++ .../exhaustive/no-custom-config/snippet.json | 13 +++ .../endpoints/params/AsyncParamsClient.java | 32 +++++++ .../params/AsyncRawParamsClient.java | 84 +++++++++++++++++++ .../endpoints/params/ParamsClient.java | 30 +++++++ .../endpoints/params/RawParamsClient.java | 68 +++++++++++++++ .../src/main/java/com/snippets/Example33.java | 2 +- .../src/main/java/com/snippets/Example34.java | 2 +- .../src/main/java/com/snippets/Example35.java | 2 +- .../src/main/java/com/snippets/Example36.java | 2 +- .../src/main/java/com/snippets/Example37.java | 2 +- .../src/main/java/com/snippets/Example38.java | 3 +- .../src/main/java/com/snippets/Example39.java | 3 +- .../src/main/java/com/snippets/Example40.java | 3 +- .../src/main/java/com/snippets/Example41.java | 3 +- .../src/main/java/com/snippets/Example42.java | 3 +- .../src/main/java/com/snippets/Example43.java | 8 +- .../src/main/java/com/snippets/Example44.java | 7 +- .../src/main/java/com/snippets/Example45.java | 2 +- .../src/main/java/com/snippets/Example46.java | 2 +- .../src/main/java/com/snippets/Example47.java | 2 +- .../src/main/java/com/snippets/Example48.java | 34 +------- .../src/main/java/com/snippets/Example50.java | 37 ++++++-- .../src/main/java/com/snippets/Example52.java | 7 +- .../src/main/java/com/snippets/Example53.java | 2 +- .../src/main/java/com/snippets/Example54.java | 8 +- .../src/main/java/com/snippets/Example55.java | 20 +++++ .../exhaustive/publish-to/reference.md | 54 ++++++++++++ .../exhaustive/publish-to/snippet.json | 13 +++ .../endpoints/params/AsyncParamsClient.java | 32 +++++++ .../params/AsyncRawParamsClient.java | 84 +++++++++++++++++++ .../endpoints/params/ParamsClient.java | 30 +++++++ .../endpoints/params/RawParamsClient.java | 68 +++++++++++++++ .../src/main/java/com/snippets/Example33.java | 2 +- .../src/main/java/com/snippets/Example34.java | 2 +- .../src/main/java/com/snippets/Example35.java | 2 +- .../src/main/java/com/snippets/Example36.java | 2 +- .../src/main/java/com/snippets/Example37.java | 2 +- .../src/main/java/com/snippets/Example38.java | 3 +- .../src/main/java/com/snippets/Example39.java | 3 +- .../src/main/java/com/snippets/Example40.java | 3 +- .../src/main/java/com/snippets/Example41.java | 3 +- .../src/main/java/com/snippets/Example42.java | 3 +- .../src/main/java/com/snippets/Example43.java | 8 +- .../src/main/java/com/snippets/Example44.java | 7 +- .../src/main/java/com/snippets/Example45.java | 2 +- .../src/main/java/com/snippets/Example46.java | 2 +- .../src/main/java/com/snippets/Example47.java | 2 +- .../src/main/java/com/snippets/Example48.java | 34 +------- .../src/main/java/com/snippets/Example50.java | 37 ++++++-- .../src/main/java/com/snippets/Example52.java | 7 +- .../src/main/java/com/snippets/Example53.java | 2 +- .../src/main/java/com/snippets/Example54.java | 8 +- .../src/main/java/com/snippets/Example55.java | 20 +++++ .../exhaustive/signed_publish/reference.md | 54 ++++++++++++ .../exhaustive/signed_publish/snippet.json | 13 +++ .../endpoints/params/AsyncParamsClient.java | 32 +++++++ .../params/AsyncRawParamsClient.java | 84 +++++++++++++++++++ .../endpoints/params/ParamsClient.java | 30 +++++++ .../endpoints/params/RawParamsClient.java | 68 +++++++++++++++ .../src/main/java/com/snippets/Example33.java | 2 +- .../src/main/java/com/snippets/Example34.java | 2 +- .../src/main/java/com/snippets/Example35.java | 2 +- .../src/main/java/com/snippets/Example36.java | 2 +- .../src/main/java/com/snippets/Example37.java | 2 +- .../src/main/java/com/snippets/Example38.java | 3 +- .../src/main/java/com/snippets/Example39.java | 3 +- .../src/main/java/com/snippets/Example40.java | 3 +- .../src/main/java/com/snippets/Example41.java | 3 +- .../src/main/java/com/snippets/Example42.java | 3 +- .../src/main/java/com/snippets/Example43.java | 8 +- .../src/main/java/com/snippets/Example44.java | 7 +- .../src/main/java/com/snippets/Example45.java | 2 +- .../src/main/java/com/snippets/Example46.java | 2 +- .../src/main/java/com/snippets/Example47.java | 2 +- .../src/main/java/com/snippets/Example48.java | 34 +------- .../src/main/java/com/snippets/Example50.java | 37 ++++++-- .../src/main/java/com/snippets/Example52.java | 7 +- .../src/main/java/com/snippets/Example53.java | 2 +- .../src/main/java/com/snippets/Example54.java | 8 +- .../src/main/java/com/snippets/Example55.java | 20 +++++ 324 files changed, 4396 insertions(+), 864 deletions(-) create mode 100644 seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example55.java create mode 100644 seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example55.java create mode 100644 seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example55.java create mode 100644 seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example55.java create mode 100644 seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example55.java create mode 100644 seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example55.java create mode 100644 seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example55.java create mode 100644 seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example55.java create mode 100644 seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example55.java create mode 100644 seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example55.java create mode 100644 seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example55.java create mode 100644 seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example55.java diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/reference.md b/seed/java-sdk/exhaustive/custom-client-class-name/reference.md index 650f282e620a..6c66e7b30c98 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/reference.md +++ b/seed/java-sdk/exhaustive/custom-client-class-name/reference.md @@ -1874,6 +1874,60 @@ client.endpoints().params().modifyWithPath("param", "string"); + + + + +
client.endpoints.params.uploadWithPath(param, request) -> ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```java +client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `String` + +
+
+
+
+ +
diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/snippet.json b/seed/java-sdk/exhaustive/custom-client-class-name/snippet.json index cef44870e123..927b6973c477 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/snippet.json +++ b/seed/java-sdk/exhaustive/custom-client-class-name/snippet.json @@ -429,6 +429,19 @@ "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.Best;\n\npublic class Example {\n public static void main(String[] args) {\n Best client = Best\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().modifyWithPath(\"param\", \"string\");\n }\n}\n" } }, + { + "example_identifier": "b305eac1", + "id": { + "method": "POST", + "path": "/params/path/{param}", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "java", + "sync_client": "package com.example.usage;\n\nimport com.seed.exhaustive.Best;\n\npublic class Example {\n public static void main(String[] args) {\n Best client = Best\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n", + "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.Best;\n\npublic class Example {\n public static void main(String[] args) {\n Best client = Best\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n" + } + }, { "example_identifier": "d8492cb1", "id": { diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java index 5fe44031aa45..2f6f8508a579 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; public class AsyncParamsClient { @@ -147,4 +149,34 @@ public CompletableFuture modifyWithInlinePath( ModifyResourceAtInlinedPath request, RequestOptions requestOptions) { return this.rawClient.modifyWithInlinePath(request, requestOptions).thenApply(response -> response.body()); } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java index 1019a73b9545..96734a1de439 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java @@ -8,6 +8,7 @@ import com.seed.exhaustive.core.BestException; import com.seed.exhaustive.core.BestHttpResponse; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,12 +19,16 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -525,4 +530,81 @@ public void onFailure(@NotNull Call call, @NotNull IOException e) { }); return future; } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new BestHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response)); + return; + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new BestApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (IOException e) { + future.completeExceptionally(new BestException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new BestException("Network error executing HTTP request", e)); + } + }); + return future; + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath(String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java index 1e1def81f07d..a3db6c5f6dec 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; public class ParamsClient { protected final ClientOptions clientOptions; @@ -140,4 +142,32 @@ public String modifyWithInlinePath(ModifyResourceAtInlinedPath request) { public String modifyWithInlinePath(ModifyResourceAtInlinedPath request, RequestOptions requestOptions) { return this.rawClient.modifyWithInlinePath(request, requestOptions).body(); } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java index 83041f440e70..dccd7603c516 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java @@ -8,6 +8,7 @@ import com.seed.exhaustive.core.BestException; import com.seed.exhaustive.core.BestHttpResponse; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,9 +19,13 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -415,4 +420,67 @@ public BestHttpResponse modifyWithInlinePath( throw new BestException("Network error executing HTTP request", e); } } + + /** + * POST bytes with path param returning object + */ + public BestHttpResponse uploadWithPath(String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public BestHttpResponse uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .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(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new BestHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response); + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new BestApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (IOException e) { + throw new BestException("Network error executing HTTP request", e); + } + } + + /** + * POST bytes with path param returning object + */ + public BestHttpResponse uploadWithPath(String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public BestHttpResponse uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example33.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example33.java index ba88fc525b69..47e302502988 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example33.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example33.java @@ -7,6 +7,6 @@ public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.endpoints().primitive().getAndReturnString("string"); + client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example34.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example34.java index e424db2f46bc..c4944c035109 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example34.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example34.java @@ -7,6 +7,6 @@ public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.endpoints().primitive().getAndReturnInt(1); + client.endpoints().primitive().getAndReturnString("string"); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example35.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example35.java index 1f82742e3119..6249a24b8c23 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example35.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example35.java @@ -7,6 +7,6 @@ public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.endpoints().primitive().getAndReturnLong(1000000L); + client.endpoints().primitive().getAndReturnInt(1); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example36.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example36.java index baa3ad4668a2..db8e6693957c 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example36.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example36.java @@ -7,6 +7,6 @@ public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.endpoints().primitive().getAndReturnDouble(1.1); + client.endpoints().primitive().getAndReturnLong(1000000L); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example37.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example37.java index abf91245f4b6..0f216868bbea 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example37.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example37.java @@ -7,6 +7,6 @@ public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.endpoints().primitive().getAndReturnBool(true); + client.endpoints().primitive().getAndReturnDouble(1.1); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example38.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example38.java index 4f2d51cda09b..fbd1c61baf3b 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example38.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example38.java @@ -1,13 +1,12 @@ package com.snippets; import com.seed.exhaustive.Best; -import java.time.OffsetDateTime; public class Example38 { public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); + client.endpoints().primitive().getAndReturnBool(true); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example39.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example39.java index 19ce874499dc..441d48594041 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example39.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example39.java @@ -1,12 +1,13 @@ package com.snippets; import com.seed.exhaustive.Best; +import java.time.OffsetDateTime; public class Example39 { public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.endpoints().primitive().getAndReturnDate("2023-01-15"); + client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example40.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example40.java index a77bf1298511..9520fa062c7b 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example40.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example40.java @@ -1,13 +1,12 @@ package com.snippets; import com.seed.exhaustive.Best; -import java.util.UUID; public class Example40 { public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); + client.endpoints().primitive().getAndReturnDate("2023-01-15"); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example41.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example41.java index cef73678043c..c19b535c17e7 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example41.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example41.java @@ -1,12 +1,13 @@ package com.snippets; import com.seed.exhaustive.Best; +import java.util.UUID; public class Example41 { public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); + client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example42.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example42.java index 52fae28b9077..2d0adb08148c 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example42.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example42.java @@ -1,13 +1,12 @@ package com.snippets; import com.seed.exhaustive.Best; -import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example42 { public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.endpoints().put().add(PutRequest.builder().id("id").build()); + client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example43.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example43.java index 452850e42b9b..22212aa03ae6 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example43.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example43.java @@ -1,17 +1,13 @@ package com.snippets; import com.seed.exhaustive.Best; -import com.seed.exhaustive.resources.types.union.types.Animal; -import com.seed.exhaustive.resources.types.union.types.Dog; +import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example43 { public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.endpoints() - .union() - .getAndReturnUnion( - Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); + client.endpoints().put().add(PutRequest.builder().id("id").build()); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example44.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example44.java index 73254951d950..f4974c78fd68 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example44.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example44.java @@ -1,12 +1,17 @@ package com.snippets; import com.seed.exhaustive.Best; +import com.seed.exhaustive.resources.types.union.types.Animal; +import com.seed.exhaustive.resources.types.union.types.Dog; public class Example44 { public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.endpoints().urls().withMixedCase(); + client.endpoints() + .union() + .getAndReturnUnion( + Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example45.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example45.java index 6015b8b005b4..3d1c3617e500 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example45.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example45.java @@ -7,6 +7,6 @@ public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.endpoints().urls().noEndingSlash(); + client.endpoints().urls().withMixedCase(); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example46.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example46.java index a5acadbc8407..e13957a2e4cc 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example46.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example46.java @@ -7,6 +7,6 @@ public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.endpoints().urls().withEndingSlash(); + client.endpoints().urls().noEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example47.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example47.java index 317ba72af879..34d7648136f7 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example47.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example47.java @@ -7,6 +7,6 @@ public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.endpoints().urls().withUnderscores(); + client.endpoints().urls().withEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example48.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example48.java index 719aef32e74a..0ae85e8fa68f 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example48.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example48.java @@ -1,44 +1,12 @@ package com.snippets; import com.seed.exhaustive.Best; -import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; -import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; -import java.math.BigInteger; -import java.time.OffsetDateTime; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Optional; -import java.util.UUID; public class Example48 { public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.inlinedRequests() - .postWithObjectBodyandResponse(PostWithObjectBody.builder() - .string("string") - .integer(1) - .nestedObject(ObjectWithOptionalField.builder() - .string("string") - .integer(1) - .long_(1000000L) - .double_(1.1) - .bool(true) - .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) - .date("2023-01-15") - .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) - .base64("SGVsbG8gd29ybGQh".getBytes()) - .list(Optional.of(Arrays.asList("list", "list"))) - .set(new HashSet(Arrays.asList("set"))) - .map(new HashMap() { - { - put(1, "map"); - } - }) - .bigint(new BigInteger("1000000")) - .build()) - .build()); + client.endpoints().urls().withUnderscores(); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example50.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example50.java index 3eadecfc4e30..7683b80237ff 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example50.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example50.java @@ -1,17 +1,44 @@ package com.snippets; import com.seed.exhaustive.Best; +import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.math.BigInteger; +import java.time.OffsetDateTime; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; +import java.util.UUID; public class Example50 { public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.noAuth().postWithNoAuth(new HashMap() { - { - put("key", "value"); - } - }); + client.inlinedRequests() + .postWithObjectBodyandResponse(PostWithObjectBody.builder() + .string("string") + .integer(1) + .nestedObject(ObjectWithOptionalField.builder() + .string("string") + .integer(1) + .long_(1000000L) + .double_(1.1) + .bool(true) + .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) + .date("2023-01-15") + .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) + .base64("SGVsbG8gd29ybGQh".getBytes()) + .list(Optional.of(Arrays.asList("list", "list"))) + .set(new HashSet(Arrays.asList("set"))) + .map(new HashMap() { + { + put(1, "map"); + } + }) + .bigint(new BigInteger("1000000")) + .build()) + .build()); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example52.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example52.java index 9c7341cf0765..6712cfb6fe72 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example52.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example52.java @@ -1,12 +1,17 @@ package com.snippets; import com.seed.exhaustive.Best; +import java.util.HashMap; public class Example52 { public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.noReqBody().getWithNoRequestBody(); + client.noAuth().postWithNoAuth(new HashMap() { + { + put("key", "value"); + } + }); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example53.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example53.java index 22615cc11281..e55acf7cf4fa 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example53.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example53.java @@ -7,6 +7,6 @@ public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.noReqBody().postWithNoRequestBody(); + client.noReqBody().getWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example54.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example54.java index a0944f0d7a3e..4a0af8800043 100644 --- a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example54.java +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example54.java @@ -1,18 +1,12 @@ package com.snippets; import com.seed.exhaustive.Best; -import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; public class Example54 { public static void main(String[] args) { Best client = Best.builder().token("").url("https://api.fern.com").build(); - client.reqWithHeaders() - .getWithCustomHeader(ReqWithHeaders.builder() - .xTestServiceHeader("X-TEST-SERVICE-HEADER") - .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") - .body("string") - .build()); + client.noReqBody().postWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example55.java b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example55.java new file mode 100644 index 000000000000..bd20a0cb5f40 --- /dev/null +++ b/seed/java-sdk/exhaustive/custom-client-class-name/src/main/java/com/snippets/Example55.java @@ -0,0 +1,18 @@ +package com.snippets; + +import com.seed.exhaustive.Best; +import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; + +public class Example55 { + public static void main(String[] args) { + Best client = + Best.builder().token("").url("https://api.fern.com").build(); + + client.reqWithHeaders() + .getWithCustomHeader(ReqWithHeaders.builder() + .xTestServiceHeader("X-TEST-SERVICE-HEADER") + .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") + .body("string") + .build()); + } +} diff --git a/seed/java-sdk/exhaustive/custom-dependency/reference.md b/seed/java-sdk/exhaustive/custom-dependency/reference.md index 650f282e620a..6c66e7b30c98 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/reference.md +++ b/seed/java-sdk/exhaustive/custom-dependency/reference.md @@ -1874,6 +1874,60 @@ client.endpoints().params().modifyWithPath("param", "string"); + + + + +
client.endpoints.params.uploadWithPath(param, request) -> ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```java +client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `String` + +
+
+
+
+ +
diff --git a/seed/java-sdk/exhaustive/custom-dependency/snippet.json b/seed/java-sdk/exhaustive/custom-dependency/snippet.json index 99ce0c05814a..b2c2c1336e79 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/snippet.json +++ b/seed/java-sdk/exhaustive/custom-dependency/snippet.json @@ -429,6 +429,19 @@ "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().modifyWithPath(\"param\", \"string\");\n }\n}\n" } }, + { + "example_identifier": "b305eac1", + "id": { + "method": "POST", + "path": "/params/path/{param}", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "java", + "sync_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n", + "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n" + } + }, { "example_identifier": "d8492cb1", "id": { diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java index 5fe44031aa45..2f6f8508a579 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; public class AsyncParamsClient { @@ -147,4 +149,34 @@ public CompletableFuture modifyWithInlinePath( ModifyResourceAtInlinedPath request, RequestOptions requestOptions) { return this.rawClient.modifyWithInlinePath(request, requestOptions).thenApply(response -> response.body()); } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java index 03b9ad6bcd21..d099ea37bce1 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,12 +19,16 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -538,4 +543,83 @@ public void onFailure(@NotNull Call call, @NotNull IOException e) { }); return future; } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response)); + return; + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (IOException e) { + future.completeExceptionally( + new SeedExhaustiveException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new SeedExhaustiveException("Network error executing HTTP request", e)); + } + }); + return future; + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java index 1e1def81f07d..a3db6c5f6dec 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; public class ParamsClient { protected final ClientOptions clientOptions; @@ -140,4 +142,32 @@ public String modifyWithInlinePath(ModifyResourceAtInlinedPath request) { public String modifyWithInlinePath(ModifyResourceAtInlinedPath request, RequestOptions requestOptions) { return this.rawClient.modifyWithInlinePath(request, requestOptions).body(); } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java index 4be7bb4e5e17..435a1f4afb88 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,9 +19,13 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -417,4 +422,67 @@ public SeedExhaustiveHttpResponse modifyWithInlinePath( throw new SeedExhaustiveException("Network error executing HTTP request", e); } } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .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(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response); + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example33.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example33.java index 6156d87604dd..460f05ef2737 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example33.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example33.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnString("string"); + client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example34.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example34.java index 8b17b19dc71e..e46c7fcb990f 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example34.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example34.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnInt(1); + client.endpoints().primitive().getAndReturnString("string"); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example35.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example35.java index 5ffa8dada196..68a3b26c1a0a 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example35.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example35.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnLong(1000000L); + client.endpoints().primitive().getAndReturnInt(1); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example36.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example36.java index e516a87a8f69..22782f9ac5df 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example36.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example36.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDouble(1.1); + client.endpoints().primitive().getAndReturnLong(1000000L); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example37.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example37.java index 1fe9e261ba37..a934874d8af6 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example37.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example37.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBool(true); + client.endpoints().primitive().getAndReturnDouble(1.1); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example38.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example38.java index ba0362ff4543..d9c5b5323f35 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example38.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example38.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.time.OffsetDateTime; public class Example38 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); + client.endpoints().primitive().getAndReturnBool(true); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example39.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example39.java index 5efebb9981a2..43192321b29f 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example39.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example39.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.time.OffsetDateTime; public class Example39 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDate("2023-01-15"); + client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example40.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example40.java index 73de5bc5604d..0245016f6194 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example40.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example40.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.util.UUID; public class Example40 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); + client.endpoints().primitive().getAndReturnDate("2023-01-15"); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example41.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example41.java index 5d435e059a24..6df02f99de5c 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example41.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example41.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.UUID; public class Example41 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); + client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example42.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example42.java index dbeade9154f8..299978a13411 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example42.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example42.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example42 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().put().add(PutRequest.builder().id("id").build()); + client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example43.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example43.java index 3313bfd6e768..62b17cb0fa74 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example43.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example43.java @@ -1,8 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.types.union.types.Animal; -import com.seed.exhaustive.resources.types.union.types.Dog; +import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example43 { public static void main(String[] args) { @@ -11,9 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints() - .union() - .getAndReturnUnion( - Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); + client.endpoints().put().add(PutRequest.builder().id("id").build()); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example44.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example44.java index 5359cd42db44..db173b5a131c 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example44.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example44.java @@ -1,6 +1,8 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.types.union.types.Animal; +import com.seed.exhaustive.resources.types.union.types.Dog; public class Example44 { public static void main(String[] args) { @@ -9,6 +11,9 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withMixedCase(); + client.endpoints() + .union() + .getAndReturnUnion( + Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example45.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example45.java index b7c1fb916ad2..b2e73987416b 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example45.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example45.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().noEndingSlash(); + client.endpoints().urls().withMixedCase(); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example46.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example46.java index 4e43babb3f14..7143df1ebd75 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example46.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example46.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withEndingSlash(); + client.endpoints().urls().noEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example47.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example47.java index 9045034481ea..d830891a2b61 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example47.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example47.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withUnderscores(); + client.endpoints().urls().withEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example48.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example48.java index 40a0d12d7120..9431dc95cd09 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example48.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example48.java @@ -1,15 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; -import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; -import java.math.BigInteger; -import java.time.OffsetDateTime; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Optional; -import java.util.UUID; public class Example48 { public static void main(String[] args) { @@ -18,29 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.inlinedRequests() - .postWithObjectBodyandResponse(PostWithObjectBody.builder() - .string("string") - .integer(1) - .nestedObject(ObjectWithOptionalField.builder() - .string("string") - .integer(1) - .long_(1000000L) - .double_(1.1) - .bool(true) - .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) - .date("2023-01-15") - .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) - .base64("SGVsbG8gd29ybGQh".getBytes()) - .list(Optional.of(Arrays.asList("list", "list"))) - .set(new HashSet(Arrays.asList("set"))) - .map(new HashMap() { - { - put(1, "map"); - } - }) - .bigint(new BigInteger("1000000")) - .build()) - .build()); + client.endpoints().urls().withUnderscores(); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example50.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example50.java index 4f463fab0404..f8c4b4bfbf22 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example50.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example50.java @@ -1,7 +1,15 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.math.BigInteger; +import java.time.OffsetDateTime; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; +import java.util.UUID; public class Example50 { public static void main(String[] args) { @@ -10,10 +18,29 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noAuth().postWithNoAuth(new HashMap() { - { - put("key", "value"); - } - }); + client.inlinedRequests() + .postWithObjectBodyandResponse(PostWithObjectBody.builder() + .string("string") + .integer(1) + .nestedObject(ObjectWithOptionalField.builder() + .string("string") + .integer(1) + .long_(1000000L) + .double_(1.1) + .bool(true) + .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) + .date("2023-01-15") + .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) + .base64("SGVsbG8gd29ybGQh".getBytes()) + .list(Optional.of(Arrays.asList("list", "list"))) + .set(new HashSet(Arrays.asList("set"))) + .map(new HashMap() { + { + put(1, "map"); + } + }) + .bigint(new BigInteger("1000000")) + .build()) + .build()); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example52.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example52.java index 625192661cb8..b2b407485c7f 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example52.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example52.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.HashMap; public class Example52 { public static void main(String[] args) { @@ -9,6 +10,10 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().getWithNoRequestBody(); + client.noAuth().postWithNoAuth(new HashMap() { + { + put("key", "value"); + } + }); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example53.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example53.java index e13868d2213a..1812912a36c9 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example53.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example53.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().postWithNoRequestBody(); + client.noReqBody().getWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example54.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example54.java index ecc8b4f8e035..7ae68a1dbb75 100644 --- a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example54.java +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example54.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; public class Example54 { public static void main(String[] args) { @@ -10,11 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.reqWithHeaders() - .getWithCustomHeader(ReqWithHeaders.builder() - .xTestServiceHeader("X-TEST-SERVICE-HEADER") - .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") - .body("string") - .build()); + client.noReqBody().postWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example55.java b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example55.java new file mode 100644 index 000000000000..5ba6aebfd9fa --- /dev/null +++ b/seed/java-sdk/exhaustive/custom-dependency/src/main/java/com/snippets/Example55.java @@ -0,0 +1,20 @@ +package com.snippets; + +import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; + +public class Example55 { + public static void main(String[] args) { + SeedExhaustiveClient client = SeedExhaustiveClient.builder() + .token("") + .url("https://api.fern.com") + .build(); + + client.reqWithHeaders() + .getWithCustomHeader(ReqWithHeaders.builder() + .xTestServiceHeader("X-TEST-SERVICE-HEADER") + .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") + .body("string") + .build()); + } +} diff --git a/seed/java-sdk/exhaustive/custom-error-names/reference.md b/seed/java-sdk/exhaustive/custom-error-names/reference.md index 650f282e620a..6c66e7b30c98 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/reference.md +++ b/seed/java-sdk/exhaustive/custom-error-names/reference.md @@ -1874,6 +1874,60 @@ client.endpoints().params().modifyWithPath("param", "string"); + + + + +
client.endpoints.params.uploadWithPath(param, request) -> ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```java +client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `String` + +
+
+
+
+ +
diff --git a/seed/java-sdk/exhaustive/custom-error-names/snippet.json b/seed/java-sdk/exhaustive/custom-error-names/snippet.json index 99ce0c05814a..b2c2c1336e79 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/snippet.json +++ b/seed/java-sdk/exhaustive/custom-error-names/snippet.json @@ -429,6 +429,19 @@ "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().modifyWithPath(\"param\", \"string\");\n }\n}\n" } }, + { + "example_identifier": "b305eac1", + "id": { + "method": "POST", + "path": "/params/path/{param}", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "java", + "sync_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n", + "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n" + } + }, { "example_identifier": "d8492cb1", "id": { diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java index 5fe44031aa45..2f6f8508a579 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; public class AsyncParamsClient { @@ -147,4 +149,34 @@ public CompletableFuture modifyWithInlinePath( ModifyResourceAtInlinedPath request, RequestOptions requestOptions) { return this.rawClient.modifyWithInlinePath(request, requestOptions).thenApply(response -> response.body()); } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java index 41f9f3d52045..379eea57d345 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java @@ -7,6 +7,7 @@ import com.seed.exhaustive.core.ClientOptions; import com.seed.exhaustive.core.CustomApiException; import com.seed.exhaustive.core.CustomException; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,12 +19,16 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -530,4 +535,82 @@ public void onFailure(@NotNull Call call, @NotNull IOException e) { }); return future; } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response)); + return; + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new CustomApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (IOException e) { + future.completeExceptionally(new CustomException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new CustomException("Network error executing HTTP request", e)); + } + }); + return future; + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java index 1e1def81f07d..a3db6c5f6dec 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; public class ParamsClient { protected final ClientOptions clientOptions; @@ -140,4 +142,32 @@ public String modifyWithInlinePath(ModifyResourceAtInlinedPath request) { public String modifyWithInlinePath(ModifyResourceAtInlinedPath request, RequestOptions requestOptions) { return this.rawClient.modifyWithInlinePath(request, requestOptions).body(); } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java index 716bb4597a6e..30e5efed467a 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java @@ -7,6 +7,7 @@ import com.seed.exhaustive.core.ClientOptions; import com.seed.exhaustive.core.CustomApiException; import com.seed.exhaustive.core.CustomException; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,9 +19,13 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -417,4 +422,67 @@ public SeedExhaustiveHttpResponse modifyWithInlinePath( throw new CustomException("Network error executing HTTP request", e); } } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .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(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response); + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new CustomApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (IOException e) { + throw new CustomException("Network error executing HTTP request", e); + } + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example33.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example33.java index 6156d87604dd..460f05ef2737 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example33.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example33.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnString("string"); + client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example34.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example34.java index 8b17b19dc71e..e46c7fcb990f 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example34.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example34.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnInt(1); + client.endpoints().primitive().getAndReturnString("string"); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example35.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example35.java index 5ffa8dada196..68a3b26c1a0a 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example35.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example35.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnLong(1000000L); + client.endpoints().primitive().getAndReturnInt(1); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example36.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example36.java index e516a87a8f69..22782f9ac5df 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example36.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example36.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDouble(1.1); + client.endpoints().primitive().getAndReturnLong(1000000L); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example37.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example37.java index 1fe9e261ba37..a934874d8af6 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example37.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example37.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBool(true); + client.endpoints().primitive().getAndReturnDouble(1.1); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example38.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example38.java index ba0362ff4543..d9c5b5323f35 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example38.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example38.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.time.OffsetDateTime; public class Example38 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); + client.endpoints().primitive().getAndReturnBool(true); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example39.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example39.java index 5efebb9981a2..43192321b29f 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example39.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example39.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.time.OffsetDateTime; public class Example39 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDate("2023-01-15"); + client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example40.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example40.java index 73de5bc5604d..0245016f6194 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example40.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example40.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.util.UUID; public class Example40 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); + client.endpoints().primitive().getAndReturnDate("2023-01-15"); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example41.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example41.java index 5d435e059a24..6df02f99de5c 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example41.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example41.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.UUID; public class Example41 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); + client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example42.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example42.java index dbeade9154f8..299978a13411 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example42.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example42.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example42 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().put().add(PutRequest.builder().id("id").build()); + client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example43.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example43.java index 3313bfd6e768..62b17cb0fa74 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example43.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example43.java @@ -1,8 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.types.union.types.Animal; -import com.seed.exhaustive.resources.types.union.types.Dog; +import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example43 { public static void main(String[] args) { @@ -11,9 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints() - .union() - .getAndReturnUnion( - Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); + client.endpoints().put().add(PutRequest.builder().id("id").build()); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example44.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example44.java index 5359cd42db44..db173b5a131c 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example44.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example44.java @@ -1,6 +1,8 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.types.union.types.Animal; +import com.seed.exhaustive.resources.types.union.types.Dog; public class Example44 { public static void main(String[] args) { @@ -9,6 +11,9 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withMixedCase(); + client.endpoints() + .union() + .getAndReturnUnion( + Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example45.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example45.java index b7c1fb916ad2..b2e73987416b 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example45.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example45.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().noEndingSlash(); + client.endpoints().urls().withMixedCase(); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example46.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example46.java index 4e43babb3f14..7143df1ebd75 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example46.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example46.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withEndingSlash(); + client.endpoints().urls().noEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example47.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example47.java index 9045034481ea..d830891a2b61 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example47.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example47.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withUnderscores(); + client.endpoints().urls().withEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example48.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example48.java index 40a0d12d7120..9431dc95cd09 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example48.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example48.java @@ -1,15 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; -import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; -import java.math.BigInteger; -import java.time.OffsetDateTime; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Optional; -import java.util.UUID; public class Example48 { public static void main(String[] args) { @@ -18,29 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.inlinedRequests() - .postWithObjectBodyandResponse(PostWithObjectBody.builder() - .string("string") - .integer(1) - .nestedObject(ObjectWithOptionalField.builder() - .string("string") - .integer(1) - .long_(1000000L) - .double_(1.1) - .bool(true) - .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) - .date("2023-01-15") - .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) - .base64("SGVsbG8gd29ybGQh".getBytes()) - .list(Optional.of(Arrays.asList("list", "list"))) - .set(new HashSet(Arrays.asList("set"))) - .map(new HashMap() { - { - put(1, "map"); - } - }) - .bigint(new BigInteger("1000000")) - .build()) - .build()); + client.endpoints().urls().withUnderscores(); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example50.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example50.java index 4f463fab0404..f8c4b4bfbf22 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example50.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example50.java @@ -1,7 +1,15 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.math.BigInteger; +import java.time.OffsetDateTime; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; +import java.util.UUID; public class Example50 { public static void main(String[] args) { @@ -10,10 +18,29 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noAuth().postWithNoAuth(new HashMap() { - { - put("key", "value"); - } - }); + client.inlinedRequests() + .postWithObjectBodyandResponse(PostWithObjectBody.builder() + .string("string") + .integer(1) + .nestedObject(ObjectWithOptionalField.builder() + .string("string") + .integer(1) + .long_(1000000L) + .double_(1.1) + .bool(true) + .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) + .date("2023-01-15") + .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) + .base64("SGVsbG8gd29ybGQh".getBytes()) + .list(Optional.of(Arrays.asList("list", "list"))) + .set(new HashSet(Arrays.asList("set"))) + .map(new HashMap() { + { + put(1, "map"); + } + }) + .bigint(new BigInteger("1000000")) + .build()) + .build()); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example52.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example52.java index 625192661cb8..b2b407485c7f 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example52.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example52.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.HashMap; public class Example52 { public static void main(String[] args) { @@ -9,6 +10,10 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().getWithNoRequestBody(); + client.noAuth().postWithNoAuth(new HashMap() { + { + put("key", "value"); + } + }); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example53.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example53.java index e13868d2213a..1812912a36c9 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example53.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example53.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().postWithNoRequestBody(); + client.noReqBody().getWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example54.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example54.java index ecc8b4f8e035..7ae68a1dbb75 100644 --- a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example54.java +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example54.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; public class Example54 { public static void main(String[] args) { @@ -10,11 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.reqWithHeaders() - .getWithCustomHeader(ReqWithHeaders.builder() - .xTestServiceHeader("X-TEST-SERVICE-HEADER") - .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") - .body("string") - .build()); + client.noReqBody().postWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example55.java b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example55.java new file mode 100644 index 000000000000..5ba6aebfd9fa --- /dev/null +++ b/seed/java-sdk/exhaustive/custom-error-names/src/main/java/com/snippets/Example55.java @@ -0,0 +1,20 @@ +package com.snippets; + +import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; + +public class Example55 { + public static void main(String[] args) { + SeedExhaustiveClient client = SeedExhaustiveClient.builder() + .token("") + .url("https://api.fern.com") + .build(); + + client.reqWithHeaders() + .getWithCustomHeader(ReqWithHeaders.builder() + .xTestServiceHeader("X-TEST-SERVICE-HEADER") + .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") + .body("string") + .build()); + } +} diff --git a/seed/java-sdk/exhaustive/custom-license/reference.md b/seed/java-sdk/exhaustive/custom-license/reference.md index 03ec177642e4..86c185534f55 100644 --- a/seed/java-sdk/exhaustive/custom-license/reference.md +++ b/seed/java-sdk/exhaustive/custom-license/reference.md @@ -1874,6 +1874,60 @@ client.endpoints().params().modifyWithPath("param", "string"); + + + + +
client.endpoints.params.uploadWithPath(param, request) -> ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```java +client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `String` + +
+
+
+
+ +
diff --git a/seed/java-sdk/exhaustive/custom-license/snippet.json b/seed/java-sdk/exhaustive/custom-license/snippet.json index 7bf83465f22f..5fd1aab15320 100644 --- a/seed/java-sdk/exhaustive/custom-license/snippet.json +++ b/seed/java-sdk/exhaustive/custom-license/snippet.json @@ -429,6 +429,19 @@ "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().modifyWithPath(\"param\", \"string\");\n }\n}\n" } }, + { + "example_identifier": "b305eac1", + "id": { + "method": "POST", + "path": "/params/path/{param}", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "java", + "sync_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n", + "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n" + } + }, { "example_identifier": "d8492cb1", "id": { diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java index c55a5f96ff6d..6abe0f280ee8 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; public class AsyncParamsClient { @@ -166,4 +168,34 @@ public CompletableFuture modifyWithInlinePath( .modifyWithInlinePath(param, request, requestOptions) .thenApply(response -> response.body()); } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java index 6cd660dae734..50e287aa36aa 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,12 +19,16 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -554,4 +559,83 @@ public void onFailure(@NotNull Call call, @NotNull IOException e) { }); return future; } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response)); + return; + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (IOException e) { + future.completeExceptionally( + new SeedExhaustiveException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new SeedExhaustiveException("Network error executing HTTP request", e)); + } + }); + return future; + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java index f2d48e1da54b..4ad0329fcb7e 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; public class ParamsClient { protected final ClientOptions clientOptions; @@ -158,4 +160,32 @@ public String modifyWithInlinePath( .modifyWithInlinePath(param, request, requestOptions) .body(); } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java index b9cd2d6f43a3..fb3c57f3a19d 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,9 +19,13 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -431,4 +436,67 @@ public SeedExhaustiveHttpResponse modifyWithInlinePath( throw new SeedExhaustiveException("Network error executing HTTP request", e); } } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .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(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response); + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example33.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example33.java index 6156d87604dd..460f05ef2737 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example33.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example33.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnString("string"); + client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example34.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example34.java index 8b17b19dc71e..e46c7fcb990f 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example34.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example34.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnInt(1); + client.endpoints().primitive().getAndReturnString("string"); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example35.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example35.java index 5ffa8dada196..68a3b26c1a0a 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example35.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example35.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnLong(1000000L); + client.endpoints().primitive().getAndReturnInt(1); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example36.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example36.java index e516a87a8f69..22782f9ac5df 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example36.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example36.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDouble(1.1); + client.endpoints().primitive().getAndReturnLong(1000000L); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example37.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example37.java index 1fe9e261ba37..a934874d8af6 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example37.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example37.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBool(true); + client.endpoints().primitive().getAndReturnDouble(1.1); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example38.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example38.java index ba0362ff4543..d9c5b5323f35 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example38.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example38.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.time.OffsetDateTime; public class Example38 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); + client.endpoints().primitive().getAndReturnBool(true); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example39.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example39.java index 5efebb9981a2..43192321b29f 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example39.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example39.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.time.OffsetDateTime; public class Example39 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDate("2023-01-15"); + client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example40.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example40.java index 73de5bc5604d..0245016f6194 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example40.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example40.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.util.UUID; public class Example40 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); + client.endpoints().primitive().getAndReturnDate("2023-01-15"); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example41.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example41.java index 5d435e059a24..6df02f99de5c 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example41.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example41.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.UUID; public class Example41 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); + client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example42.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example42.java index 840b06e3388c..299978a13411 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example42.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example42.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example42 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().put().add("id", PutRequest.builder().build()); + client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example43.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example43.java index 3313bfd6e768..3251ccac3d0f 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example43.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example43.java @@ -1,8 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.types.union.types.Animal; -import com.seed.exhaustive.resources.types.union.types.Dog; +import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example43 { public static void main(String[] args) { @@ -11,9 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints() - .union() - .getAndReturnUnion( - Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); + client.endpoints().put().add("id", PutRequest.builder().build()); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example44.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example44.java index 5359cd42db44..db173b5a131c 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example44.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example44.java @@ -1,6 +1,8 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.types.union.types.Animal; +import com.seed.exhaustive.resources.types.union.types.Dog; public class Example44 { public static void main(String[] args) { @@ -9,6 +11,9 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withMixedCase(); + client.endpoints() + .union() + .getAndReturnUnion( + Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example45.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example45.java index b7c1fb916ad2..b2e73987416b 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example45.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example45.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().noEndingSlash(); + client.endpoints().urls().withMixedCase(); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example46.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example46.java index 4e43babb3f14..7143df1ebd75 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example46.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example46.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withEndingSlash(); + client.endpoints().urls().noEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example47.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example47.java index 9045034481ea..d830891a2b61 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example47.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example47.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withUnderscores(); + client.endpoints().urls().withEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example48.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example48.java index 40a0d12d7120..9431dc95cd09 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example48.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example48.java @@ -1,15 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; -import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; -import java.math.BigInteger; -import java.time.OffsetDateTime; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Optional; -import java.util.UUID; public class Example48 { public static void main(String[] args) { @@ -18,29 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.inlinedRequests() - .postWithObjectBodyandResponse(PostWithObjectBody.builder() - .string("string") - .integer(1) - .nestedObject(ObjectWithOptionalField.builder() - .string("string") - .integer(1) - .long_(1000000L) - .double_(1.1) - .bool(true) - .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) - .date("2023-01-15") - .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) - .base64("SGVsbG8gd29ybGQh".getBytes()) - .list(Optional.of(Arrays.asList("list", "list"))) - .set(new HashSet(Arrays.asList("set"))) - .map(new HashMap() { - { - put(1, "map"); - } - }) - .bigint(new BigInteger("1000000")) - .build()) - .build()); + client.endpoints().urls().withUnderscores(); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example50.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example50.java index 4f463fab0404..f8c4b4bfbf22 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example50.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example50.java @@ -1,7 +1,15 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.math.BigInteger; +import java.time.OffsetDateTime; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; +import java.util.UUID; public class Example50 { public static void main(String[] args) { @@ -10,10 +18,29 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noAuth().postWithNoAuth(new HashMap() { - { - put("key", "value"); - } - }); + client.inlinedRequests() + .postWithObjectBodyandResponse(PostWithObjectBody.builder() + .string("string") + .integer(1) + .nestedObject(ObjectWithOptionalField.builder() + .string("string") + .integer(1) + .long_(1000000L) + .double_(1.1) + .bool(true) + .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) + .date("2023-01-15") + .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) + .base64("SGVsbG8gd29ybGQh".getBytes()) + .list(Optional.of(Arrays.asList("list", "list"))) + .set(new HashSet(Arrays.asList("set"))) + .map(new HashMap() { + { + put(1, "map"); + } + }) + .bigint(new BigInteger("1000000")) + .build()) + .build()); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example52.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example52.java index 625192661cb8..b2b407485c7f 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example52.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example52.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.HashMap; public class Example52 { public static void main(String[] args) { @@ -9,6 +10,10 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().getWithNoRequestBody(); + client.noAuth().postWithNoAuth(new HashMap() { + { + put("key", "value"); + } + }); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example53.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example53.java index e13868d2213a..1812912a36c9 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example53.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example53.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().postWithNoRequestBody(); + client.noReqBody().getWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example54.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example54.java index ecc8b4f8e035..7ae68a1dbb75 100644 --- a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example54.java +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example54.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; public class Example54 { public static void main(String[] args) { @@ -10,11 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.reqWithHeaders() - .getWithCustomHeader(ReqWithHeaders.builder() - .xTestServiceHeader("X-TEST-SERVICE-HEADER") - .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") - .body("string") - .build()); + client.noReqBody().postWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example55.java b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example55.java new file mode 100644 index 000000000000..5ba6aebfd9fa --- /dev/null +++ b/seed/java-sdk/exhaustive/custom-license/src/main/java/com/snippets/Example55.java @@ -0,0 +1,20 @@ +package com.snippets; + +import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; + +public class Example55 { + public static void main(String[] args) { + SeedExhaustiveClient client = SeedExhaustiveClient.builder() + .token("") + .url("https://api.fern.com") + .build(); + + client.reqWithHeaders() + .getWithCustomHeader(ReqWithHeaders.builder() + .xTestServiceHeader("X-TEST-SERVICE-HEADER") + .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") + .body("string") + .build()); + } +} diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/reference.md b/seed/java-sdk/exhaustive/enable-public-constructors/reference.md index 650f282e620a..6c66e7b30c98 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/reference.md +++ b/seed/java-sdk/exhaustive/enable-public-constructors/reference.md @@ -1874,6 +1874,60 @@ client.endpoints().params().modifyWithPath("param", "string"); + + + + +
client.endpoints.params.uploadWithPath(param, request) -> ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```java +client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `String` + +
+
+
+
+ +
diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/snippet.json b/seed/java-sdk/exhaustive/enable-public-constructors/snippet.json index 99ce0c05814a..b2c2c1336e79 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/snippet.json +++ b/seed/java-sdk/exhaustive/enable-public-constructors/snippet.json @@ -429,6 +429,19 @@ "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().modifyWithPath(\"param\", \"string\");\n }\n}\n" } }, + { + "example_identifier": "b305eac1", + "id": { + "method": "POST", + "path": "/params/path/{param}", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "java", + "sync_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n", + "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n" + } + }, { "example_identifier": "d8492cb1", "id": { diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java index 5fe44031aa45..2f6f8508a579 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; public class AsyncParamsClient { @@ -147,4 +149,34 @@ public CompletableFuture modifyWithInlinePath( ModifyResourceAtInlinedPath request, RequestOptions requestOptions) { return this.rawClient.modifyWithInlinePath(request, requestOptions).thenApply(response -> response.body()); } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java index 03b9ad6bcd21..d099ea37bce1 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,12 +19,16 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -538,4 +543,83 @@ public void onFailure(@NotNull Call call, @NotNull IOException e) { }); return future; } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response)); + return; + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (IOException e) { + future.completeExceptionally( + new SeedExhaustiveException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new SeedExhaustiveException("Network error executing HTTP request", e)); + } + }); + return future; + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java index 1e1def81f07d..a3db6c5f6dec 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; public class ParamsClient { protected final ClientOptions clientOptions; @@ -140,4 +142,32 @@ public String modifyWithInlinePath(ModifyResourceAtInlinedPath request) { public String modifyWithInlinePath(ModifyResourceAtInlinedPath request, RequestOptions requestOptions) { return this.rawClient.modifyWithInlinePath(request, requestOptions).body(); } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java index 4be7bb4e5e17..435a1f4afb88 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,9 +19,13 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -417,4 +422,67 @@ public SeedExhaustiveHttpResponse modifyWithInlinePath( throw new SeedExhaustiveException("Network error executing HTTP request", e); } } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .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(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response); + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example33.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example33.java index 6156d87604dd..460f05ef2737 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example33.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example33.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnString("string"); + client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example34.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example34.java index 8b17b19dc71e..e46c7fcb990f 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example34.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example34.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnInt(1); + client.endpoints().primitive().getAndReturnString("string"); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example35.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example35.java index 5ffa8dada196..68a3b26c1a0a 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example35.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example35.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnLong(1000000L); + client.endpoints().primitive().getAndReturnInt(1); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example36.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example36.java index e516a87a8f69..22782f9ac5df 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example36.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example36.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDouble(1.1); + client.endpoints().primitive().getAndReturnLong(1000000L); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example37.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example37.java index 1fe9e261ba37..a934874d8af6 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example37.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example37.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBool(true); + client.endpoints().primitive().getAndReturnDouble(1.1); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example38.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example38.java index ba0362ff4543..d9c5b5323f35 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example38.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example38.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.time.OffsetDateTime; public class Example38 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); + client.endpoints().primitive().getAndReturnBool(true); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example39.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example39.java index 5efebb9981a2..43192321b29f 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example39.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example39.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.time.OffsetDateTime; public class Example39 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDate("2023-01-15"); + client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example40.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example40.java index 73de5bc5604d..0245016f6194 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example40.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example40.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.util.UUID; public class Example40 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); + client.endpoints().primitive().getAndReturnDate("2023-01-15"); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example41.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example41.java index 5d435e059a24..6df02f99de5c 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example41.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example41.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.UUID; public class Example41 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); + client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example42.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example42.java index dbeade9154f8..299978a13411 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example42.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example42.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example42 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().put().add(PutRequest.builder().id("id").build()); + client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example43.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example43.java index 3313bfd6e768..62b17cb0fa74 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example43.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example43.java @@ -1,8 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.types.union.types.Animal; -import com.seed.exhaustive.resources.types.union.types.Dog; +import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example43 { public static void main(String[] args) { @@ -11,9 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints() - .union() - .getAndReturnUnion( - Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); + client.endpoints().put().add(PutRequest.builder().id("id").build()); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example44.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example44.java index 5359cd42db44..db173b5a131c 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example44.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example44.java @@ -1,6 +1,8 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.types.union.types.Animal; +import com.seed.exhaustive.resources.types.union.types.Dog; public class Example44 { public static void main(String[] args) { @@ -9,6 +11,9 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withMixedCase(); + client.endpoints() + .union() + .getAndReturnUnion( + Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example45.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example45.java index b7c1fb916ad2..b2e73987416b 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example45.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example45.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().noEndingSlash(); + client.endpoints().urls().withMixedCase(); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example46.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example46.java index 4e43babb3f14..7143df1ebd75 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example46.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example46.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withEndingSlash(); + client.endpoints().urls().noEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example47.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example47.java index 9045034481ea..d830891a2b61 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example47.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example47.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withUnderscores(); + client.endpoints().urls().withEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example48.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example48.java index 40a0d12d7120..9431dc95cd09 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example48.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example48.java @@ -1,15 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; -import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; -import java.math.BigInteger; -import java.time.OffsetDateTime; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Optional; -import java.util.UUID; public class Example48 { public static void main(String[] args) { @@ -18,29 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.inlinedRequests() - .postWithObjectBodyandResponse(PostWithObjectBody.builder() - .string("string") - .integer(1) - .nestedObject(ObjectWithOptionalField.builder() - .string("string") - .integer(1) - .long_(1000000L) - .double_(1.1) - .bool(true) - .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) - .date("2023-01-15") - .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) - .base64("SGVsbG8gd29ybGQh".getBytes()) - .list(Optional.of(Arrays.asList("list", "list"))) - .set(new HashSet(Arrays.asList("set"))) - .map(new HashMap() { - { - put(1, "map"); - } - }) - .bigint(new BigInteger("1000000")) - .build()) - .build()); + client.endpoints().urls().withUnderscores(); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example50.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example50.java index 4f463fab0404..f8c4b4bfbf22 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example50.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example50.java @@ -1,7 +1,15 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.math.BigInteger; +import java.time.OffsetDateTime; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; +import java.util.UUID; public class Example50 { public static void main(String[] args) { @@ -10,10 +18,29 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noAuth().postWithNoAuth(new HashMap() { - { - put("key", "value"); - } - }); + client.inlinedRequests() + .postWithObjectBodyandResponse(PostWithObjectBody.builder() + .string("string") + .integer(1) + .nestedObject(ObjectWithOptionalField.builder() + .string("string") + .integer(1) + .long_(1000000L) + .double_(1.1) + .bool(true) + .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) + .date("2023-01-15") + .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) + .base64("SGVsbG8gd29ybGQh".getBytes()) + .list(Optional.of(Arrays.asList("list", "list"))) + .set(new HashSet(Arrays.asList("set"))) + .map(new HashMap() { + { + put(1, "map"); + } + }) + .bigint(new BigInteger("1000000")) + .build()) + .build()); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example52.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example52.java index 625192661cb8..b2b407485c7f 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example52.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example52.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.HashMap; public class Example52 { public static void main(String[] args) { @@ -9,6 +10,10 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().getWithNoRequestBody(); + client.noAuth().postWithNoAuth(new HashMap() { + { + put("key", "value"); + } + }); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example53.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example53.java index e13868d2213a..1812912a36c9 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example53.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example53.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().postWithNoRequestBody(); + client.noReqBody().getWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example54.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example54.java index ecc8b4f8e035..7ae68a1dbb75 100644 --- a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example54.java +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example54.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; public class Example54 { public static void main(String[] args) { @@ -10,11 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.reqWithHeaders() - .getWithCustomHeader(ReqWithHeaders.builder() - .xTestServiceHeader("X-TEST-SERVICE-HEADER") - .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") - .body("string") - .build()); + client.noReqBody().postWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example55.java b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example55.java new file mode 100644 index 000000000000..5ba6aebfd9fa --- /dev/null +++ b/seed/java-sdk/exhaustive/enable-public-constructors/src/main/java/com/snippets/Example55.java @@ -0,0 +1,20 @@ +package com.snippets; + +import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; + +public class Example55 { + public static void main(String[] args) { + SeedExhaustiveClient client = SeedExhaustiveClient.builder() + .token("") + .url("https://api.fern.com") + .build(); + + client.reqWithHeaders() + .getWithCustomHeader(ReqWithHeaders.builder() + .xTestServiceHeader("X-TEST-SERVICE-HEADER") + .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") + .body("string") + .build()); + } +} diff --git a/seed/java-sdk/exhaustive/flat-package-layout/reference.md b/seed/java-sdk/exhaustive/flat-package-layout/reference.md index 03ec177642e4..86c185534f55 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/reference.md +++ b/seed/java-sdk/exhaustive/flat-package-layout/reference.md @@ -1874,6 +1874,60 @@ client.endpoints().params().modifyWithPath("param", "string"); + + + + +
client.endpoints.params.uploadWithPath(param, request) -> ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```java +client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `String` + +
+
+
+
+ +
diff --git a/seed/java-sdk/exhaustive/flat-package-layout/snippet.json b/seed/java-sdk/exhaustive/flat-package-layout/snippet.json index 17569858da33..103a34da05a1 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/snippet.json +++ b/seed/java-sdk/exhaustive/flat-package-layout/snippet.json @@ -429,6 +429,19 @@ "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().modifyWithPath(\"param\", \"string\");\n }\n}\n" } }, + { + "example_identifier": "b305eac1", + "id": { + "method": "POST", + "path": "/params/path/{param}", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "java", + "sync_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n", + "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n" + } + }, { "example_identifier": "d8492cb1", "id": { diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/seed/exhaustive/endpoints/AsyncParamsClient.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/seed/exhaustive/endpoints/AsyncParamsClient.java index 29a00a4178d8..f9ada48a136b 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/seed/exhaustive/endpoints/AsyncParamsClient.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/seed/exhaustive/endpoints/AsyncParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.endpoints.types.GetWithPathAndQuery; import com.seed.exhaustive.endpoints.types.GetWithQuery; import com.seed.exhaustive.endpoints.types.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.types.types.ObjectWithRequiredField; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; public class AsyncParamsClient { @@ -166,4 +168,34 @@ public CompletableFuture modifyWithInlinePath( .modifyWithInlinePath(param, request, requestOptions) .thenApply(response -> response.body()); } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/seed/exhaustive/endpoints/AsyncRawParamsClient.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/seed/exhaustive/endpoints/AsyncRawParamsClient.java index 04d3d7008009..7d0660c3df65 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/seed/exhaustive/endpoints/AsyncRawParamsClient.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/seed/exhaustive/endpoints/AsyncRawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,12 +19,16 @@ import com.seed.exhaustive.endpoints.types.GetWithPathAndQuery; import com.seed.exhaustive.endpoints.types.GetWithQuery; import com.seed.exhaustive.endpoints.types.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.types.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -554,4 +559,83 @@ public void onFailure(@NotNull Call call, @NotNull IOException e) { }); return future; } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response)); + return; + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (IOException e) { + future.completeExceptionally( + new SeedExhaustiveException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new SeedExhaustiveException("Network error executing HTTP request", e)); + } + }); + return future; + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/seed/exhaustive/endpoints/ParamsClient.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/seed/exhaustive/endpoints/ParamsClient.java index 68b5fb94554b..84bb2e98d13c 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/seed/exhaustive/endpoints/ParamsClient.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/seed/exhaustive/endpoints/ParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.endpoints.types.GetWithPathAndQuery; import com.seed.exhaustive.endpoints.types.GetWithQuery; import com.seed.exhaustive.endpoints.types.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.types.types.ObjectWithRequiredField; +import java.io.InputStream; public class ParamsClient { protected final ClientOptions clientOptions; @@ -158,4 +160,32 @@ public String modifyWithInlinePath( .modifyWithInlinePath(param, request, requestOptions) .body(); } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/seed/exhaustive/endpoints/RawParamsClient.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/seed/exhaustive/endpoints/RawParamsClient.java index 31a1b46e06de..2a1a34546c0f 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/seed/exhaustive/endpoints/RawParamsClient.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/seed/exhaustive/endpoints/RawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,9 +19,13 @@ import com.seed.exhaustive.endpoints.types.GetWithPathAndQuery; import com.seed.exhaustive.endpoints.types.GetWithQuery; import com.seed.exhaustive.endpoints.types.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.types.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -431,4 +436,67 @@ public SeedExhaustiveHttpResponse modifyWithInlinePath( throw new SeedExhaustiveException("Network error executing HTTP request", e); } } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .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(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response); + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example33.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example33.java index 6156d87604dd..460f05ef2737 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example33.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example33.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnString("string"); + client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example34.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example34.java index 8b17b19dc71e..e46c7fcb990f 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example34.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example34.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnInt(1); + client.endpoints().primitive().getAndReturnString("string"); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example35.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example35.java index 5ffa8dada196..68a3b26c1a0a 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example35.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example35.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnLong(1000000L); + client.endpoints().primitive().getAndReturnInt(1); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example36.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example36.java index e516a87a8f69..22782f9ac5df 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example36.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example36.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDouble(1.1); + client.endpoints().primitive().getAndReturnLong(1000000L); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example37.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example37.java index 1fe9e261ba37..a934874d8af6 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example37.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example37.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBool(true); + client.endpoints().primitive().getAndReturnDouble(1.1); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example38.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example38.java index ba0362ff4543..d9c5b5323f35 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example38.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example38.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.time.OffsetDateTime; public class Example38 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); + client.endpoints().primitive().getAndReturnBool(true); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example39.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example39.java index 5efebb9981a2..43192321b29f 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example39.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example39.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.time.OffsetDateTime; public class Example39 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDate("2023-01-15"); + client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example40.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example40.java index 73de5bc5604d..0245016f6194 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example40.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example40.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.util.UUID; public class Example40 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); + client.endpoints().primitive().getAndReturnDate("2023-01-15"); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example41.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example41.java index 5d435e059a24..6df02f99de5c 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example41.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example41.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.UUID; public class Example41 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); + client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example42.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example42.java index 55f41e44e918..299978a13411 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example42.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example42.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.endpoints.types.PutRequest; public class Example42 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().put().add("id", PutRequest.builder().build()); + client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example43.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example43.java index 6a687374f5cb..eecdd0cae44f 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example43.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example43.java @@ -1,8 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.types.types.Animal; -import com.seed.exhaustive.types.types.Dog; +import com.seed.exhaustive.endpoints.types.PutRequest; public class Example43 { public static void main(String[] args) { @@ -11,9 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints() - .union() - .getAndReturnUnion( - Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); + client.endpoints().put().add("id", PutRequest.builder().build()); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example44.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example44.java index 5359cd42db44..8b7a2db8e7c7 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example44.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example44.java @@ -1,6 +1,8 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.types.types.Animal; +import com.seed.exhaustive.types.types.Dog; public class Example44 { public static void main(String[] args) { @@ -9,6 +11,9 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withMixedCase(); + client.endpoints() + .union() + .getAndReturnUnion( + Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example45.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example45.java index b7c1fb916ad2..b2e73987416b 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example45.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example45.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().noEndingSlash(); + client.endpoints().urls().withMixedCase(); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example46.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example46.java index 4e43babb3f14..7143df1ebd75 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example46.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example46.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withEndingSlash(); + client.endpoints().urls().noEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example47.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example47.java index 9045034481ea..d830891a2b61 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example47.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example47.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withUnderscores(); + client.endpoints().urls().withEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example48.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example48.java index 16b16fb9d87e..9431dc95cd09 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example48.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example48.java @@ -1,15 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.types.PostWithObjectBody; -import com.seed.exhaustive.types.types.ObjectWithOptionalField; -import java.math.BigInteger; -import java.time.OffsetDateTime; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Optional; -import java.util.UUID; public class Example48 { public static void main(String[] args) { @@ -18,29 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.inlinedRequests() - .postWithObjectBodyandResponse(PostWithObjectBody.builder() - .string("string") - .integer(1) - .nestedObject(ObjectWithOptionalField.builder() - .string("string") - .integer(1) - .long_(1000000L) - .double_(1.1) - .bool(true) - .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) - .date("2023-01-15") - .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) - .base64("SGVsbG8gd29ybGQh".getBytes()) - .list(Optional.of(Arrays.asList("list", "list"))) - .set(new HashSet(Arrays.asList("set"))) - .map(new HashMap() { - { - put(1, "map"); - } - }) - .bigint(new BigInteger("1000000")) - .build()) - .build()); + client.endpoints().urls().withUnderscores(); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example50.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example50.java index 4f463fab0404..195ed5317812 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example50.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example50.java @@ -1,7 +1,15 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.types.PostWithObjectBody; +import com.seed.exhaustive.types.types.ObjectWithOptionalField; +import java.math.BigInteger; +import java.time.OffsetDateTime; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; +import java.util.UUID; public class Example50 { public static void main(String[] args) { @@ -10,10 +18,29 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noAuth().postWithNoAuth(new HashMap() { - { - put("key", "value"); - } - }); + client.inlinedRequests() + .postWithObjectBodyandResponse(PostWithObjectBody.builder() + .string("string") + .integer(1) + .nestedObject(ObjectWithOptionalField.builder() + .string("string") + .integer(1) + .long_(1000000L) + .double_(1.1) + .bool(true) + .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) + .date("2023-01-15") + .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) + .base64("SGVsbG8gd29ybGQh".getBytes()) + .list(Optional.of(Arrays.asList("list", "list"))) + .set(new HashSet(Arrays.asList("set"))) + .map(new HashMap() { + { + put(1, "map"); + } + }) + .bigint(new BigInteger("1000000")) + .build()) + .build()); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example52.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example52.java index 625192661cb8..b2b407485c7f 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example52.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example52.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.HashMap; public class Example52 { public static void main(String[] args) { @@ -9,6 +10,10 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().getWithNoRequestBody(); + client.noAuth().postWithNoAuth(new HashMap() { + { + put("key", "value"); + } + }); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example53.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example53.java index e13868d2213a..1812912a36c9 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example53.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example53.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().postWithNoRequestBody(); + client.noReqBody().getWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example54.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example54.java index a940e114750a..7ae68a1dbb75 100644 --- a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example54.java +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example54.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.types.ReqWithHeaders; public class Example54 { public static void main(String[] args) { @@ -10,11 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.reqWithHeaders() - .getWithCustomHeader(ReqWithHeaders.builder() - .xTestServiceHeader("X-TEST-SERVICE-HEADER") - .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") - .body("string") - .build()); + client.noReqBody().postWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example55.java b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example55.java new file mode 100644 index 000000000000..b68fdc737212 --- /dev/null +++ b/seed/java-sdk/exhaustive/flat-package-layout/src/main/java/com/snippets/Example55.java @@ -0,0 +1,20 @@ +package com.snippets; + +import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.types.ReqWithHeaders; + +public class Example55 { + public static void main(String[] args) { + SeedExhaustiveClient client = SeedExhaustiveClient.builder() + .token("") + .url("https://api.fern.com") + .build(); + + client.reqWithHeaders() + .getWithCustomHeader(ReqWithHeaders.builder() + .xTestServiceHeader("X-TEST-SERVICE-HEADER") + .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") + .body("string") + .build()); + } +} diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/reference.md b/seed/java-sdk/exhaustive/forward-compatible-enums/reference.md index 650f282e620a..6c66e7b30c98 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/reference.md +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/reference.md @@ -1874,6 +1874,60 @@ client.endpoints().params().modifyWithPath("param", "string"); + + + + +
client.endpoints.params.uploadWithPath(param, request) -> ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```java +client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `String` + +
+
+
+
+ +
diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/snippet.json b/seed/java-sdk/exhaustive/forward-compatible-enums/snippet.json index 99ce0c05814a..b2c2c1336e79 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/snippet.json +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/snippet.json @@ -429,6 +429,19 @@ "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().modifyWithPath(\"param\", \"string\");\n }\n}\n" } }, + { + "example_identifier": "b305eac1", + "id": { + "method": "POST", + "path": "/params/path/{param}", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "java", + "sync_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n", + "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n" + } + }, { "example_identifier": "d8492cb1", "id": { diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java index 5fe44031aa45..2f6f8508a579 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; public class AsyncParamsClient { @@ -147,4 +149,34 @@ public CompletableFuture modifyWithInlinePath( ModifyResourceAtInlinedPath request, RequestOptions requestOptions) { return this.rawClient.modifyWithInlinePath(request, requestOptions).thenApply(response -> response.body()); } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java index 03b9ad6bcd21..d099ea37bce1 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,12 +19,16 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -538,4 +543,83 @@ public void onFailure(@NotNull Call call, @NotNull IOException e) { }); return future; } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response)); + return; + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (IOException e) { + future.completeExceptionally( + new SeedExhaustiveException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new SeedExhaustiveException("Network error executing HTTP request", e)); + } + }); + return future; + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java index 1e1def81f07d..a3db6c5f6dec 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; public class ParamsClient { protected final ClientOptions clientOptions; @@ -140,4 +142,32 @@ public String modifyWithInlinePath(ModifyResourceAtInlinedPath request) { public String modifyWithInlinePath(ModifyResourceAtInlinedPath request, RequestOptions requestOptions) { return this.rawClient.modifyWithInlinePath(request, requestOptions).body(); } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java index 4be7bb4e5e17..435a1f4afb88 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,9 +19,13 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -417,4 +422,67 @@ public SeedExhaustiveHttpResponse modifyWithInlinePath( throw new SeedExhaustiveException("Network error executing HTTP request", e); } } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .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(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response); + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example33.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example33.java index 6156d87604dd..460f05ef2737 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example33.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example33.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnString("string"); + client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example34.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example34.java index 8b17b19dc71e..e46c7fcb990f 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example34.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example34.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnInt(1); + client.endpoints().primitive().getAndReturnString("string"); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example35.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example35.java index 5ffa8dada196..68a3b26c1a0a 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example35.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example35.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnLong(1000000L); + client.endpoints().primitive().getAndReturnInt(1); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example36.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example36.java index e516a87a8f69..22782f9ac5df 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example36.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example36.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDouble(1.1); + client.endpoints().primitive().getAndReturnLong(1000000L); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example37.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example37.java index 1fe9e261ba37..a934874d8af6 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example37.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example37.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBool(true); + client.endpoints().primitive().getAndReturnDouble(1.1); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example38.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example38.java index ba0362ff4543..d9c5b5323f35 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example38.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example38.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.time.OffsetDateTime; public class Example38 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); + client.endpoints().primitive().getAndReturnBool(true); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example39.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example39.java index 5efebb9981a2..43192321b29f 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example39.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example39.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.time.OffsetDateTime; public class Example39 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDate("2023-01-15"); + client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example40.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example40.java index 73de5bc5604d..0245016f6194 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example40.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example40.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.util.UUID; public class Example40 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); + client.endpoints().primitive().getAndReturnDate("2023-01-15"); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example41.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example41.java index 5d435e059a24..6df02f99de5c 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example41.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example41.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.UUID; public class Example41 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); + client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example42.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example42.java index dbeade9154f8..299978a13411 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example42.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example42.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example42 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().put().add(PutRequest.builder().id("id").build()); + client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example43.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example43.java index 3313bfd6e768..62b17cb0fa74 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example43.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example43.java @@ -1,8 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.types.union.types.Animal; -import com.seed.exhaustive.resources.types.union.types.Dog; +import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example43 { public static void main(String[] args) { @@ -11,9 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints() - .union() - .getAndReturnUnion( - Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); + client.endpoints().put().add(PutRequest.builder().id("id").build()); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example44.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example44.java index 5359cd42db44..db173b5a131c 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example44.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example44.java @@ -1,6 +1,8 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.types.union.types.Animal; +import com.seed.exhaustive.resources.types.union.types.Dog; public class Example44 { public static void main(String[] args) { @@ -9,6 +11,9 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withMixedCase(); + client.endpoints() + .union() + .getAndReturnUnion( + Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example45.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example45.java index b7c1fb916ad2..b2e73987416b 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example45.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example45.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().noEndingSlash(); + client.endpoints().urls().withMixedCase(); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example46.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example46.java index 4e43babb3f14..7143df1ebd75 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example46.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example46.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withEndingSlash(); + client.endpoints().urls().noEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example47.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example47.java index 9045034481ea..d830891a2b61 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example47.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example47.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withUnderscores(); + client.endpoints().urls().withEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example48.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example48.java index 40a0d12d7120..9431dc95cd09 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example48.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example48.java @@ -1,15 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; -import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; -import java.math.BigInteger; -import java.time.OffsetDateTime; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Optional; -import java.util.UUID; public class Example48 { public static void main(String[] args) { @@ -18,29 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.inlinedRequests() - .postWithObjectBodyandResponse(PostWithObjectBody.builder() - .string("string") - .integer(1) - .nestedObject(ObjectWithOptionalField.builder() - .string("string") - .integer(1) - .long_(1000000L) - .double_(1.1) - .bool(true) - .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) - .date("2023-01-15") - .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) - .base64("SGVsbG8gd29ybGQh".getBytes()) - .list(Optional.of(Arrays.asList("list", "list"))) - .set(new HashSet(Arrays.asList("set"))) - .map(new HashMap() { - { - put(1, "map"); - } - }) - .bigint(new BigInteger("1000000")) - .build()) - .build()); + client.endpoints().urls().withUnderscores(); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example50.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example50.java index 4f463fab0404..f8c4b4bfbf22 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example50.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example50.java @@ -1,7 +1,15 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.math.BigInteger; +import java.time.OffsetDateTime; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; +import java.util.UUID; public class Example50 { public static void main(String[] args) { @@ -10,10 +18,29 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noAuth().postWithNoAuth(new HashMap() { - { - put("key", "value"); - } - }); + client.inlinedRequests() + .postWithObjectBodyandResponse(PostWithObjectBody.builder() + .string("string") + .integer(1) + .nestedObject(ObjectWithOptionalField.builder() + .string("string") + .integer(1) + .long_(1000000L) + .double_(1.1) + .bool(true) + .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) + .date("2023-01-15") + .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) + .base64("SGVsbG8gd29ybGQh".getBytes()) + .list(Optional.of(Arrays.asList("list", "list"))) + .set(new HashSet(Arrays.asList("set"))) + .map(new HashMap() { + { + put(1, "map"); + } + }) + .bigint(new BigInteger("1000000")) + .build()) + .build()); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example52.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example52.java index 625192661cb8..b2b407485c7f 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example52.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example52.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.HashMap; public class Example52 { public static void main(String[] args) { @@ -9,6 +10,10 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().getWithNoRequestBody(); + client.noAuth().postWithNoAuth(new HashMap() { + { + put("key", "value"); + } + }); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example53.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example53.java index e13868d2213a..1812912a36c9 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example53.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example53.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().postWithNoRequestBody(); + client.noReqBody().getWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example54.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example54.java index ecc8b4f8e035..7ae68a1dbb75 100644 --- a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example54.java +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example54.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; public class Example54 { public static void main(String[] args) { @@ -10,11 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.reqWithHeaders() - .getWithCustomHeader(ReqWithHeaders.builder() - .xTestServiceHeader("X-TEST-SERVICE-HEADER") - .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") - .body("string") - .build()); + client.noReqBody().postWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example55.java b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example55.java new file mode 100644 index 000000000000..5ba6aebfd9fa --- /dev/null +++ b/seed/java-sdk/exhaustive/forward-compatible-enums/src/main/java/com/snippets/Example55.java @@ -0,0 +1,20 @@ +package com.snippets; + +import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; + +public class Example55 { + public static void main(String[] args) { + SeedExhaustiveClient client = SeedExhaustiveClient.builder() + .token("") + .url("https://api.fern.com") + .build(); + + client.reqWithHeaders() + .getWithCustomHeader(ReqWithHeaders.builder() + .xTestServiceHeader("X-TEST-SERVICE-HEADER") + .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") + .body("string") + .build()); + } +} diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/reference.md b/seed/java-sdk/exhaustive/json-include-non-empty/reference.md index 650f282e620a..6c66e7b30c98 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/reference.md +++ b/seed/java-sdk/exhaustive/json-include-non-empty/reference.md @@ -1874,6 +1874,60 @@ client.endpoints().params().modifyWithPath("param", "string"); + + + + +
client.endpoints.params.uploadWithPath(param, request) -> ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```java +client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `String` + +
+
+
+
+ +
diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/snippet.json b/seed/java-sdk/exhaustive/json-include-non-empty/snippet.json index 99ce0c05814a..b2c2c1336e79 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/snippet.json +++ b/seed/java-sdk/exhaustive/json-include-non-empty/snippet.json @@ -429,6 +429,19 @@ "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().modifyWithPath(\"param\", \"string\");\n }\n}\n" } }, + { + "example_identifier": "b305eac1", + "id": { + "method": "POST", + "path": "/params/path/{param}", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "java", + "sync_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n", + "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n" + } + }, { "example_identifier": "d8492cb1", "id": { diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java index 5fe44031aa45..2f6f8508a579 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; public class AsyncParamsClient { @@ -147,4 +149,34 @@ public CompletableFuture modifyWithInlinePath( ModifyResourceAtInlinedPath request, RequestOptions requestOptions) { return this.rawClient.modifyWithInlinePath(request, requestOptions).thenApply(response -> response.body()); } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java index 03b9ad6bcd21..d099ea37bce1 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,12 +19,16 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -538,4 +543,83 @@ public void onFailure(@NotNull Call call, @NotNull IOException e) { }); return future; } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response)); + return; + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (IOException e) { + future.completeExceptionally( + new SeedExhaustiveException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new SeedExhaustiveException("Network error executing HTTP request", e)); + } + }); + return future; + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java index 1e1def81f07d..a3db6c5f6dec 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; public class ParamsClient { protected final ClientOptions clientOptions; @@ -140,4 +142,32 @@ public String modifyWithInlinePath(ModifyResourceAtInlinedPath request) { public String modifyWithInlinePath(ModifyResourceAtInlinedPath request, RequestOptions requestOptions) { return this.rawClient.modifyWithInlinePath(request, requestOptions).body(); } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java index 4be7bb4e5e17..435a1f4afb88 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,9 +19,13 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -417,4 +422,67 @@ public SeedExhaustiveHttpResponse modifyWithInlinePath( throw new SeedExhaustiveException("Network error executing HTTP request", e); } } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .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(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response); + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example33.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example33.java index 6156d87604dd..460f05ef2737 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example33.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example33.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnString("string"); + client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example34.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example34.java index 8b17b19dc71e..e46c7fcb990f 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example34.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example34.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnInt(1); + client.endpoints().primitive().getAndReturnString("string"); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example35.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example35.java index 5ffa8dada196..68a3b26c1a0a 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example35.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example35.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnLong(1000000L); + client.endpoints().primitive().getAndReturnInt(1); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example36.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example36.java index e516a87a8f69..22782f9ac5df 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example36.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example36.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDouble(1.1); + client.endpoints().primitive().getAndReturnLong(1000000L); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example37.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example37.java index 1fe9e261ba37..a934874d8af6 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example37.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example37.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBool(true); + client.endpoints().primitive().getAndReturnDouble(1.1); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example38.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example38.java index ba0362ff4543..d9c5b5323f35 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example38.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example38.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.time.OffsetDateTime; public class Example38 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); + client.endpoints().primitive().getAndReturnBool(true); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example39.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example39.java index 5efebb9981a2..43192321b29f 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example39.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example39.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.time.OffsetDateTime; public class Example39 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDate("2023-01-15"); + client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example40.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example40.java index 73de5bc5604d..0245016f6194 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example40.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example40.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.util.UUID; public class Example40 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); + client.endpoints().primitive().getAndReturnDate("2023-01-15"); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example41.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example41.java index 5d435e059a24..6df02f99de5c 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example41.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example41.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.UUID; public class Example41 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); + client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example42.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example42.java index dbeade9154f8..299978a13411 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example42.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example42.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example42 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().put().add(PutRequest.builder().id("id").build()); + client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example43.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example43.java index 3313bfd6e768..62b17cb0fa74 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example43.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example43.java @@ -1,8 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.types.union.types.Animal; -import com.seed.exhaustive.resources.types.union.types.Dog; +import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example43 { public static void main(String[] args) { @@ -11,9 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints() - .union() - .getAndReturnUnion( - Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); + client.endpoints().put().add(PutRequest.builder().id("id").build()); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example44.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example44.java index 5359cd42db44..db173b5a131c 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example44.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example44.java @@ -1,6 +1,8 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.types.union.types.Animal; +import com.seed.exhaustive.resources.types.union.types.Dog; public class Example44 { public static void main(String[] args) { @@ -9,6 +11,9 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withMixedCase(); + client.endpoints() + .union() + .getAndReturnUnion( + Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example45.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example45.java index b7c1fb916ad2..b2e73987416b 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example45.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example45.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().noEndingSlash(); + client.endpoints().urls().withMixedCase(); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example46.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example46.java index 4e43babb3f14..7143df1ebd75 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example46.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example46.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withEndingSlash(); + client.endpoints().urls().noEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example47.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example47.java index 9045034481ea..d830891a2b61 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example47.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example47.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withUnderscores(); + client.endpoints().urls().withEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example48.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example48.java index 40a0d12d7120..9431dc95cd09 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example48.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example48.java @@ -1,15 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; -import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; -import java.math.BigInteger; -import java.time.OffsetDateTime; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Optional; -import java.util.UUID; public class Example48 { public static void main(String[] args) { @@ -18,29 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.inlinedRequests() - .postWithObjectBodyandResponse(PostWithObjectBody.builder() - .string("string") - .integer(1) - .nestedObject(ObjectWithOptionalField.builder() - .string("string") - .integer(1) - .long_(1000000L) - .double_(1.1) - .bool(true) - .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) - .date("2023-01-15") - .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) - .base64("SGVsbG8gd29ybGQh".getBytes()) - .list(Optional.of(Arrays.asList("list", "list"))) - .set(new HashSet(Arrays.asList("set"))) - .map(new HashMap() { - { - put(1, "map"); - } - }) - .bigint(new BigInteger("1000000")) - .build()) - .build()); + client.endpoints().urls().withUnderscores(); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example50.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example50.java index 4f463fab0404..f8c4b4bfbf22 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example50.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example50.java @@ -1,7 +1,15 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.math.BigInteger; +import java.time.OffsetDateTime; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; +import java.util.UUID; public class Example50 { public static void main(String[] args) { @@ -10,10 +18,29 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noAuth().postWithNoAuth(new HashMap() { - { - put("key", "value"); - } - }); + client.inlinedRequests() + .postWithObjectBodyandResponse(PostWithObjectBody.builder() + .string("string") + .integer(1) + .nestedObject(ObjectWithOptionalField.builder() + .string("string") + .integer(1) + .long_(1000000L) + .double_(1.1) + .bool(true) + .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) + .date("2023-01-15") + .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) + .base64("SGVsbG8gd29ybGQh".getBytes()) + .list(Optional.of(Arrays.asList("list", "list"))) + .set(new HashSet(Arrays.asList("set"))) + .map(new HashMap() { + { + put(1, "map"); + } + }) + .bigint(new BigInteger("1000000")) + .build()) + .build()); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example52.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example52.java index 625192661cb8..b2b407485c7f 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example52.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example52.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.HashMap; public class Example52 { public static void main(String[] args) { @@ -9,6 +10,10 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().getWithNoRequestBody(); + client.noAuth().postWithNoAuth(new HashMap() { + { + put("key", "value"); + } + }); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example53.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example53.java index e13868d2213a..1812912a36c9 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example53.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example53.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().postWithNoRequestBody(); + client.noReqBody().getWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example54.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example54.java index ecc8b4f8e035..7ae68a1dbb75 100644 --- a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example54.java +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example54.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; public class Example54 { public static void main(String[] args) { @@ -10,11 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.reqWithHeaders() - .getWithCustomHeader(ReqWithHeaders.builder() - .xTestServiceHeader("X-TEST-SERVICE-HEADER") - .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") - .body("string") - .build()); + client.noReqBody().postWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example55.java b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example55.java new file mode 100644 index 000000000000..5ba6aebfd9fa --- /dev/null +++ b/seed/java-sdk/exhaustive/json-include-non-empty/src/main/java/com/snippets/Example55.java @@ -0,0 +1,20 @@ +package com.snippets; + +import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; + +public class Example55 { + public static void main(String[] args) { + SeedExhaustiveClient client = SeedExhaustiveClient.builder() + .token("") + .url("https://api.fern.com") + .build(); + + client.reqWithHeaders() + .getWithCustomHeader(ReqWithHeaders.builder() + .xTestServiceHeader("X-TEST-SERVICE-HEADER") + .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") + .body("string") + .build()); + } +} diff --git a/seed/java-sdk/exhaustive/local-files/reference.md b/seed/java-sdk/exhaustive/local-files/reference.md index 650f282e620a..6c66e7b30c98 100644 --- a/seed/java-sdk/exhaustive/local-files/reference.md +++ b/seed/java-sdk/exhaustive/local-files/reference.md @@ -1874,6 +1874,60 @@ client.endpoints().params().modifyWithPath("param", "string"); + + + + +
client.endpoints.params.uploadWithPath(param, request) -> ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```java +client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `String` + +
+
+
+
+ +
diff --git a/seed/java-sdk/exhaustive/local-files/snippet.json b/seed/java-sdk/exhaustive/local-files/snippet.json index 248c98238a9a..27da44f673f5 100644 --- a/seed/java-sdk/exhaustive/local-files/snippet.json +++ b/seed/java-sdk/exhaustive/local-files/snippet.json @@ -429,6 +429,19 @@ "async_client": "package com.example.usage;\n\nimport com.fern.sdk.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().modifyWithPath(\"param\", \"string\");\n }\n}\n" } }, + { + "example_identifier": "b305eac1", + "id": { + "method": "POST", + "path": "/params/path/{param}", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "java", + "sync_client": "package com.example.usage;\n\nimport com.fern.sdk.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n", + "async_client": "package com.example.usage;\n\nimport com.fern.sdk.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n" + } + }, { "example_identifier": "d8492cb1", "id": { diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example33.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example33.java index 96e33700cd1b..2ea2832467bb 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example33.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example33.java @@ -10,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnString("string"); + client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example34.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example34.java index 81e44757daa7..20a75af640bc 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example34.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example34.java @@ -10,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnInt(1); + client.endpoints().primitive().getAndReturnString("string"); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example35.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example35.java index af8a727ef414..746c1b2b9718 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example35.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example35.java @@ -10,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnLong(1000000L); + client.endpoints().primitive().getAndReturnInt(1); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example36.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example36.java index 6ff0d47e0a8a..b476f58b312a 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example36.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example36.java @@ -10,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDouble(1.1); + client.endpoints().primitive().getAndReturnLong(1000000L); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example37.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example37.java index ae918df239e0..d063da9b89a6 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example37.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example37.java @@ -10,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBool(true); + client.endpoints().primitive().getAndReturnDouble(1.1); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example38.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example38.java index b9dc364c16aa..02ce9fba3148 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example38.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example38.java @@ -1,7 +1,6 @@ package com.snippets; import com.fern.sdk.SeedExhaustiveClient; -import java.time.OffsetDateTime; public class Example38 { public static void main(String[] args) { @@ -11,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); + client.endpoints().primitive().getAndReturnBool(true); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example39.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example39.java index 654226cd1b42..0f58828d2923 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example39.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example39.java @@ -1,6 +1,7 @@ package com.snippets; import com.fern.sdk.SeedExhaustiveClient; +import java.time.OffsetDateTime; public class Example39 { public static void main(String[] args) { @@ -10,6 +11,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDate("2023-01-15"); + client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example40.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example40.java index 65e56addf753..11c8d318bf0d 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example40.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example40.java @@ -1,7 +1,6 @@ package com.snippets; import com.fern.sdk.SeedExhaustiveClient; -import java.util.UUID; public class Example40 { public static void main(String[] args) { @@ -11,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); + client.endpoints().primitive().getAndReturnDate("2023-01-15"); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example41.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example41.java index 20b7424f85c6..845dd9aa5aa6 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example41.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example41.java @@ -1,6 +1,7 @@ package com.snippets; import com.fern.sdk.SeedExhaustiveClient; +import java.util.UUID; public class Example41 { public static void main(String[] args) { @@ -10,6 +11,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); + client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example42.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example42.java index 25b76c37e0cb..29c0aa0954f2 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example42.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example42.java @@ -1,7 +1,6 @@ package com.snippets; import com.fern.sdk.SeedExhaustiveClient; -import com.fern.sdk.resources.endpoints.put.requests.PutRequest; public class Example42 { public static void main(String[] args) { @@ -11,11 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().put().add( - PutRequest - .builder() - .id("id") - .build() - ); + client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example43.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example43.java index 80f20b5c8054..27d4292f441b 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example43.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example43.java @@ -1,8 +1,7 @@ package com.snippets; import com.fern.sdk.SeedExhaustiveClient; -import com.fern.sdk.resources.types.union.types.Animal; -import com.fern.sdk.resources.types.union.types.Dog; +import com.fern.sdk.resources.endpoints.put.requests.PutRequest; public class Example43 { public static void main(String[] args) { @@ -12,14 +11,11 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().union().getAndReturnUnion( - Animal.dog( - Dog - .builder() - .name("name") - .likesToWoof(true) - .build() - ) + client.endpoints().put().add( + PutRequest + .builder() + .id("id") + .build() ); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example44.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example44.java index 874f4a59e325..5db1578b5770 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example44.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example44.java @@ -1,6 +1,8 @@ package com.snippets; import com.fern.sdk.SeedExhaustiveClient; +import com.fern.sdk.resources.types.union.types.Animal; +import com.fern.sdk.resources.types.union.types.Dog; public class Example44 { public static void main(String[] args) { @@ -10,6 +12,14 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withMixedCase(); + client.endpoints().union().getAndReturnUnion( + Animal.dog( + Dog + .builder() + .name("name") + .likesToWoof(true) + .build() + ) + ); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example45.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example45.java index 58db3c693cc7..e23484169eb6 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example45.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example45.java @@ -10,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().noEndingSlash(); + client.endpoints().urls().withMixedCase(); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example46.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example46.java index fed0762148a2..59c1be7d0270 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example46.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example46.java @@ -10,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withEndingSlash(); + client.endpoints().urls().noEndingSlash(); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example47.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example47.java index 3ca4f9da22d6..ba87b9d9769b 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example47.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example47.java @@ -10,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withUnderscores(); + client.endpoints().urls().withEndingSlash(); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example48.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example48.java index 120ab1d8ad8f..1f9548afdce7 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example48.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example48.java @@ -1,15 +1,6 @@ package com.snippets; import com.fern.sdk.SeedExhaustiveClient; -import com.fern.sdk.resources.inlinedrequests.requests.PostWithObjectBody; -import com.fern.sdk.resources.types.object.types.ObjectWithOptionalField; -import java.math.BigInteger; -import java.time.OffsetDateTime; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Optional; -import java.util.UUID; public class Example48 { public static void main(String[] args) { @@ -19,42 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.inlinedRequests().postWithObjectBodyandResponse( - PostWithObjectBody - .builder() - .string("string") - .integer(1) - .nestedObject( - ObjectWithOptionalField - .builder() - .string("string") - .integer(1) - .long_(1000000L) - .double_(1.1) - .bool(true) - .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) - .date("2023-01-15") - .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) - .base64("SGVsbG8gd29ybGQh".getBytes()) - .list( - Optional.of( - Arrays.asList("list", "list") - ) - ) - .set( - new HashSet( - Arrays.asList("set") - ) - ) - .map( - new HashMap() {{ - put(1, "map"); - }} - ) - .bigint(new BigInteger("1000000")) - .build() - ) - .build() - ); + client.endpoints().urls().withUnderscores(); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example50.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example50.java index 9d5341001112..ed4067fc9945 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example50.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example50.java @@ -1,7 +1,15 @@ package com.snippets; import com.fern.sdk.SeedExhaustiveClient; +import com.fern.sdk.resources.inlinedrequests.requests.PostWithObjectBody; +import com.fern.sdk.resources.types.object.types.ObjectWithOptionalField; +import java.math.BigInteger; +import java.time.OffsetDateTime; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; +import java.util.UUID; public class Example50 { public static void main(String[] args) { @@ -11,8 +19,42 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noAuth().postWithNoAuth(new - HashMap() {{put("key", "value"); - }}); + client.inlinedRequests().postWithObjectBodyandResponse( + PostWithObjectBody + .builder() + .string("string") + .integer(1) + .nestedObject( + ObjectWithOptionalField + .builder() + .string("string") + .integer(1) + .long_(1000000L) + .double_(1.1) + .bool(true) + .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) + .date("2023-01-15") + .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) + .base64("SGVsbG8gd29ybGQh".getBytes()) + .list( + Optional.of( + Arrays.asList("list", "list") + ) + ) + .set( + new HashSet( + Arrays.asList("set") + ) + ) + .map( + new HashMap() {{ + put(1, "map"); + }} + ) + .bigint(new BigInteger("1000000")) + .build() + ) + .build() + ); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example52.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example52.java index eb89381b5281..16170a0450e9 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example52.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example52.java @@ -1,6 +1,7 @@ package com.snippets; import com.fern.sdk.SeedExhaustiveClient; +import java.util.HashMap; public class Example52 { public static void main(String[] args) { @@ -10,6 +11,8 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().getWithNoRequestBody(); + client.noAuth().postWithNoAuth(new + HashMap() {{put("key", "value"); + }}); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example53.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example53.java index 5802e9f6cdef..c5d73fc3148e 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example53.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example53.java @@ -10,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().postWithNoRequestBody(); + client.noReqBody().getWithNoRequestBody(); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example54.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example54.java index afb1f26d0eb4..ff6966ebe467 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example54.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example54.java @@ -1,7 +1,6 @@ package com.snippets; import com.fern.sdk.SeedExhaustiveClient; -import com.fern.sdk.resources.reqwithheaders.requests.ReqWithHeaders; public class Example54 { public static void main(String[] args) { @@ -11,13 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.reqWithHeaders().getWithCustomHeader( - ReqWithHeaders - .builder() - .xTestServiceHeader("X-TEST-SERVICE-HEADER") - .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") - .body("string") - .build() - ); + client.noReqBody().postWithNoRequestBody(); } } \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example55.java b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example55.java new file mode 100644 index 000000000000..d25a8c7f7ef6 --- /dev/null +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/com/snippets/Example55.java @@ -0,0 +1,23 @@ +package com.snippets; + +import com.fern.sdk.SeedExhaustiveClient; +import com.fern.sdk.resources.reqwithheaders.requests.ReqWithHeaders; + +public class Example55 { + public static void main(String[] args) { + SeedExhaustiveClient client = SeedExhaustiveClient + .builder() + .token("") + .url("https://api.fern.com") + .build(); + + client.reqWithHeaders().getWithCustomHeader( + ReqWithHeaders + .builder() + .xTestServiceHeader("X-TEST-SERVICE-HEADER") + .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") + .body("string") + .build() + ); + } +} \ No newline at end of file diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/resources/endpoints/params/AsyncParamsClient.java b/seed/java-sdk/exhaustive/local-files/src/main/java/resources/endpoints/params/AsyncParamsClient.java index 2e20afd398e4..010199a54d92 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/resources/endpoints/params/AsyncParamsClient.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/resources/endpoints/params/AsyncParamsClient.java @@ -12,6 +12,8 @@ import com.fern.sdk.resources.endpoints.params.requests.GetWithPathAndQuery; import com.fern.sdk.resources.endpoints.params.requests.GetWithQuery; import com.fern.sdk.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.fern.sdk.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; import java.lang.String; import java.lang.Void; import java.util.concurrent.CompletableFuture; @@ -166,4 +168,35 @@ public CompletableFuture modifyWithInlinePath(String param, ModifyResourceAtInlinedPath request, RequestOptions requestOptions) { return this.rawClient.modifyWithInlinePath(param, request, requestOptions).thenApply(response -> response.body()); } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, + InputStream request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, + InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, byte[] request, + RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } } diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/resources/endpoints/params/AsyncRawParamsClient.java b/seed/java-sdk/exhaustive/local-files/src/main/java/resources/endpoints/params/AsyncRawParamsClient.java index 4a68d737d717..5a3cdad1f469 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/resources/endpoints/params/AsyncRawParamsClient.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/resources/endpoints/params/AsyncRawParamsClient.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fern.sdk.core.ClientOptions; +import com.fern.sdk.core.InputStreamRequestBody; import com.fern.sdk.core.MediaTypes; import com.fern.sdk.core.ObjectMappers; import com.fern.sdk.core.QueryStringMapper; @@ -19,7 +20,10 @@ import com.fern.sdk.resources.endpoints.params.requests.GetWithPathAndQuery; import com.fern.sdk.resources.endpoints.params.requests.GetWithQuery; import com.fern.sdk.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.fern.sdk.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.lang.Object; import java.lang.Override; import java.lang.String; @@ -29,6 +33,7 @@ import okhttp3.Callback; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -537,4 +542,78 @@ public void onFailure(@NotNull Call call, @NotNull IOException e) { }); return future; } - } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request) { + return uploadWithPath(param,request,null); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()).newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param);if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + } ); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new SeedExhaustiveHttpResponse<>(ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), response)); + return; + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new SeedExhaustiveApiException("Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } + catch (IOException e) { + future.completeExceptionally(new SeedExhaustiveException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new SeedExhaustiveException("Network error executing HTTP request", e)); + } + }); + return future; + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } + } diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/resources/endpoints/params/ParamsClient.java b/seed/java-sdk/exhaustive/local-files/src/main/java/resources/endpoints/params/ParamsClient.java index 1a6b60a04802..8df285afb9b2 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/resources/endpoints/params/ParamsClient.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/resources/endpoints/params/ParamsClient.java @@ -12,6 +12,8 @@ import com.fern.sdk.resources.endpoints.params.requests.GetWithPathAndQuery; import com.fern.sdk.resources.endpoints.params.requests.GetWithQuery; import com.fern.sdk.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.fern.sdk.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; import java.lang.String; public class ParamsClient { @@ -161,4 +163,34 @@ public String modifyWithInlinePath(String param, ModifyResourceAtInlinedPath req RequestOptions requestOptions) { return this.rawClient.modifyWithInlinePath(param, request, requestOptions).body(); } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request, + RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request, + RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } } diff --git a/seed/java-sdk/exhaustive/local-files/src/main/java/resources/endpoints/params/RawParamsClient.java b/seed/java-sdk/exhaustive/local-files/src/main/java/resources/endpoints/params/RawParamsClient.java index 15e6981626c3..a2335079b78b 100644 --- a/seed/java-sdk/exhaustive/local-files/src/main/java/resources/endpoints/params/RawParamsClient.java +++ b/seed/java-sdk/exhaustive/local-files/src/main/java/resources/endpoints/params/RawParamsClient.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fern.sdk.core.ClientOptions; +import com.fern.sdk.core.InputStreamRequestBody; import com.fern.sdk.core.MediaTypes; import com.fern.sdk.core.ObjectMappers; import com.fern.sdk.core.QueryStringMapper; @@ -19,12 +20,16 @@ import com.fern.sdk.resources.endpoints.params.requests.GetWithPathAndQuery; import com.fern.sdk.resources.endpoints.params.requests.GetWithQuery; import com.fern.sdk.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.fern.sdk.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.lang.Object; import java.lang.String; import java.lang.Void; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -426,4 +431,65 @@ public SeedExhaustiveHttpResponse modifyWithInlinePath(String param, throw new SeedExhaustiveException("Network error executing HTTP request", e); } } - } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, InputStream request) { + return uploadWithPath(param,request,null); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()).newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param);if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + } ); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .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(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new SeedExhaustiveHttpResponse<>(ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), response); + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new SeedExhaustiveApiException("Error with status code " + response.code(), response.code(), errorBody, response); + } + catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } + } diff --git a/seed/java-sdk/exhaustive/no-custom-config/reference.md b/seed/java-sdk/exhaustive/no-custom-config/reference.md index 03ec177642e4..86c185534f55 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/reference.md +++ b/seed/java-sdk/exhaustive/no-custom-config/reference.md @@ -1874,6 +1874,60 @@ client.endpoints().params().modifyWithPath("param", "string"); + + + + +
client.endpoints.params.uploadWithPath(param, request) -> ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```java +client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `String` + +
+
+
+
+ +
diff --git a/seed/java-sdk/exhaustive/no-custom-config/snippet.json b/seed/java-sdk/exhaustive/no-custom-config/snippet.json index 7bf83465f22f..5fd1aab15320 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/snippet.json +++ b/seed/java-sdk/exhaustive/no-custom-config/snippet.json @@ -429,6 +429,19 @@ "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().modifyWithPath(\"param\", \"string\");\n }\n}\n" } }, + { + "example_identifier": "b305eac1", + "id": { + "method": "POST", + "path": "/params/path/{param}", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "java", + "sync_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n", + "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n" + } + }, { "example_identifier": "d8492cb1", "id": { diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java index c55a5f96ff6d..6abe0f280ee8 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; public class AsyncParamsClient { @@ -166,4 +168,34 @@ public CompletableFuture modifyWithInlinePath( .modifyWithInlinePath(param, request, requestOptions) .thenApply(response -> response.body()); } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java index 6cd660dae734..50e287aa36aa 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,12 +19,16 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -554,4 +559,83 @@ public void onFailure(@NotNull Call call, @NotNull IOException e) { }); return future; } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response)); + return; + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (IOException e) { + future.completeExceptionally( + new SeedExhaustiveException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new SeedExhaustiveException("Network error executing HTTP request", e)); + } + }); + return future; + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java index f2d48e1da54b..4ad0329fcb7e 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; public class ParamsClient { protected final ClientOptions clientOptions; @@ -158,4 +160,32 @@ public String modifyWithInlinePath( .modifyWithInlinePath(param, request, requestOptions) .body(); } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java index b9cd2d6f43a3..fb3c57f3a19d 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,9 +19,13 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -431,4 +436,67 @@ public SeedExhaustiveHttpResponse modifyWithInlinePath( throw new SeedExhaustiveException("Network error executing HTTP request", e); } } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .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(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response); + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example33.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example33.java index 6156d87604dd..460f05ef2737 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example33.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example33.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnString("string"); + client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example34.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example34.java index 8b17b19dc71e..e46c7fcb990f 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example34.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example34.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnInt(1); + client.endpoints().primitive().getAndReturnString("string"); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example35.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example35.java index 5ffa8dada196..68a3b26c1a0a 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example35.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example35.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnLong(1000000L); + client.endpoints().primitive().getAndReturnInt(1); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example36.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example36.java index e516a87a8f69..22782f9ac5df 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example36.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example36.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDouble(1.1); + client.endpoints().primitive().getAndReturnLong(1000000L); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example37.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example37.java index 1fe9e261ba37..a934874d8af6 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example37.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example37.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBool(true); + client.endpoints().primitive().getAndReturnDouble(1.1); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example38.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example38.java index ba0362ff4543..d9c5b5323f35 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example38.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example38.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.time.OffsetDateTime; public class Example38 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); + client.endpoints().primitive().getAndReturnBool(true); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example39.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example39.java index 5efebb9981a2..43192321b29f 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example39.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example39.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.time.OffsetDateTime; public class Example39 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDate("2023-01-15"); + client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example40.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example40.java index 73de5bc5604d..0245016f6194 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example40.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example40.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.util.UUID; public class Example40 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); + client.endpoints().primitive().getAndReturnDate("2023-01-15"); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example41.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example41.java index 5d435e059a24..6df02f99de5c 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example41.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example41.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.UUID; public class Example41 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); + client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example42.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example42.java index 840b06e3388c..299978a13411 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example42.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example42.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example42 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().put().add("id", PutRequest.builder().build()); + client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example43.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example43.java index 3313bfd6e768..3251ccac3d0f 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example43.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example43.java @@ -1,8 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.types.union.types.Animal; -import com.seed.exhaustive.resources.types.union.types.Dog; +import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example43 { public static void main(String[] args) { @@ -11,9 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints() - .union() - .getAndReturnUnion( - Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); + client.endpoints().put().add("id", PutRequest.builder().build()); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example44.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example44.java index 5359cd42db44..db173b5a131c 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example44.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example44.java @@ -1,6 +1,8 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.types.union.types.Animal; +import com.seed.exhaustive.resources.types.union.types.Dog; public class Example44 { public static void main(String[] args) { @@ -9,6 +11,9 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withMixedCase(); + client.endpoints() + .union() + .getAndReturnUnion( + Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example45.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example45.java index b7c1fb916ad2..b2e73987416b 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example45.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example45.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().noEndingSlash(); + client.endpoints().urls().withMixedCase(); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example46.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example46.java index 4e43babb3f14..7143df1ebd75 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example46.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example46.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withEndingSlash(); + client.endpoints().urls().noEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example47.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example47.java index 9045034481ea..d830891a2b61 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example47.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example47.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withUnderscores(); + client.endpoints().urls().withEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example48.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example48.java index 40a0d12d7120..9431dc95cd09 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example48.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example48.java @@ -1,15 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; -import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; -import java.math.BigInteger; -import java.time.OffsetDateTime; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Optional; -import java.util.UUID; public class Example48 { public static void main(String[] args) { @@ -18,29 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.inlinedRequests() - .postWithObjectBodyandResponse(PostWithObjectBody.builder() - .string("string") - .integer(1) - .nestedObject(ObjectWithOptionalField.builder() - .string("string") - .integer(1) - .long_(1000000L) - .double_(1.1) - .bool(true) - .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) - .date("2023-01-15") - .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) - .base64("SGVsbG8gd29ybGQh".getBytes()) - .list(Optional.of(Arrays.asList("list", "list"))) - .set(new HashSet(Arrays.asList("set"))) - .map(new HashMap() { - { - put(1, "map"); - } - }) - .bigint(new BigInteger("1000000")) - .build()) - .build()); + client.endpoints().urls().withUnderscores(); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example50.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example50.java index 4f463fab0404..f8c4b4bfbf22 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example50.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example50.java @@ -1,7 +1,15 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.math.BigInteger; +import java.time.OffsetDateTime; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; +import java.util.UUID; public class Example50 { public static void main(String[] args) { @@ -10,10 +18,29 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noAuth().postWithNoAuth(new HashMap() { - { - put("key", "value"); - } - }); + client.inlinedRequests() + .postWithObjectBodyandResponse(PostWithObjectBody.builder() + .string("string") + .integer(1) + .nestedObject(ObjectWithOptionalField.builder() + .string("string") + .integer(1) + .long_(1000000L) + .double_(1.1) + .bool(true) + .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) + .date("2023-01-15") + .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) + .base64("SGVsbG8gd29ybGQh".getBytes()) + .list(Optional.of(Arrays.asList("list", "list"))) + .set(new HashSet(Arrays.asList("set"))) + .map(new HashMap() { + { + put(1, "map"); + } + }) + .bigint(new BigInteger("1000000")) + .build()) + .build()); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example52.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example52.java index 625192661cb8..b2b407485c7f 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example52.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example52.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.HashMap; public class Example52 { public static void main(String[] args) { @@ -9,6 +10,10 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().getWithNoRequestBody(); + client.noAuth().postWithNoAuth(new HashMap() { + { + put("key", "value"); + } + }); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example53.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example53.java index e13868d2213a..1812912a36c9 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example53.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example53.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().postWithNoRequestBody(); + client.noReqBody().getWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example54.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example54.java index ecc8b4f8e035..7ae68a1dbb75 100644 --- a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example54.java +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example54.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; public class Example54 { public static void main(String[] args) { @@ -10,11 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.reqWithHeaders() - .getWithCustomHeader(ReqWithHeaders.builder() - .xTestServiceHeader("X-TEST-SERVICE-HEADER") - .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") - .body("string") - .build()); + client.noReqBody().postWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example55.java b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example55.java new file mode 100644 index 000000000000..5ba6aebfd9fa --- /dev/null +++ b/seed/java-sdk/exhaustive/no-custom-config/src/main/java/com/snippets/Example55.java @@ -0,0 +1,20 @@ +package com.snippets; + +import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; + +public class Example55 { + public static void main(String[] args) { + SeedExhaustiveClient client = SeedExhaustiveClient.builder() + .token("") + .url("https://api.fern.com") + .build(); + + client.reqWithHeaders() + .getWithCustomHeader(ReqWithHeaders.builder() + .xTestServiceHeader("X-TEST-SERVICE-HEADER") + .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") + .body("string") + .build()); + } +} diff --git a/seed/java-sdk/exhaustive/publish-to/reference.md b/seed/java-sdk/exhaustive/publish-to/reference.md index 03ec177642e4..86c185534f55 100644 --- a/seed/java-sdk/exhaustive/publish-to/reference.md +++ b/seed/java-sdk/exhaustive/publish-to/reference.md @@ -1874,6 +1874,60 @@ client.endpoints().params().modifyWithPath("param", "string"); + + + + +
client.endpoints.params.uploadWithPath(param, request) -> ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```java +client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `String` + +
+
+
+
+ +
diff --git a/seed/java-sdk/exhaustive/publish-to/snippet.json b/seed/java-sdk/exhaustive/publish-to/snippet.json index 7bf83465f22f..5fd1aab15320 100644 --- a/seed/java-sdk/exhaustive/publish-to/snippet.json +++ b/seed/java-sdk/exhaustive/publish-to/snippet.json @@ -429,6 +429,19 @@ "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().modifyWithPath(\"param\", \"string\");\n }\n}\n" } }, + { + "example_identifier": "b305eac1", + "id": { + "method": "POST", + "path": "/params/path/{param}", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "java", + "sync_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n", + "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n" + } + }, { "example_identifier": "d8492cb1", "id": { diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java index c55a5f96ff6d..6abe0f280ee8 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; public class AsyncParamsClient { @@ -166,4 +168,34 @@ public CompletableFuture modifyWithInlinePath( .modifyWithInlinePath(param, request, requestOptions) .thenApply(response -> response.body()); } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java index 6cd660dae734..50e287aa36aa 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,12 +19,16 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -554,4 +559,83 @@ public void onFailure(@NotNull Call call, @NotNull IOException e) { }); return future; } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response)); + return; + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (IOException e) { + future.completeExceptionally( + new SeedExhaustiveException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new SeedExhaustiveException("Network error executing HTTP request", e)); + } + }); + return future; + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java index f2d48e1da54b..4ad0329fcb7e 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; public class ParamsClient { protected final ClientOptions clientOptions; @@ -158,4 +160,32 @@ public String modifyWithInlinePath( .modifyWithInlinePath(param, request, requestOptions) .body(); } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java index b9cd2d6f43a3..fb3c57f3a19d 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,9 +19,13 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -431,4 +436,67 @@ public SeedExhaustiveHttpResponse modifyWithInlinePath( throw new SeedExhaustiveException("Network error executing HTTP request", e); } } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .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(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response); + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example33.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example33.java index 6156d87604dd..460f05ef2737 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example33.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example33.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnString("string"); + client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example34.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example34.java index 8b17b19dc71e..e46c7fcb990f 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example34.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example34.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnInt(1); + client.endpoints().primitive().getAndReturnString("string"); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example35.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example35.java index 5ffa8dada196..68a3b26c1a0a 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example35.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example35.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnLong(1000000L); + client.endpoints().primitive().getAndReturnInt(1); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example36.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example36.java index e516a87a8f69..22782f9ac5df 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example36.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example36.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDouble(1.1); + client.endpoints().primitive().getAndReturnLong(1000000L); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example37.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example37.java index 1fe9e261ba37..a934874d8af6 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example37.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example37.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBool(true); + client.endpoints().primitive().getAndReturnDouble(1.1); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example38.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example38.java index ba0362ff4543..d9c5b5323f35 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example38.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example38.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.time.OffsetDateTime; public class Example38 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); + client.endpoints().primitive().getAndReturnBool(true); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example39.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example39.java index 5efebb9981a2..43192321b29f 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example39.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example39.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.time.OffsetDateTime; public class Example39 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDate("2023-01-15"); + client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example40.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example40.java index 73de5bc5604d..0245016f6194 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example40.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example40.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.util.UUID; public class Example40 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); + client.endpoints().primitive().getAndReturnDate("2023-01-15"); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example41.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example41.java index 5d435e059a24..6df02f99de5c 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example41.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example41.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.UUID; public class Example41 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); + client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example42.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example42.java index 840b06e3388c..299978a13411 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example42.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example42.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example42 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().put().add("id", PutRequest.builder().build()); + client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example43.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example43.java index 3313bfd6e768..3251ccac3d0f 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example43.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example43.java @@ -1,8 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.types.union.types.Animal; -import com.seed.exhaustive.resources.types.union.types.Dog; +import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example43 { public static void main(String[] args) { @@ -11,9 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints() - .union() - .getAndReturnUnion( - Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); + client.endpoints().put().add("id", PutRequest.builder().build()); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example44.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example44.java index 5359cd42db44..db173b5a131c 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example44.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example44.java @@ -1,6 +1,8 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.types.union.types.Animal; +import com.seed.exhaustive.resources.types.union.types.Dog; public class Example44 { public static void main(String[] args) { @@ -9,6 +11,9 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withMixedCase(); + client.endpoints() + .union() + .getAndReturnUnion( + Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example45.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example45.java index b7c1fb916ad2..b2e73987416b 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example45.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example45.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().noEndingSlash(); + client.endpoints().urls().withMixedCase(); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example46.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example46.java index 4e43babb3f14..7143df1ebd75 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example46.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example46.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withEndingSlash(); + client.endpoints().urls().noEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example47.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example47.java index 9045034481ea..d830891a2b61 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example47.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example47.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withUnderscores(); + client.endpoints().urls().withEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example48.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example48.java index 40a0d12d7120..9431dc95cd09 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example48.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example48.java @@ -1,15 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; -import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; -import java.math.BigInteger; -import java.time.OffsetDateTime; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Optional; -import java.util.UUID; public class Example48 { public static void main(String[] args) { @@ -18,29 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.inlinedRequests() - .postWithObjectBodyandResponse(PostWithObjectBody.builder() - .string("string") - .integer(1) - .nestedObject(ObjectWithOptionalField.builder() - .string("string") - .integer(1) - .long_(1000000L) - .double_(1.1) - .bool(true) - .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) - .date("2023-01-15") - .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) - .base64("SGVsbG8gd29ybGQh".getBytes()) - .list(Optional.of(Arrays.asList("list", "list"))) - .set(new HashSet(Arrays.asList("set"))) - .map(new HashMap() { - { - put(1, "map"); - } - }) - .bigint(new BigInteger("1000000")) - .build()) - .build()); + client.endpoints().urls().withUnderscores(); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example50.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example50.java index 4f463fab0404..f8c4b4bfbf22 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example50.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example50.java @@ -1,7 +1,15 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.math.BigInteger; +import java.time.OffsetDateTime; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; +import java.util.UUID; public class Example50 { public static void main(String[] args) { @@ -10,10 +18,29 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noAuth().postWithNoAuth(new HashMap() { - { - put("key", "value"); - } - }); + client.inlinedRequests() + .postWithObjectBodyandResponse(PostWithObjectBody.builder() + .string("string") + .integer(1) + .nestedObject(ObjectWithOptionalField.builder() + .string("string") + .integer(1) + .long_(1000000L) + .double_(1.1) + .bool(true) + .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) + .date("2023-01-15") + .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) + .base64("SGVsbG8gd29ybGQh".getBytes()) + .list(Optional.of(Arrays.asList("list", "list"))) + .set(new HashSet(Arrays.asList("set"))) + .map(new HashMap() { + { + put(1, "map"); + } + }) + .bigint(new BigInteger("1000000")) + .build()) + .build()); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example52.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example52.java index 625192661cb8..b2b407485c7f 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example52.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example52.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.HashMap; public class Example52 { public static void main(String[] args) { @@ -9,6 +10,10 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().getWithNoRequestBody(); + client.noAuth().postWithNoAuth(new HashMap() { + { + put("key", "value"); + } + }); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example53.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example53.java index e13868d2213a..1812912a36c9 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example53.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example53.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().postWithNoRequestBody(); + client.noReqBody().getWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example54.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example54.java index ecc8b4f8e035..7ae68a1dbb75 100644 --- a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example54.java +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example54.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; public class Example54 { public static void main(String[] args) { @@ -10,11 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.reqWithHeaders() - .getWithCustomHeader(ReqWithHeaders.builder() - .xTestServiceHeader("X-TEST-SERVICE-HEADER") - .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") - .body("string") - .build()); + client.noReqBody().postWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example55.java b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example55.java new file mode 100644 index 000000000000..5ba6aebfd9fa --- /dev/null +++ b/seed/java-sdk/exhaustive/publish-to/src/main/java/com/snippets/Example55.java @@ -0,0 +1,20 @@ +package com.snippets; + +import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; + +public class Example55 { + public static void main(String[] args) { + SeedExhaustiveClient client = SeedExhaustiveClient.builder() + .token("") + .url("https://api.fern.com") + .build(); + + client.reqWithHeaders() + .getWithCustomHeader(ReqWithHeaders.builder() + .xTestServiceHeader("X-TEST-SERVICE-HEADER") + .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") + .body("string") + .build()); + } +} diff --git a/seed/java-sdk/exhaustive/signed_publish/reference.md b/seed/java-sdk/exhaustive/signed_publish/reference.md index 03ec177642e4..86c185534f55 100644 --- a/seed/java-sdk/exhaustive/signed_publish/reference.md +++ b/seed/java-sdk/exhaustive/signed_publish/reference.md @@ -1874,6 +1874,60 @@ client.endpoints().params().modifyWithPath("param", "string"); + + + + +
client.endpoints.params.uploadWithPath(param, request) -> ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```java +client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `String` + +
+
+
+
+ +
diff --git a/seed/java-sdk/exhaustive/signed_publish/snippet.json b/seed/java-sdk/exhaustive/signed_publish/snippet.json index 7bf83465f22f..5fd1aab15320 100644 --- a/seed/java-sdk/exhaustive/signed_publish/snippet.json +++ b/seed/java-sdk/exhaustive/signed_publish/snippet.json @@ -429,6 +429,19 @@ "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().modifyWithPath(\"param\", \"string\");\n }\n}\n" } }, + { + "example_identifier": "b305eac1", + "id": { + "method": "POST", + "path": "/params/path/{param}", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "java", + "sync_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n", + "async_client": "package com.example.usage;\n\nimport com.seed.exhaustive.SeedExhaustiveClient;\n\npublic class Example {\n public static void main(String[] args) {\n SeedExhaustiveClient client = SeedExhaustiveClient\n .builder()\n .token(\"\")\n .build();\n\n client.endpoints().params().uploadWithPath(\"upload-path\", \"\".getBytes());\n }\n}\n" + } + }, { "example_identifier": "d8492cb1", "id": { diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java index c55a5f96ff6d..6abe0f280ee8 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; public class AsyncParamsClient { @@ -166,4 +168,34 @@ public CompletableFuture modifyWithInlinePath( .modifyWithInlinePath(param, request, requestOptions) .thenApply(response -> response.body()); } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).thenApply(response -> response.body()); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).thenApply(response -> response.body()); + } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java index 6cd660dae734..50e287aa36aa 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/seed/exhaustive/resources/endpoints/params/AsyncRawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,12 +19,16 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.concurrent.CompletableFuture; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -554,4 +559,83 @@ public void onFailure(@NotNull Call call, @NotNull IOException e) { }); return future; } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try (ResponseBody responseBody = response.body()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response)); + return; + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (IOException e) { + future.completeExceptionally( + new SeedExhaustiveException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new SeedExhaustiveException("Network error executing HTTP request", e)); + } + }); + return future; + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public CompletableFuture> uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java index f2d48e1da54b..4ad0329fcb7e 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/seed/exhaustive/resources/endpoints/params/ParamsClient.java @@ -11,6 +11,8 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.InputStream; public class ParamsClient { protected final ClientOptions clientOptions; @@ -158,4 +160,32 @@ public String modifyWithInlinePath( .modifyWithInlinePath(param, request, requestOptions) .body(); } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, InputStream request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request) { + return this.rawClient.uploadWithPath(param, request).body(); + } + + /** + * POST bytes with path param returning object + */ + public ObjectWithRequiredField uploadWithPath(String param, byte[] request, RequestOptions requestOptions) { + return this.rawClient.uploadWithPath(param, request, requestOptions).body(); + } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java index b9cd2d6f43a3..fb3c57f3a19d 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/seed/exhaustive/resources/endpoints/params/RawParamsClient.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.seed.exhaustive.core.ClientOptions; +import com.seed.exhaustive.core.InputStreamRequestBody; import com.seed.exhaustive.core.MediaTypes; import com.seed.exhaustive.core.ObjectMappers; import com.seed.exhaustive.core.QueryStringMapper; @@ -18,9 +19,13 @@ import com.seed.exhaustive.resources.endpoints.params.requests.GetWithPathAndQuery; import com.seed.exhaustive.resources.endpoints.params.requests.GetWithQuery; import com.seed.exhaustive.resources.endpoints.params.requests.ModifyResourceAtInlinedPath; +import com.seed.exhaustive.resources.types.object.types.ObjectWithRequiredField; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import okhttp3.Headers; import okhttp3.HttpUrl; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; @@ -431,4 +436,67 @@ public SeedExhaustiveHttpResponse modifyWithInlinePath( throw new SeedExhaustiveException("Network error executing HTTP request", e); } } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, InputStream request) { + return uploadWithPath(param, request, null); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, InputStream request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("params") + .addPathSegments("path") + .addPathSegment(param); + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody(MediaType.parse("application/octet-stream"), request); + Request okhttpRequest = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .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(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new SeedExhaustiveHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, ObjectWithRequiredField.class), + response); + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new SeedExhaustiveApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (IOException e) { + throw new SeedExhaustiveException("Network error executing HTTP request", e); + } + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath(String param, byte[] request) { + return uploadWithPath(param, new ByteArrayInputStream(request)); + } + + /** + * POST bytes with path param returning object + */ + public SeedExhaustiveHttpResponse uploadWithPath( + String param, byte[] request, RequestOptions requestOptions) { + return uploadWithPath(param, new ByteArrayInputStream(request), requestOptions); + } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example33.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example33.java index 6156d87604dd..460f05ef2737 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example33.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example33.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnString("string"); + client.endpoints().params().uploadWithPath("upload-path", "".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example34.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example34.java index 8b17b19dc71e..e46c7fcb990f 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example34.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example34.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnInt(1); + client.endpoints().primitive().getAndReturnString("string"); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example35.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example35.java index 5ffa8dada196..68a3b26c1a0a 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example35.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example35.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnLong(1000000L); + client.endpoints().primitive().getAndReturnInt(1); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example36.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example36.java index e516a87a8f69..22782f9ac5df 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example36.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example36.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDouble(1.1); + client.endpoints().primitive().getAndReturnLong(1000000L); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example37.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example37.java index 1fe9e261ba37..a934874d8af6 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example37.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example37.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBool(true); + client.endpoints().primitive().getAndReturnDouble(1.1); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example38.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example38.java index ba0362ff4543..d9c5b5323f35 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example38.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example38.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.time.OffsetDateTime; public class Example38 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); + client.endpoints().primitive().getAndReturnBool(true); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example39.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example39.java index 5efebb9981a2..43192321b29f 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example39.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example39.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.time.OffsetDateTime; public class Example39 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnDate("2023-01-15"); + client.endpoints().primitive().getAndReturnDatetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example40.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example40.java index 73de5bc5604d..0245016f6194 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example40.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example40.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import java.util.UUID; public class Example40 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); + client.endpoints().primitive().getAndReturnDate("2023-01-15"); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example41.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example41.java index 5d435e059a24..6df02f99de5c 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example41.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example41.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.UUID; public class Example41 { public static void main(String[] args) { @@ -9,6 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); + client.endpoints().primitive().getAndReturnUuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example42.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example42.java index 840b06e3388c..299978a13411 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example42.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example42.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example42 { public static void main(String[] args) { @@ -10,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().put().add("id", PutRequest.builder().build()); + client.endpoints().primitive().getAndReturnBase64("SGVsbG8gd29ybGQh".getBytes()); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example43.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example43.java index 3313bfd6e768..3251ccac3d0f 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example43.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example43.java @@ -1,8 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.types.union.types.Animal; -import com.seed.exhaustive.resources.types.union.types.Dog; +import com.seed.exhaustive.resources.endpoints.put.requests.PutRequest; public class Example43 { public static void main(String[] args) { @@ -11,9 +10,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints() - .union() - .getAndReturnUnion( - Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); + client.endpoints().put().add("id", PutRequest.builder().build()); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example44.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example44.java index 5359cd42db44..db173b5a131c 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example44.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example44.java @@ -1,6 +1,8 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.types.union.types.Animal; +import com.seed.exhaustive.resources.types.union.types.Dog; public class Example44 { public static void main(String[] args) { @@ -9,6 +11,9 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withMixedCase(); + client.endpoints() + .union() + .getAndReturnUnion( + Animal.dog(Dog.builder().name("name").likesToWoof(true).build())); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example45.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example45.java index b7c1fb916ad2..b2e73987416b 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example45.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example45.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().noEndingSlash(); + client.endpoints().urls().withMixedCase(); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example46.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example46.java index 4e43babb3f14..7143df1ebd75 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example46.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example46.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withEndingSlash(); + client.endpoints().urls().noEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example47.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example47.java index 9045034481ea..d830891a2b61 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example47.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example47.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.endpoints().urls().withUnderscores(); + client.endpoints().urls().withEndingSlash(); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example48.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example48.java index 40a0d12d7120..9431dc95cd09 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example48.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example48.java @@ -1,15 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; -import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; -import java.math.BigInteger; -import java.time.OffsetDateTime; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Optional; -import java.util.UUID; public class Example48 { public static void main(String[] args) { @@ -18,29 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.inlinedRequests() - .postWithObjectBodyandResponse(PostWithObjectBody.builder() - .string("string") - .integer(1) - .nestedObject(ObjectWithOptionalField.builder() - .string("string") - .integer(1) - .long_(1000000L) - .double_(1.1) - .bool(true) - .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) - .date("2023-01-15") - .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) - .base64("SGVsbG8gd29ybGQh".getBytes()) - .list(Optional.of(Arrays.asList("list", "list"))) - .set(new HashSet(Arrays.asList("set"))) - .map(new HashMap() { - { - put(1, "map"); - } - }) - .bigint(new BigInteger("1000000")) - .build()) - .build()); + client.endpoints().urls().withUnderscores(); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example50.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example50.java index 4f463fab0404..f8c4b4bfbf22 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example50.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example50.java @@ -1,7 +1,15 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.inlinedrequests.requests.PostWithObjectBody; +import com.seed.exhaustive.resources.types.object.types.ObjectWithOptionalField; +import java.math.BigInteger; +import java.time.OffsetDateTime; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; +import java.util.UUID; public class Example50 { public static void main(String[] args) { @@ -10,10 +18,29 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noAuth().postWithNoAuth(new HashMap() { - { - put("key", "value"); - } - }); + client.inlinedRequests() + .postWithObjectBodyandResponse(PostWithObjectBody.builder() + .string("string") + .integer(1) + .nestedObject(ObjectWithOptionalField.builder() + .string("string") + .integer(1) + .long_(1000000L) + .double_(1.1) + .bool(true) + .datetime(OffsetDateTime.parse("2024-01-15T09:30:00Z")) + .date("2023-01-15") + .uuid(UUID.fromString("d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32")) + .base64("SGVsbG8gd29ybGQh".getBytes()) + .list(Optional.of(Arrays.asList("list", "list"))) + .set(new HashSet(Arrays.asList("set"))) + .map(new HashMap() { + { + put(1, "map"); + } + }) + .bigint(new BigInteger("1000000")) + .build()) + .build()); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example52.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example52.java index 625192661cb8..b2b407485c7f 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example52.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example52.java @@ -1,6 +1,7 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; +import java.util.HashMap; public class Example52 { public static void main(String[] args) { @@ -9,6 +10,10 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().getWithNoRequestBody(); + client.noAuth().postWithNoAuth(new HashMap() { + { + put("key", "value"); + } + }); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example53.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example53.java index e13868d2213a..1812912a36c9 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example53.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example53.java @@ -9,6 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.noReqBody().postWithNoRequestBody(); + client.noReqBody().getWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example54.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example54.java index ecc8b4f8e035..7ae68a1dbb75 100644 --- a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example54.java +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example54.java @@ -1,7 +1,6 @@ package com.snippets; import com.seed.exhaustive.SeedExhaustiveClient; -import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; public class Example54 { public static void main(String[] args) { @@ -10,11 +9,6 @@ public static void main(String[] args) { .url("https://api.fern.com") .build(); - client.reqWithHeaders() - .getWithCustomHeader(ReqWithHeaders.builder() - .xTestServiceHeader("X-TEST-SERVICE-HEADER") - .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") - .body("string") - .build()); + client.noReqBody().postWithNoRequestBody(); } } diff --git a/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example55.java b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example55.java new file mode 100644 index 000000000000..5ba6aebfd9fa --- /dev/null +++ b/seed/java-sdk/exhaustive/signed_publish/src/main/java/com/snippets/Example55.java @@ -0,0 +1,20 @@ +package com.snippets; + +import com.seed.exhaustive.SeedExhaustiveClient; +import com.seed.exhaustive.resources.reqwithheaders.requests.ReqWithHeaders; + +public class Example55 { + public static void main(String[] args) { + SeedExhaustiveClient client = SeedExhaustiveClient.builder() + .token("") + .url("https://api.fern.com") + .build(); + + client.reqWithHeaders() + .getWithCustomHeader(ReqWithHeaders.builder() + .xTestServiceHeader("X-TEST-SERVICE-HEADER") + .xTestEndpointHeader("X-TEST-ENDPOINT-HEADER") + .body("string") + .build()); + } +} From f24315719cb457ada529d2fac35784c21f0e488a Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 24 Feb 2026 22:08:46 -0500 Subject: [PATCH 15/20] chore(ruby): update ruby-sdk-v2 seed (#12734) Co-authored-by: thesandlord --- .../dynamic-snippets/example33/snippet.rb | 2 +- .../dynamic-snippets/example34/snippet.rb | 2 +- .../dynamic-snippets/example35/snippet.rb | 2 +- .../dynamic-snippets/example36/snippet.rb | 2 +- .../dynamic-snippets/example37/snippet.rb | 2 +- .../dynamic-snippets/example38/snippet.rb | 2 +- .../dynamic-snippets/example39/snippet.rb | 2 +- .../dynamic-snippets/example40/snippet.rb | 2 +- .../dynamic-snippets/example41/snippet.rb | 2 +- .../dynamic-snippets/example42/snippet.rb | 2 +- .../dynamic-snippets/example43/snippet.rb | 2 +- .../dynamic-snippets/example44/snippet.rb | 2 +- .../dynamic-snippets/example45/snippet.rb | 2 +- .../dynamic-snippets/example46/snippet.rb | 2 +- .../dynamic-snippets/example47/snippet.rb | 2 +- .../dynamic-snippets/example48/snippet.rb | 22 +------ .../dynamic-snippets/example50/snippet.rb | 22 ++++++- .../dynamic-snippets/example52/snippet.rb | 2 +- .../dynamic-snippets/example53/snippet.rb | 2 +- .../dynamic-snippets/example54/snippet.rb | 6 +- .../dynamic-snippets/example55/snippet.rb | 12 ++++ .../lib/seed/endpoints/params/client.rb | 34 ++++++++++ .../exhaustive/wire-tests/reference.md | 62 +++++++++++++++++++ .../test/wire/endpoints_params_test.rb | 21 +++++++ .../wiremock/wiremock-mappings.json | 2 +- 25 files changed, 170 insertions(+), 45 deletions(-) create mode 100644 seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example55/snippet.rb diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example33/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example33/snippet.rb index 89257abafb8d..7ad7bd86d195 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example33/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example33/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.endpoints.primitive.get_and_return_string(request: "string") +client.endpoints.params.upload_with_path(param: "upload-path") diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example34/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example34/snippet.rb index d88920fd87a9..89257abafb8d 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example34/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example34/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.endpoints.primitive.get_and_return_int(request: 1) +client.endpoints.primitive.get_and_return_string(request: "string") diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example35/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example35/snippet.rb index af09c38bbc97..d88920fd87a9 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example35/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example35/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.endpoints.primitive.get_and_return_long(request: 1000000) +client.endpoints.primitive.get_and_return_int(request: 1) diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example36/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example36/snippet.rb index bb5b42f5012f..af09c38bbc97 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example36/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example36/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.endpoints.primitive.get_and_return_double(request: 1.1) +client.endpoints.primitive.get_and_return_long(request: 1000000) diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example37/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example37/snippet.rb index 81dbb5086528..bb5b42f5012f 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example37/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example37/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.endpoints.primitive.get_and_return_bool(request: true) +client.endpoints.primitive.get_and_return_double(request: 1.1) diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example38/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example38/snippet.rb index 960041774890..81dbb5086528 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example38/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example38/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.endpoints.primitive.get_and_return_datetime(request: "2024-01-15T09:30:00Z") +client.endpoints.primitive.get_and_return_bool(request: true) diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example39/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example39/snippet.rb index 0cbe0bfdec01..960041774890 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example39/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example39/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.endpoints.primitive.get_and_return_date(request: "2023-01-15") +client.endpoints.primitive.get_and_return_datetime(request: "2024-01-15T09:30:00Z") diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example40/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example40/snippet.rb index fa30420418e5..0cbe0bfdec01 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example40/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example40/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.endpoints.primitive.get_and_return_uuid(request: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32") +client.endpoints.primitive.get_and_return_date(request: "2023-01-15") diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example41/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example41/snippet.rb index 3283b72cd541..fa30420418e5 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example41/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example41/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.endpoints.primitive.get_and_return_base_64(request: "SGVsbG8gd29ybGQh") +client.endpoints.primitive.get_and_return_uuid(request: "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32") diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example42/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example42/snippet.rb index e05bddfd5478..3283b72cd541 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example42/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example42/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.endpoints.put.add(id: "id") +client.endpoints.primitive.get_and_return_base_64(request: "SGVsbG8gd29ybGQh") diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example43/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example43/snippet.rb index 520dcc78d713..e05bddfd5478 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example43/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example43/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.endpoints.union.get_and_return_union +client.endpoints.put.add(id: "id") diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example44/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example44/snippet.rb index fad51fd6b072..520dcc78d713 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example44/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example44/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.endpoints.urls.with_mixed_case +client.endpoints.union.get_and_return_union diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example45/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example45/snippet.rb index 3668cdf9e4c8..fad51fd6b072 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example45/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example45/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.endpoints.urls.no_ending_slash +client.endpoints.urls.with_mixed_case diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example46/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example46/snippet.rb index ba7b7b17f5be..3668cdf9e4c8 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example46/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example46/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.endpoints.urls.with_ending_slash +client.endpoints.urls.no_ending_slash diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example47/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example47/snippet.rb index 1cf0389f0b07..ba7b7b17f5be 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example47/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example47/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.endpoints.urls.with_underscores +client.endpoints.urls.with_ending_slash diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example48/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example48/snippet.rb index 27ecfce73d3b..1cf0389f0b07 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example48/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example48/snippet.rb @@ -5,24 +5,4 @@ base_url: "https://api.fern.com" ) -client.inlined_requests.post_with_object_bodyand_response( - string: "string", - integer: 1, - nested_object: { - 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", - base_64: "SGVsbG8gd29ybGQh", - list: %w[list list], - set: Set.new(["set"]), - map: { - 1 => "map" - }, - bigint: "1000000" - } -) +client.endpoints.urls.with_underscores diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example50/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example50/snippet.rb index 1b4fd590d009..27ecfce73d3b 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example50/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example50/snippet.rb @@ -5,4 +5,24 @@ base_url: "https://api.fern.com" ) -client.no_auth.post_with_no_auth +client.inlined_requests.post_with_object_bodyand_response( + string: "string", + integer: 1, + nested_object: { + 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", + base_64: "SGVsbG8gd29ybGQh", + list: %w[list list], + set: Set.new(["set"]), + map: { + 1 => "map" + }, + bigint: "1000000" + } +) diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example52/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example52/snippet.rb index 84805d095dd6..1b4fd590d009 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example52/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example52/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.no_req_body.get_with_no_request_body +client.no_auth.post_with_no_auth diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example53/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example53/snippet.rb index 41b55b77b455..84805d095dd6 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example53/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example53/snippet.rb @@ -5,4 +5,4 @@ base_url: "https://api.fern.com" ) -client.no_req_body.post_with_no_request_body +client.no_req_body.get_with_no_request_body diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example54/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example54/snippet.rb index 0366bf524c68..41b55b77b455 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example54/snippet.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example54/snippet.rb @@ -5,8 +5,4 @@ base_url: "https://api.fern.com" ) -client.req_with_headers.get_with_custom_header( - x_test_service_header: "X-TEST-SERVICE-HEADER", - x_test_endpoint_header: "X-TEST-ENDPOINT-HEADER", - body: "string" -) +client.no_req_body.post_with_no_request_body diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example55/snippet.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example55/snippet.rb new file mode 100644 index 000000000000..0366bf524c68 --- /dev/null +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/dynamic-snippets/example55/snippet.rb @@ -0,0 +1,12 @@ +require "seed" + +client = Seed::MyClient.new( + token: "", + base_url: "https://api.fern.com" +) + +client.req_with_headers.get_with_custom_header( + x_test_service_header: "X-TEST-SERVICE-HEADER", + x_test_endpoint_header: "X-TEST-ENDPOINT-HEADER", + body: "string" +) diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/lib/seed/endpoints/params/client.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/lib/seed/endpoints/params/client.rb index f669bceaa4fc..777c36cc1003 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/lib/seed/endpoints/params/client.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/lib/seed/endpoints/params/client.rb @@ -301,6 +301,40 @@ def modify_with_inline_path(request_options: {}, **params) error_class = Seed::Errors::ResponseError.subclass_for_code(code) raise error_class.new(response.body, code: code) end + + # POST bytes with path param returning object + # + # @param request_options [Hash] + # @param params [Hash] + # @option request_options [String] :base_url + # @option request_options [Hash{String => Object}] :additional_headers + # @option request_options [Hash{String => Object}] :additional_query_parameters + # @option request_options [Hash{String => Object}] :additional_body_parameters + # @option request_options [Integer] :timeout_in_seconds + # @option params [String] :param + # + # @return [Seed::Types::Object_::Types::ObjectWithRequiredField] + def upload_with_path(request_options: {}, **params) + params = Seed::Internal::Types::Utils.normalize_keys(params) + request = Seed::Internal::JSON::Request.new( + base_url: request_options[:base_url], + method: "POST", + path: "/params/path/#{params[:param]}", + request_options: request_options + ) + begin + response = @client.send(request) + rescue Net::HTTPRequestTimeout + raise Seed::Errors::TimeoutError + end + code = response.code.to_i + if code.between?(200, 299) + Seed::Types::Object_::Types::ObjectWithRequiredField.load(response.body) + else + error_class = Seed::Errors::ResponseError.subclass_for_code(code) + raise error_class.new(response.body, code: code) + end + end end end end diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/reference.md b/seed/ruby-sdk-v2/exhaustive/wire-tests/reference.md index 4b8178c12d32..a93eb50c3d25 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/reference.md +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/reference.md @@ -1949,6 +1949,68 @@ client.endpoints.params.modify_with_path( + + + + +
client.endpoints.params.upload_with_path(param, request) -> Seed::Types::Object_::Types::ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```ruby +client.endpoints.params.upload_with_path(param: "upload-path") +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `String` + +
+
+ +
+
+ +**request_options:** `Seed::Endpoints::Params::RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/test/wire/endpoints_params_test.rb b/seed/ruby-sdk-v2/exhaustive/wire-tests/test/wire/endpoints_params_test.rb index 65b258936328..42bb3a42bd7d 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/test/wire/endpoints_params_test.rb +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/test/wire/endpoints_params_test.rb @@ -185,4 +185,25 @@ def test_endpoints_params_modify_with_inline_path_with_wiremock expected: 1 ) end + + def test_endpoints_params_upload_with_path_with_wiremock + test_id = "endpoints.params.upload_with_path.0" + + @client.endpoints.params.upload_with_path( + param: "upload-path", + request_options: { + additional_headers: { + "X-Test-Id" => "endpoints.params.upload_with_path.0" + } + } + ) + + verify_request_count( + test_id: test_id, + method: "POST", + url_path: "/params/path/upload-path", + query_params: nil, + expected: 1 + ) + end end diff --git a/seed/ruby-sdk-v2/exhaustive/wire-tests/wiremock/wiremock-mappings.json b/seed/ruby-sdk-v2/exhaustive/wire-tests/wiremock/wiremock-mappings.json index 4916bea9a994..8ffc6a19b116 100644 --- a/seed/ruby-sdk-v2/exhaustive/wire-tests/wiremock/wiremock-mappings.json +++ b/seed/ruby-sdk-v2/exhaustive/wire-tests/wiremock/wiremock-mappings.json @@ -1 +1 @@ -{"mappings":[{"id":"864d58e6-0c08-4f38-b7e1-2638f8d7fd6d","name":"getAndReturnListOfPrimitives - default","request":{"urlPathTemplate":"/container/list-of-primitives","method":"POST"},"response":{"status":200,"body":"[\n \"string\",\n \"string\"\n]","headers":{"Content-Type":"application/json"}},"uuid":"864d58e6-0c08-4f38-b7e1-2638f8d7fd6d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"ac1d4c4f-a8a7-4c27-ae64-7fc977cfa122","name":"getAndReturnListOfObjects - default","request":{"urlPathTemplate":"/container/list-of-objects","method":"POST"},"response":{"status":200,"body":"[\n {\n \"string\": \"string\"\n },\n {\n \"string\": \"string\"\n }\n]","headers":{"Content-Type":"application/json"}},"uuid":"ac1d4c4f-a8a7-4c27-ae64-7fc977cfa122","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"591d5c48-a536-452b-8a2e-ad7c23c38298","name":"getAndReturnSetOfPrimitives - default","request":{"urlPathTemplate":"/container/set-of-primitives","method":"POST"},"response":{"status":200,"body":"[\n \"string\"\n]","headers":{"Content-Type":"application/json"}},"uuid":"591d5c48-a536-452b-8a2e-ad7c23c38298","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e1d5f52b-7a51-464f-ac8f-83c0345a3a35","name":"getAndReturnSetOfObjects - default","request":{"urlPathTemplate":"/container/set-of-objects","method":"POST"},"response":{"status":200,"body":"[\n {\n \"string\": \"string\"\n }\n]","headers":{"Content-Type":"application/json"}},"uuid":"e1d5f52b-7a51-464f-ac8f-83c0345a3a35","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4b1d33b3-ca7d-462a-a2e3-23d531ae2922","name":"getAndReturnMapPrimToPrim - default","request":{"urlPathTemplate":"/container/map-prim-to-prim","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"4b1d33b3-ca7d-462a-a2e3-23d531ae2922","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b01ac2b9-3470-48aa-badc-57d331bb5a49","name":"getAndReturnMapOfPrimToObject - default","request":{"urlPathTemplate":"/container/map-prim-to-object","method":"POST"},"response":{"status":200,"body":"{\n \"string\": {\n \"string\": \"string\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"b01ac2b9-3470-48aa-badc-57d331bb5a49","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"eaf9315d-55c4-4434-8004-a70c25af5656","name":"getAndReturnMapOfPrimToUndiscriminatedUnion - default","request":{"urlPathTemplate":"/container/map-prim-to-union","method":"POST"},"response":{"status":200,"body":"{\n \"string\": 1.1\n}","headers":{"Content-Type":"application/json"}},"uuid":"eaf9315d-55c4-4434-8004-a70c25af5656","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e5271904-de0a-425f-940d-d6f6bde34755","name":"getAndReturnOptional - default","request":{"urlPathTemplate":"/container/opt-objects","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"e5271904-de0a-425f-940d-d6f6bde34755","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d19e1fbe-79cc-465c-962e-e1866ca2361b","name":"postJsonPatchContentType - default","request":{"urlPathTemplate":"/foo/bar","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"d19e1fbe-79cc-465c-962e-e1866ca2361b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4a107cf5-6284-48f8-9ddb-99d944ba989b","name":"postJsonPatchContentWithCharsetType - default","request":{"urlPathTemplate":"/foo/baz","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"4a107cf5-6284-48f8-9ddb-99d944ba989b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"60fd3c8a-3983-41b9-8178-f42997388900","name":"getAndReturnEnum - default","request":{"urlPathTemplate":"/enum","method":"POST"},"response":{"status":200,"body":"\"SUNNY\"","headers":{"Content-Type":"application/json"}},"uuid":"60fd3c8a-3983-41b9-8178-f42997388900","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"3d95c052-db6e-4eff-95f2-895666a5af54","name":"testGet - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"GET","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"3d95c052-db6e-4eff-95f2-895666a5af54","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"2552873f-9f1d-4557-a6c7-6c6b7a55b566","name":"testPost - default","request":{"urlPathTemplate":"/http-methods","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"2552873f-9f1d-4557-a6c7-6c6b7a55b566","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"db42fbdf-5426-41b7-b7ea-28ed39a38e82","name":"testPut - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"PUT","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"db42fbdf-5426-41b7-b7ea-28ed39a38e82","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c9def317-32a0-4bc0-b9b6-5efae8ca44a6","name":"testPatch - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"PATCH","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"c9def317-32a0-4bc0-b9b6-5efae8ca44a6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"05333635-c9ce-4c1a-bb6d-95e8a0fb80dc","name":"testDelete - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"DELETE","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"05333635-c9ce-4c1a-bb6d-95e8a0fb80dc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"41cdef0e-040f-4d08-8426-87b19e60f7d7","name":"getAndReturnWithOptionalField - default","request":{"urlPathTemplate":"/object/get-and-return-with-optional-field","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"41cdef0e-040f-4d08-8426-87b19e60f7d7","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"f74df550-df8c-4503-b202-0b9b3165c1a7","name":"getAndReturnWithRequiredField - default","request":{"urlPathTemplate":"/object/get-and-return-with-required-field","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"f74df550-df8c-4503-b202-0b9b3165c1a7","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0c904dbb-ce54-48a2-8364-13a4989be7f2","name":"getAndReturnWithMapOfMap - default","request":{"urlPathTemplate":"/object/get-and-return-with-map-of-map","method":"POST"},"response":{"status":200,"body":"{\n \"map\": {\n \"map\": {\n \"map\": \"map\"\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"0c904dbb-ce54-48a2-8364-13a4989be7f2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"67e969fc-ff81-4a67-9858-66a29ffc9b72","name":"getAndReturnNestedWithOptionalField - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-optional-field","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"67e969fc-ff81-4a67-9858-66a29ffc9b72","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"169827d0-4247-4236-8cef-34b94d2659de","name":"getAndReturnNestedWithRequiredField - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-required-field/{string}","method":"POST","pathParameters":{"string":{"equalTo":"string"}}},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"169827d0-4247-4236-8cef-34b94d2659de","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6d9ed308-5724-4bbc-a4f7-28dc264d188f","name":"getAndReturnNestedWithRequiredFieldAsList - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-required-field-list","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"6d9ed308-5724-4bbc-a4f7-28dc264d188f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e2cc3e92-6e37-4a50-9081-23952fdd73fe","name":"getAndReturnWithDatetimeLikeString - default","request":{"urlPathTemplate":"/object/get-and-return-with-datetime-like-string","method":"POST"},"response":{"status":200,"body":"{\n \"datetimeLikeString\": \"2023-08-31T14:15:22Z\",\n \"actualDatetime\": \"2023-08-31T14:15:22Z\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"e2cc3e92-6e37-4a50-9081-23952fdd73fe","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"f15b2079-2482-4ed2-951d-d2b5c4de9afd","name":"listItems - default","request":{"urlPathTemplate":"/pagination","method":"GET"},"response":{"status":200,"body":"{\n \"items\": [\n {\n \"string\": \"string\"\n },\n {\n \"string\": \"string\"\n }\n ],\n \"next\": \"next\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"f15b2079-2482-4ed2-951d-d2b5c4de9afd","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"97806fdb-f31f-4f90-84b8-f9cc1713d53d","name":"getWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"97806fdb-f31f-4f90-84b8-f9cc1713d53d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"711fc64f-4af9-4084-8c29-1e7a9e58be70","name":"getWithInlinePath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"711fc64f-4af9-4084-8c29-1e7a9e58be70","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8e5739b3-d75f-47d7-b6b8-a663d91a66b5","name":"getWithQuery - default","request":{"urlPathTemplate":"/params","method":"GET"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"8e5739b3-d75f-47d7-b6b8-a663d91a66b5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"4125d349-732b-4ff7-948d-1eeb977ed13b","name":"getWithAllowMultipleQuery - default","request":{"urlPathTemplate":"/params","method":"GET"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"4125d349-732b-4ff7-948d-1eeb977ed13b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"1933d96a-a1b9-4cf1-85ba-c1e8eff5bd56","name":"getWithPathAndQuery - default","request":{"urlPathTemplate":"/params/path-query/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"1933d96a-a1b9-4cf1-85ba-c1e8eff5bd56","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c4f5012a-fb3f-45ac-b695-beedc3353ad8","name":"getWithInlinePathAndQuery - default","request":{"urlPathTemplate":"/params/path-query/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"c4f5012a-fb3f-45ac-b695-beedc3353ad8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"046bf7d6-751b-48e9-bfc6-ff74a17e88e1","name":"modifyWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"PUT","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"046bf7d6-751b-48e9-bfc6-ff74a17e88e1","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6bfc7195-b99c-4449-bf47-7c4f74f6f33b","name":"modifyWithInlinePath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"PUT","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"6bfc7195-b99c-4449-bf47-7c4f74f6f33b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0506ae7c-87b7-4cd2-9e50-31d60f81893f","name":"getAndReturnString - default","request":{"urlPathTemplate":"/primitive/string","method":"POST"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"0506ae7c-87b7-4cd2-9e50-31d60f81893f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"67f875d1-e19e-440d-8f0c-7e1d24ef2619","name":"getAndReturnInt - default","request":{"urlPathTemplate":"/primitive/integer","method":"POST"},"response":{"status":200,"body":"1","headers":{"Content-Type":"application/json"}},"uuid":"67f875d1-e19e-440d-8f0c-7e1d24ef2619","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"5fadbebd-86c0-41f9-8be5-864e39eb5924","name":"getAndReturnLong - default","request":{"urlPathTemplate":"/primitive/long","method":"POST"},"response":{"status":200,"body":"1000000","headers":{"Content-Type":"application/json"}},"uuid":"5fadbebd-86c0-41f9-8be5-864e39eb5924","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e03872b6-86b1-490b-9195-86e5d3a014f2","name":"getAndReturnDouble - default","request":{"urlPathTemplate":"/primitive/double","method":"POST"},"response":{"status":200,"body":"1.1","headers":{"Content-Type":"application/json"}},"uuid":"e03872b6-86b1-490b-9195-86e5d3a014f2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"442e632f-890a-4105-9448-f7015127e3b4","name":"getAndReturnBool - default","request":{"urlPathTemplate":"/primitive/boolean","method":"POST"},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"442e632f-890a-4105-9448-f7015127e3b4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"ad76fa81-1e63-43fd-9a99-1d5e4339d98c","name":"getAndReturnDatetime - default","request":{"urlPathTemplate":"/primitive/datetime","method":"POST"},"response":{"status":200,"body":"\"2024-01-15T09:30:00Z\"","headers":{"Content-Type":"application/json"}},"uuid":"ad76fa81-1e63-43fd-9a99-1d5e4339d98c","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"afb524b1-54ab-4674-8446-fdd574c368cc","name":"getAndReturnDate - default","request":{"urlPathTemplate":"/primitive/date","method":"POST"},"response":{"status":200,"body":"\"2023-01-15\"","headers":{"Content-Type":"application/json"}},"uuid":"afb524b1-54ab-4674-8446-fdd574c368cc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"969c0f3a-218a-45b7-b17f-64b1a9307d43","name":"getAndReturnUUID - default","request":{"urlPathTemplate":"/primitive/uuid","method":"POST"},"response":{"status":200,"body":"\"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\"","headers":{"Content-Type":"application/json"}},"uuid":"969c0f3a-218a-45b7-b17f-64b1a9307d43","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"04baa20d-d318-40e6-9784-c40158c16acd","name":"getAndReturnBase64 - default","request":{"urlPathTemplate":"/primitive/base64","method":"POST"},"response":{"status":200,"body":"\"SGVsbG8gd29ybGQh\"","headers":{"Content-Type":"application/json"}},"uuid":"04baa20d-d318-40e6-9784-c40158c16acd","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"fddd5aaf-ab0d-4ef6-949c-5d329f6d7eb2","name":"Put - default","request":{"urlPathTemplate":"/{id}","method":"PUT","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\n \"errors\": [\n {\n \"category\": \"API_ERROR\",\n \"code\": \"INTERNAL_SERVER_ERROR\",\n \"detail\": \"detail\",\n \"field\": \"field\"\n },\n {\n \"category\": \"API_ERROR\",\n \"code\": \"INTERNAL_SERVER_ERROR\",\n \"detail\": \"detail\",\n \"field\": \"field\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"fddd5aaf-ab0d-4ef6-949c-5d329f6d7eb2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d29330f9-49ff-49c4-9081-7c83dbad315b","name":"getAndReturnUnion - default","request":{"urlPathTemplate":"/union","method":"POST"},"response":{"status":200,"body":"{\n \"animal\": \"dog\",\n \"name\": \"name\",\n \"likesToWoof\": true\n}","headers":{"Content-Type":"application/json"}},"uuid":"d29330f9-49ff-49c4-9081-7c83dbad315b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"343f71f3-36ce-4684-b762-d60a086b43a4","name":"withMixedCase - default","request":{"urlPathTemplate":"/urls/MixedCase","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"343f71f3-36ce-4684-b762-d60a086b43a4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"48f36314-b2b7-4910-9e1d-5b05f3346a60","name":"noEndingSlash - default","request":{"urlPathTemplate":"/urls/no-ending-slash","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"48f36314-b2b7-4910-9e1d-5b05f3346a60","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"f7b95029-2f25-4f70-8b4c-0855712747d8","name":"withEndingSlash - default","request":{"urlPathTemplate":"/urls/with-ending-slash/","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"f7b95029-2f25-4f70-8b4c-0855712747d8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"2f9671a5-e6da-43a8-be27-d0be0191dcbf","name":"withUnderscores - default","request":{"urlPathTemplate":"/urls/with_underscores","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"2f9671a5-e6da-43a8-be27-d0be0191dcbf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"4a6d0aaa-05f2-47bb-9f40-6e3743b9d2bf","name":"postWithObjectBodyandResponse - default","request":{"urlPathTemplate":"/req-bodies/object","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"4a6d0aaa-05f2-47bb-9f40-6e3743b9d2bf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c6e7cc6c-b76f-4860-a9ad-52dc8f55b6e6","name":"postWithNoAuth - default","request":{"urlPathTemplate":"/no-auth","method":"POST"},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"c6e7cc6c-b76f-4860-a9ad-52dc8f55b6e6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8098eeea-bc6b-4068-9601-566c2092f83f","name":"getWithNoRequestBody - default","request":{"urlPathTemplate":"/no-req-body","method":"GET"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"8098eeea-bc6b-4068-9601-566c2092f83f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"7dd9f944-1b35-42e9-a5ed-e48214ac8e91","name":"postWithNoRequestBody - default","request":{"urlPathTemplate":"/no-req-body","method":"POST"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"7dd9f944-1b35-42e9-a5ed-e48214ac8e91","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d7b54168-aef4-4d68-a9c5-446e97dee2fc","name":"getWithCustomHeader - default","request":{"urlPathTemplate":"/test-headers/custom-header","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"d7b54168-aef4-4d68-a9c5-446e97dee2fc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}}],"meta":{"total":52}} \ No newline at end of file +{"mappings":[{"id":"864d58e6-0c08-4f38-b7e1-2638f8d7fd6d","name":"getAndReturnListOfPrimitives - default","request":{"urlPathTemplate":"/container/list-of-primitives","method":"POST"},"response":{"status":200,"body":"[\n \"string\",\n \"string\"\n]","headers":{"Content-Type":"application/json"}},"uuid":"864d58e6-0c08-4f38-b7e1-2638f8d7fd6d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"ac1d4c4f-a8a7-4c27-ae64-7fc977cfa122","name":"getAndReturnListOfObjects - default","request":{"urlPathTemplate":"/container/list-of-objects","method":"POST"},"response":{"status":200,"body":"[\n {\n \"string\": \"string\"\n },\n {\n \"string\": \"string\"\n }\n]","headers":{"Content-Type":"application/json"}},"uuid":"ac1d4c4f-a8a7-4c27-ae64-7fc977cfa122","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"591d5c48-a536-452b-8a2e-ad7c23c38298","name":"getAndReturnSetOfPrimitives - default","request":{"urlPathTemplate":"/container/set-of-primitives","method":"POST"},"response":{"status":200,"body":"[\n \"string\"\n]","headers":{"Content-Type":"application/json"}},"uuid":"591d5c48-a536-452b-8a2e-ad7c23c38298","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e1d5f52b-7a51-464f-ac8f-83c0345a3a35","name":"getAndReturnSetOfObjects - default","request":{"urlPathTemplate":"/container/set-of-objects","method":"POST"},"response":{"status":200,"body":"[\n {\n \"string\": \"string\"\n }\n]","headers":{"Content-Type":"application/json"}},"uuid":"e1d5f52b-7a51-464f-ac8f-83c0345a3a35","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4b1d33b3-ca7d-462a-a2e3-23d531ae2922","name":"getAndReturnMapPrimToPrim - default","request":{"urlPathTemplate":"/container/map-prim-to-prim","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"4b1d33b3-ca7d-462a-a2e3-23d531ae2922","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"b01ac2b9-3470-48aa-badc-57d331bb5a49","name":"getAndReturnMapOfPrimToObject - default","request":{"urlPathTemplate":"/container/map-prim-to-object","method":"POST"},"response":{"status":200,"body":"{\n \"string\": {\n \"string\": \"string\"\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"b01ac2b9-3470-48aa-badc-57d331bb5a49","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"eaf9315d-55c4-4434-8004-a70c25af5656","name":"getAndReturnMapOfPrimToUndiscriminatedUnion - default","request":{"urlPathTemplate":"/container/map-prim-to-union","method":"POST"},"response":{"status":200,"body":"{\n \"string\": 1.1\n}","headers":{"Content-Type":"application/json"}},"uuid":"eaf9315d-55c4-4434-8004-a70c25af5656","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e5271904-de0a-425f-940d-d6f6bde34755","name":"getAndReturnOptional - default","request":{"urlPathTemplate":"/container/opt-objects","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"e5271904-de0a-425f-940d-d6f6bde34755","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d19e1fbe-79cc-465c-962e-e1866ca2361b","name":"postJsonPatchContentType - default","request":{"urlPathTemplate":"/foo/bar","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"d19e1fbe-79cc-465c-962e-e1866ca2361b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"4a107cf5-6284-48f8-9ddb-99d944ba989b","name":"postJsonPatchContentWithCharsetType - default","request":{"urlPathTemplate":"/foo/baz","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"4a107cf5-6284-48f8-9ddb-99d944ba989b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"60fd3c8a-3983-41b9-8178-f42997388900","name":"getAndReturnEnum - default","request":{"urlPathTemplate":"/enum","method":"POST"},"response":{"status":200,"body":"\"SUNNY\"","headers":{"Content-Type":"application/json"}},"uuid":"60fd3c8a-3983-41b9-8178-f42997388900","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"3d95c052-db6e-4eff-95f2-895666a5af54","name":"testGet - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"GET","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"3d95c052-db6e-4eff-95f2-895666a5af54","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"2552873f-9f1d-4557-a6c7-6c6b7a55b566","name":"testPost - default","request":{"urlPathTemplate":"/http-methods","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"2552873f-9f1d-4557-a6c7-6c6b7a55b566","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"db42fbdf-5426-41b7-b7ea-28ed39a38e82","name":"testPut - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"PUT","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"db42fbdf-5426-41b7-b7ea-28ed39a38e82","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c9def317-32a0-4bc0-b9b6-5efae8ca44a6","name":"testPatch - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"PATCH","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"c9def317-32a0-4bc0-b9b6-5efae8ca44a6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"05333635-c9ce-4c1a-bb6d-95e8a0fb80dc","name":"testDelete - default","request":{"urlPathTemplate":"/http-methods/{id}","method":"DELETE","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"05333635-c9ce-4c1a-bb6d-95e8a0fb80dc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"41cdef0e-040f-4d08-8426-87b19e60f7d7","name":"getAndReturnWithOptionalField - default","request":{"urlPathTemplate":"/object/get-and-return-with-optional-field","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"41cdef0e-040f-4d08-8426-87b19e60f7d7","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"f74df550-df8c-4503-b202-0b9b3165c1a7","name":"getAndReturnWithRequiredField - default","request":{"urlPathTemplate":"/object/get-and-return-with-required-field","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"f74df550-df8c-4503-b202-0b9b3165c1a7","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0c904dbb-ce54-48a2-8364-13a4989be7f2","name":"getAndReturnWithMapOfMap - default","request":{"urlPathTemplate":"/object/get-and-return-with-map-of-map","method":"POST"},"response":{"status":200,"body":"{\n \"map\": {\n \"map\": {\n \"map\": \"map\"\n }\n }\n}","headers":{"Content-Type":"application/json"}},"uuid":"0c904dbb-ce54-48a2-8364-13a4989be7f2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"67e969fc-ff81-4a67-9858-66a29ffc9b72","name":"getAndReturnNestedWithOptionalField - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-optional-field","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"67e969fc-ff81-4a67-9858-66a29ffc9b72","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"169827d0-4247-4236-8cef-34b94d2659de","name":"getAndReturnNestedWithRequiredField - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-required-field/{string}","method":"POST","pathParameters":{"string":{"equalTo":"string"}}},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"169827d0-4247-4236-8cef-34b94d2659de","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6d9ed308-5724-4bbc-a4f7-28dc264d188f","name":"getAndReturnNestedWithRequiredFieldAsList - default","request":{"urlPathTemplate":"/object/get-and-return-nested-with-required-field-list","method":"POST"},"response":{"status":200,"body":"{\n \"string\": \"string\",\n \"NestedObject\": {\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 }\n}","headers":{"Content-Type":"application/json"}},"uuid":"6d9ed308-5724-4bbc-a4f7-28dc264d188f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e2cc3e92-6e37-4a50-9081-23952fdd73fe","name":"getAndReturnWithDatetimeLikeString - default","request":{"urlPathTemplate":"/object/get-and-return-with-datetime-like-string","method":"POST"},"response":{"status":200,"body":"{\n \"datetimeLikeString\": \"2023-08-31T14:15:22Z\",\n \"actualDatetime\": \"2023-08-31T14:15:22Z\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"e2cc3e92-6e37-4a50-9081-23952fdd73fe","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"f15b2079-2482-4ed2-951d-d2b5c4de9afd","name":"listItems - default","request":{"urlPathTemplate":"/pagination","method":"GET"},"response":{"status":200,"body":"{\n \"items\": [\n {\n \"string\": \"string\"\n },\n {\n \"string\": \"string\"\n }\n ],\n \"next\": \"next\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"f15b2079-2482-4ed2-951d-d2b5c4de9afd","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"97806fdb-f31f-4f90-84b8-f9cc1713d53d","name":"getWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"97806fdb-f31f-4f90-84b8-f9cc1713d53d","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"711fc64f-4af9-4084-8c29-1e7a9e58be70","name":"getWithInlinePath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"711fc64f-4af9-4084-8c29-1e7a9e58be70","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8e5739b3-d75f-47d7-b6b8-a663d91a66b5","name":"getWithQuery - default","request":{"urlPathTemplate":"/params","method":"GET"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"8e5739b3-d75f-47d7-b6b8-a663d91a66b5","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"4125d349-732b-4ff7-948d-1eeb977ed13b","name":"getWithAllowMultipleQuery - default","request":{"urlPathTemplate":"/params","method":"GET"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"4125d349-732b-4ff7-948d-1eeb977ed13b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"1933d96a-a1b9-4cf1-85ba-c1e8eff5bd56","name":"getWithPathAndQuery - default","request":{"urlPathTemplate":"/params/path-query/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"1933d96a-a1b9-4cf1-85ba-c1e8eff5bd56","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c4f5012a-fb3f-45ac-b695-beedc3353ad8","name":"getWithInlinePathAndQuery - default","request":{"urlPathTemplate":"/params/path-query/{param}","method":"GET","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"c4f5012a-fb3f-45ac-b695-beedc3353ad8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"046bf7d6-751b-48e9-bfc6-ff74a17e88e1","name":"modifyWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"PUT","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"046bf7d6-751b-48e9-bfc6-ff74a17e88e1","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"6bfc7195-b99c-4449-bf47-7c4f74f6f33b","name":"modifyWithInlinePath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"PUT","pathParameters":{"param":{"equalTo":"param"}}},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"6bfc7195-b99c-4449-bf47-7c4f74f6f33b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"658d6145-14f6-48d3-9c66-c63e01eb6fd2","name":"uploadWithPath - default","request":{"urlPathTemplate":"/params/path/{param}","method":"POST","pathParameters":{"param":{"equalTo":"upload-path"}}},"response":{"status":200,"body":"{\n \"string\": \"uploaded\"\n}","headers":{"Content-Type":"application/json"}},"uuid":"658d6145-14f6-48d3-9c66-c63e01eb6fd2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"0506ae7c-87b7-4cd2-9e50-31d60f81893f","name":"getAndReturnString - default","request":{"urlPathTemplate":"/primitive/string","method":"POST"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"0506ae7c-87b7-4cd2-9e50-31d60f81893f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"67f875d1-e19e-440d-8f0c-7e1d24ef2619","name":"getAndReturnInt - default","request":{"urlPathTemplate":"/primitive/integer","method":"POST"},"response":{"status":200,"body":"1","headers":{"Content-Type":"application/json"}},"uuid":"67f875d1-e19e-440d-8f0c-7e1d24ef2619","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"5fadbebd-86c0-41f9-8be5-864e39eb5924","name":"getAndReturnLong - default","request":{"urlPathTemplate":"/primitive/long","method":"POST"},"response":{"status":200,"body":"1000000","headers":{"Content-Type":"application/json"}},"uuid":"5fadbebd-86c0-41f9-8be5-864e39eb5924","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"e03872b6-86b1-490b-9195-86e5d3a014f2","name":"getAndReturnDouble - default","request":{"urlPathTemplate":"/primitive/double","method":"POST"},"response":{"status":200,"body":"1.1","headers":{"Content-Type":"application/json"}},"uuid":"e03872b6-86b1-490b-9195-86e5d3a014f2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"442e632f-890a-4105-9448-f7015127e3b4","name":"getAndReturnBool - default","request":{"urlPathTemplate":"/primitive/boolean","method":"POST"},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"442e632f-890a-4105-9448-f7015127e3b4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"ad76fa81-1e63-43fd-9a99-1d5e4339d98c","name":"getAndReturnDatetime - default","request":{"urlPathTemplate":"/primitive/datetime","method":"POST"},"response":{"status":200,"body":"\"2024-01-15T09:30:00Z\"","headers":{"Content-Type":"application/json"}},"uuid":"ad76fa81-1e63-43fd-9a99-1d5e4339d98c","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"afb524b1-54ab-4674-8446-fdd574c368cc","name":"getAndReturnDate - default","request":{"urlPathTemplate":"/primitive/date","method":"POST"},"response":{"status":200,"body":"\"2023-01-15\"","headers":{"Content-Type":"application/json"}},"uuid":"afb524b1-54ab-4674-8446-fdd574c368cc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"969c0f3a-218a-45b7-b17f-64b1a9307d43","name":"getAndReturnUUID - default","request":{"urlPathTemplate":"/primitive/uuid","method":"POST"},"response":{"status":200,"body":"\"d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32\"","headers":{"Content-Type":"application/json"}},"uuid":"969c0f3a-218a-45b7-b17f-64b1a9307d43","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"04baa20d-d318-40e6-9784-c40158c16acd","name":"getAndReturnBase64 - default","request":{"urlPathTemplate":"/primitive/base64","method":"POST"},"response":{"status":200,"body":"\"SGVsbG8gd29ybGQh\"","headers":{"Content-Type":"application/json"}},"uuid":"04baa20d-d318-40e6-9784-c40158c16acd","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"fddd5aaf-ab0d-4ef6-949c-5d329f6d7eb2","name":"Put - default","request":{"urlPathTemplate":"/{id}","method":"PUT","pathParameters":{"id":{"equalTo":"id"}}},"response":{"status":200,"body":"{\n \"errors\": [\n {\n \"category\": \"API_ERROR\",\n \"code\": \"INTERNAL_SERVER_ERROR\",\n \"detail\": \"detail\",\n \"field\": \"field\"\n },\n {\n \"category\": \"API_ERROR\",\n \"code\": \"INTERNAL_SERVER_ERROR\",\n \"detail\": \"detail\",\n \"field\": \"field\"\n }\n ]\n}","headers":{"Content-Type":"application/json"}},"uuid":"fddd5aaf-ab0d-4ef6-949c-5d329f6d7eb2","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d29330f9-49ff-49c4-9081-7c83dbad315b","name":"getAndReturnUnion - default","request":{"urlPathTemplate":"/union","method":"POST"},"response":{"status":200,"body":"{\n \"animal\": \"dog\",\n \"name\": \"name\",\n \"likesToWoof\": true\n}","headers":{"Content-Type":"application/json"}},"uuid":"d29330f9-49ff-49c4-9081-7c83dbad315b","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"343f71f3-36ce-4684-b762-d60a086b43a4","name":"withMixedCase - default","request":{"urlPathTemplate":"/urls/MixedCase","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"343f71f3-36ce-4684-b762-d60a086b43a4","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"48f36314-b2b7-4910-9e1d-5b05f3346a60","name":"noEndingSlash - default","request":{"urlPathTemplate":"/urls/no-ending-slash","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"48f36314-b2b7-4910-9e1d-5b05f3346a60","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"f7b95029-2f25-4f70-8b4c-0855712747d8","name":"withEndingSlash - default","request":{"urlPathTemplate":"/urls/with-ending-slash/","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"f7b95029-2f25-4f70-8b4c-0855712747d8","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"2f9671a5-e6da-43a8-be27-d0be0191dcbf","name":"withUnderscores - default","request":{"urlPathTemplate":"/urls/with_underscores","method":"GET"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"2f9671a5-e6da-43a8-be27-d0be0191dcbf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"4a6d0aaa-05f2-47bb-9f40-6e3743b9d2bf","name":"postWithObjectBodyandResponse - default","request":{"urlPathTemplate":"/req-bodies/object","method":"POST"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"4a6d0aaa-05f2-47bb-9f40-6e3743b9d2bf","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"c6e7cc6c-b76f-4860-a9ad-52dc8f55b6e6","name":"postWithNoAuth - default","request":{"urlPathTemplate":"/no-auth","method":"POST"},"response":{"status":200,"body":"true","headers":{"Content-Type":"application/json"}},"uuid":"c6e7cc6c-b76f-4860-a9ad-52dc8f55b6e6","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"8098eeea-bc6b-4068-9601-566c2092f83f","name":"getWithNoRequestBody - default","request":{"urlPathTemplate":"/no-req-body","method":"GET"},"response":{"status":200,"body":"{\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}","headers":{"Content-Type":"application/json"}},"uuid":"8098eeea-bc6b-4068-9601-566c2092f83f","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}},"postServeActions":[]},{"id":"7dd9f944-1b35-42e9-a5ed-e48214ac8e91","name":"postWithNoRequestBody - default","request":{"urlPathTemplate":"/no-req-body","method":"POST"},"response":{"status":200,"body":"\"string\"","headers":{"Content-Type":"application/json"}},"uuid":"7dd9f944-1b35-42e9-a5ed-e48214ac8e91","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}},{"id":"d7b54168-aef4-4d68-a9c5-446e97dee2fc","name":"getWithCustomHeader - default","request":{"urlPathTemplate":"/test-headers/custom-header","method":"POST"},"response":{"status":200,"body":"\"\"","headers":{"Content-Type":"application/json"}},"uuid":"d7b54168-aef4-4d68-a9c5-446e97dee2fc","persistent":true,"priority":3,"metadata":{"mocklab":{"created":{"at":"2020-01-01T00:00:00.000Z","via":"SYSTEM"}}}}],"meta":{"total":53}} \ No newline at end of file From ce2ab7718f1415b0fad5730c7bda648e1b9afd15 Mon Sep 17 00:00:00 2001 From: Tanmay Singh Date: Tue, 24 Feb 2026 22:53:58 -0500 Subject: [PATCH 16/20] feat(internal): Add replay step to post-generation pipeline (#12559) * move GitHub self-hosted logic into generator-cli pipeline * fixes * forget * revamp conflicts subagent mahyem * claude.md * dont swallow errors * fix CI * address feedback * leftover issues * fix: remove remaining discriminatorContext references from openapi-ir consumers * fix: add discriminatorContext: undefined for required IR SDK field * fix: restore generator-cli tsconfig composite:false for tsup compatibility * fix: update test snapshots after discriminatorContext removal * fix: regenerate generators-yml schema and update ir-migration snapshot * revert: restore discriminatorContext to match main (not our change to make) * feat(cli): add replay v3.87.0 version entry * fix(internal): remove dead push-mode force push and document force usage - Remove skipCommit parameter from executePushMode (unreachable since PostGenerationPipeline disallows push mode + replay) - Change forcePush() from --force to --force-with-lease for PR branch pushes - Revert createAndPushTag() to --force (--force-with-lease breaks for tags because git tag -f overwrites the local ref first) - Add comments explaining why force is acceptable at each call site --- .vscode/settings.json | 2 +- .../generators-yml/definition/generators.yml | 6 + .../apis/generators-yml/definition/replay.yml | 6 + generators-yml.schema.json | 22 + .../api/adapter/LegacyFernWorkspaceAdapter.ts | 1 + packages/cli/cli/package.json | 1 + packages/cli/cli/src/cli.ts | 126 +- .../commands/generate/generateAPIWorkspace.ts | 10 +- .../generate/generateAPIWorkspaces.ts | 7 +- .../cli/cli/src/resolveGroupGithubConfig.ts | 63 + packages/cli/cli/versions.yml | 13 + .../convertGeneratorsConfiguration.ts | 3 +- .../generators-yml/GeneratorsConfiguration.ts | 1 + .../types/GeneratorsConfigurationSchema.ts | 5 + .../schemas/api/resources/index.ts | 2 + .../schemas/api/resources/replay/index.ts | 1 + .../replay/types/ReplayConfigSchema.ts | 6 + .../api/resources/replay/types/index.ts | 1 + .../types/GeneratorsConfigurationSchema.ts | 3 + .../schemas/serialization/resources/index.ts | 2 + .../serialization/resources/replay/index.ts | 1 + .../replay/types/ReplayConfigSchema.ts | 18 + .../resources/replay/types/index.ts | 1 + .../local-workspace-runner/package.json | 3 +- .../src/runLocalGenerationForWorkspace.ts | 359 +---- .../src/ast/visitGeneratorsYamlAst.ts | 1 + .../commons/github/src/ClonedRepository.ts | 60 + packages/generator-cli/build.mjs | 2 +- packages/generator-cli/package.json | 24 +- packages/generator-cli/src/api.ts | 22 + packages/generator-cli/src/cli.ts | 293 +++- .../src/pipeline/PipelineLogger.ts | 21 + .../src/pipeline/PostGenerationPipeline.ts | 91 ++ .../src/pipeline/github/constants.ts | 3 + .../src/pipeline/github/createReplayBranch.ts | 78 + .../github/findExistingUpdatablePR.ts | 116 ++ .../src/pipeline/github/index.ts | 9 + .../src/pipeline/github/parseCommitMessage.ts | 6 + .../pipeline/github/postConflictComments.ts | 129 ++ packages/generator-cli/src/pipeline/index.ts | 16 + .../src/pipeline/replay-summary.ts | 147 ++ .../src/pipeline/steps/BaseStep.ts | 13 + .../src/pipeline/steps/FernignoreStep.ts | 23 + .../src/pipeline/steps/GithubStep.ts | 587 +++++++ .../src/pipeline/steps/ReplayStep.ts | 84 + packages/generator-cli/src/pipeline/types.ts | 124 ++ .../generator-cli/src/replay/fernignore.ts | 37 + .../generator-cli/src/replay/replay-init.ts | 266 ++++ .../generator-cli/src/replay/replay-run.ts | 193 +++ pnpm-lock.yaml | 1402 +++++++++++++---- 50 files changed, 3737 insertions(+), 673 deletions(-) create mode 100644 fern/apis/generators-yml/definition/replay.yml create mode 100644 packages/cli/cli/src/resolveGroupGithubConfig.ts create mode 100644 packages/cli/configuration/src/generators-yml/schemas/api/resources/replay/index.ts create mode 100644 packages/cli/configuration/src/generators-yml/schemas/api/resources/replay/types/ReplayConfigSchema.ts create mode 100644 packages/cli/configuration/src/generators-yml/schemas/api/resources/replay/types/index.ts create mode 100644 packages/cli/configuration/src/generators-yml/schemas/serialization/resources/replay/index.ts create mode 100644 packages/cli/configuration/src/generators-yml/schemas/serialization/resources/replay/types/ReplayConfigSchema.ts create mode 100644 packages/cli/configuration/src/generators-yml/schemas/serialization/resources/replay/types/index.ts create mode 100644 packages/generator-cli/src/pipeline/PipelineLogger.ts create mode 100644 packages/generator-cli/src/pipeline/PostGenerationPipeline.ts create mode 100644 packages/generator-cli/src/pipeline/github/constants.ts create mode 100644 packages/generator-cli/src/pipeline/github/createReplayBranch.ts create mode 100644 packages/generator-cli/src/pipeline/github/findExistingUpdatablePR.ts create mode 100644 packages/generator-cli/src/pipeline/github/index.ts create mode 100644 packages/generator-cli/src/pipeline/github/parseCommitMessage.ts create mode 100644 packages/generator-cli/src/pipeline/github/postConflictComments.ts create mode 100644 packages/generator-cli/src/pipeline/index.ts create mode 100644 packages/generator-cli/src/pipeline/replay-summary.ts create mode 100644 packages/generator-cli/src/pipeline/steps/BaseStep.ts create mode 100644 packages/generator-cli/src/pipeline/steps/FernignoreStep.ts create mode 100644 packages/generator-cli/src/pipeline/steps/GithubStep.ts create mode 100644 packages/generator-cli/src/pipeline/steps/ReplayStep.ts create mode 100644 packages/generator-cli/src/pipeline/types.ts create mode 100644 packages/generator-cli/src/replay/fernignore.ts create mode 100644 packages/generator-cli/src/replay/replay-init.ts create mode 100644 packages/generator-cli/src/replay/replay-run.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index ff99742d8e6c..901a988a537f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -129,7 +129,7 @@ "typescript.disableAutomaticTypeAcquisition": true, "typescript.updateImportsOnFileMove.enabled": "never", "javascript.updateImportsOnFileMove.enabled": "never", - "extensions.ignoreRecommendations": false, + "extensions.ignoreRecommendations": true, "git.ignoreLimitWarning": true, "git.branchProtection": ["main", "master"] } diff --git a/fern/apis/generators-yml/definition/generators.yml b/fern/apis/generators-yml/definition/generators.yml index 99d0ff156ff1..cdb42ffa728d 100644 --- a/fern/apis/generators-yml/definition/generators.yml +++ b/fern/apis/generators-yml/definition/generators.yml @@ -4,6 +4,7 @@ imports: file: ./fernDefinition/file.yml environments: ./fernDefinition/environments.yml group: ./group.yml + replay: ./replay.yml reviewers: ./reviewers.yml types: GeneratorsConfigurationSchema: @@ -36,6 +37,11 @@ types: type: optional> reviewers: type: optional + replay: + type: optional + docs: | + Configuration for SDK customization replay. + Automatically preserves user customizations across SDK regenerations. ai: optional autorelease: type: optional diff --git a/fern/apis/generators-yml/definition/replay.yml b/fern/apis/generators-yml/definition/replay.yml new file mode 100644 index 000000000000..5c2fe595a735 --- /dev/null +++ b/fern/apis/generators-yml/definition/replay.yml @@ -0,0 +1,6 @@ +types: + ReplayConfigSchema: + properties: + enabled: + type: boolean + docs: Whether to enable replay for this SDK diff --git a/generators-yml.schema.json b/generators-yml.schema.json index acceda5a8a3f..eed26dbb2031 100644 --- a/generators-yml.schema.json +++ b/generators-yml.schema.json @@ -103,6 +103,16 @@ } ] }, + "replay": { + "oneOf": [ + { + "$ref": "#/definitions/replay.ReplayConfigSchema" + }, + { + "type": "null" + } + ] + }, "ai": { "oneOf": [ { @@ -4139,6 +4149,18 @@ ], "additionalProperties": false }, + "replay.ReplayConfigSchema": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": [ + "enabled" + ], + "additionalProperties": false + }, "ai.ModelProvider": { "type": "string", "enum": [ diff --git a/packages/cli/cli-v2/src/api/adapter/LegacyFernWorkspaceAdapter.ts b/packages/cli/cli-v2/src/api/adapter/LegacyFernWorkspaceAdapter.ts index 7fec40f07aad..e83b32c5af26 100644 --- a/packages/cli/cli-v2/src/api/adapter/LegacyFernWorkspaceAdapter.ts +++ b/packages/cli/cli-v2/src/api/adapter/LegacyFernWorkspaceAdapter.ts @@ -146,6 +146,7 @@ export class LegacyFernWorkspaceAdapter { groups: [], whitelabel: undefined, ai: undefined, + replay: undefined, rawConfiguration: {} }; } diff --git a/packages/cli/cli/package.json b/packages/cli/cli/package.json index bd1dd74416d9..8d61b0798d62 100644 --- a/packages/cli/cli/package.json +++ b/packages/cli/cli/package.json @@ -70,6 +70,7 @@ "@fern-api/fern-definition-schema": "workspace:*", "@fern-api/fern-definition-validator": "workspace:*", "@fern-api/fs-utils": "workspace:*", + "@fern-api/generator-cli": "workspace:*", "@fern-api/generators-validator": "workspace:*", "@fern-api/init": "workspace:*", "@fern-api/ir-generator": "workspace:*", diff --git a/packages/cli/cli/src/cli.ts b/packages/cli/cli/src/cli.ts index 27a7814f6e71..ad0adf840a16 100644 --- a/packages/cli/cli/src/cli.ts +++ b/packages/cli/cli/src/cli.ts @@ -14,6 +14,7 @@ import { } from "@fern-api/configuration-loader"; import { ContainerRunner, haveSameNullishness, undefinedIfNullish, undefinedIfSomeNullish } from "@fern-api/core-utils"; import { AbsoluteFilePath, cwd, doesPathExist, isURL, resolve } from "@fern-api/fs-utils"; +import { formatBootstrapSummary, replayInit } from "@fern-api/generator-cli"; import { initializeAPI, initializeDocs, @@ -70,6 +71,7 @@ import { writeDocsDefinitionForProject } from "./commands/write-docs-definition/ import { writeTranslationForProject } from "./commands/write-translation/writeTranslationForProject.js"; import { FERN_CWD_ENV_VAR } from "./cwd.js"; import { rerunFernCliAtVersion } from "./rerunFernCliAtVersion.js"; +import { resolveGroupGithubConfig } from "./resolveGroupGithubConfig.js"; import { RUNTIME } from "./runtime.js"; void runCli(); @@ -227,6 +229,7 @@ async function tryRunCli(cliContext: CliContext) { addWriteDocsDefinitionCommand(cli, cliContext); addWriteTranslationCommand(cli, cliContext); addExportCommand(cli, cliContext); + addReplayCommand(cli, cliContext); addBetaCommand(cli, cliContext); // CLI V2 Sanctioned Commands @@ -680,6 +683,12 @@ function addGenerateCommand(cli: Argv, cliContext: CliContext) .option("output", { type: "string", description: "Custom output directory (currently only supported with --preview for SDK generation)" + }) + .option("replay", { + boolean: true, + default: true, + hidden: true, + description: "Run replay after generation (use --no-replay to skip)" }), async (argv) => { if (argv.api != null && argv.docs != null) { @@ -740,7 +749,8 @@ function addGenerateCommand(cli: Argv, cliContext: CliContext) lfsOverride: argv.lfsOverride, fernignorePath: argv.fernignore, dynamicIrOnly: argv["dynamic-ir-only"], - outputDir: argv.output + outputDir: argv.output, + noReplay: !argv.replay }); } if (argv.docs != null) { @@ -793,7 +803,8 @@ function addGenerateCommand(cli: Argv, cliContext: CliContext) lfsOverride: argv.lfsOverride, fernignorePath: argv.fernignore, dynamicIrOnly: argv["dynamic-ir-only"], - outputDir: argv.output + outputDir: argv.output, + noReplay: !argv.replay }); } ); @@ -2023,3 +2034,114 @@ function writeBytes(stream: WriteStream, data: Uint8Array): Promise { }); }); } + +function addReplayCommand(cli: Argv, cliContext: CliContext) { + cli.command({ + command: "replay", + describe: false, // hidden from --help + builder: (yargs) => { + addReplayInitCommand(yargs, cliContext); + return yargs; + }, + handler: () => { + // parent command — subcommands handle execution + } + }); +} + +function addReplayInitCommand(cli: Argv, cliContext: CliContext) { + cli.command( + "init", + false, // hidden from --help + (yargs) => + yargs + .option("group", { + type: "string", + description: "Generator group from generators.yml (reads github config automatically)" + }) + .option("api", { + type: "string", + description: "If multiple APIs, specify which API workspace to use" + }) + .option("github", { + type: "string", + description: "GitHub repository (e.g., owner/repo). Overrides --group config." + }) + .option("token", { + type: "string", + description: "GitHub token. Overrides --group config." + }) + .option("dry-run", { + type: "boolean", + default: false, + description: "Report what would happen without making changes" + }) + .option("max-commits", { + type: "number", + description: "Max commits to scan for generation history" + }) + .option("force", { + type: "boolean", + default: false, + description: "Overwrite existing lockfile if Replay is already initialized" + }), + async (argv) => { + await cliContext.instrumentPostHogEvent({ + command: "fern replay init" + }); + + let githubRepo: string | undefined = argv.github; + let token: string | undefined = argv.token; + + // If --group is provided, load config from generators.yml + if (argv.group != null) { + const resolved = await resolveGroupGithubConfig(cliContext, argv.group, argv.api); + // Use group config as defaults, allow --github/--token to override + githubRepo = githubRepo ?? resolved.githubRepo; + token = token ?? resolved.token; + } + + if (githubRepo == null || token == null) { + return cliContext.failAndThrow( + "Missing required github config. Either use --group to read from generators.yml, or provide --github and --token directly." + ); + } + + cliContext.logger.info(`Initializing Replay for: ${githubRepo}`); + if (argv.dryRun) { + cliContext.logger.info("(dry-run mode)"); + } + + try { + const result = await replayInit({ + githubRepo, + token, + dryRun: argv.dryRun, + maxCommitsToScan: argv.maxCommits, + force: argv.force + }); + + const logEntries = formatBootstrapSummary(result); + for (const entry of logEntries) { + if (entry.level === "warn") { + cliContext.logger.warn(entry.message); + } else { + cliContext.logger.info(entry.message); + } + } + + if (!result.bootstrap.generationCommit) { + return; + } + + if (argv.dryRun) { + cliContext.logger.info("\nDry run complete. No changes made."); + } + } catch (error) { + cliContext.failAndThrow( + `Failed to initialize Replay: ${error instanceof Error ? error.message : String(error)}` + ); + } + } + ); +} diff --git a/packages/cli/cli/src/commands/generate/generateAPIWorkspace.ts b/packages/cli/cli/src/commands/generate/generateAPIWorkspace.ts index 201794cb3d6f..799571c64306 100644 --- a/packages/cli/cli/src/commands/generate/generateAPIWorkspace.ts +++ b/packages/cli/cli/src/commands/generate/generateAPIWorkspace.ts @@ -36,7 +36,8 @@ export async function generateWorkspace({ inspect, lfsOverride, fernignorePath, - dynamicIrOnly + dynamicIrOnly, + noReplay }: { organization: string; workspace: AbstractAPIWorkspace; @@ -56,6 +57,7 @@ export async function generateWorkspace({ lfsOverride: string | undefined; fernignorePath: string | undefined; dynamicIrOnly: boolean; + noReplay: boolean; }): Promise { if (workspace.generatorsConfiguration == null) { context.logger.warn("This workspaces has no generators.yml"); @@ -82,7 +84,7 @@ export async function generateWorkspace({ context ); - const { ai } = workspace.generatorsConfiguration; + const { ai, replay } = workspace.generatorsConfiguration; // Pre-check token for remote generation before starting any work if (!useLocalDocker && !token) { @@ -141,7 +143,9 @@ export async function generateWorkspace({ runner, absolutePathToPreview, inspect, - ai + ai, + replay, + noReplay }); } else if (token != null) { await runRemoteGenerationForAPIWorkspace({ diff --git a/packages/cli/cli/src/commands/generate/generateAPIWorkspaces.ts b/packages/cli/cli/src/commands/generate/generateAPIWorkspaces.ts index 7e8dcd95e613..502d386b70c7 100644 --- a/packages/cli/cli/src/commands/generate/generateAPIWorkspaces.ts +++ b/packages/cli/cli/src/commands/generate/generateAPIWorkspaces.ts @@ -33,7 +33,8 @@ export async function generateAPIWorkspaces({ lfsOverride, fernignorePath, dynamicIrOnly, - outputDir + outputDir, + noReplay }: { project: Project; cliContext: CliContext; @@ -52,6 +53,7 @@ export async function generateAPIWorkspaces({ fernignorePath: string | undefined; dynamicIrOnly: boolean; outputDir: string | undefined; + noReplay: boolean; }): Promise { let token: FernToken | undefined = undefined; @@ -147,7 +149,8 @@ export async function generateAPIWorkspaces({ inspect, lfsOverride, fernignorePath, - dynamicIrOnly + dynamicIrOnly, + noReplay }); }); }) diff --git a/packages/cli/cli/src/resolveGroupGithubConfig.ts b/packages/cli/cli/src/resolveGroupGithubConfig.ts new file mode 100644 index 000000000000..1b1893aafc0f --- /dev/null +++ b/packages/cli/cli/src/resolveGroupGithubConfig.ts @@ -0,0 +1,63 @@ +import { isGithubSelfhosted } from "@fern-api/configuration-loader"; +import { replaceEnvVariables } from "@fern-api/core-utils"; +import { CliContext } from "./cli-context/CliContext.js"; +import { loadProjectAndRegisterWorkspacesWithContext } from "./cliCommons.js"; + +export interface ResolvedGithubConfig { + githubRepo: string; + token: string; + mode: "push" | "pull-request"; + branch: string | undefined; +} + +export async function resolveGroupGithubConfig( + cliContext: CliContext, + groupName: string, + apiWorkspace: string | undefined +): Promise { + const project = await loadProjectAndRegisterWorkspacesWithContext(cliContext, { + commandLineApiWorkspace: apiWorkspace, + defaultToAllApiWorkspaces: false + }); + + const workspace = project.apiWorkspaces[0]; + if (workspace == null) { + return cliContext.failAndThrow("No API workspace found."); + } + + const generatorsConfig = workspace.generatorsConfiguration; + if (generatorsConfig == null) { + return cliContext.failAndThrow("No generators.yml found in workspace."); + } + + const group = generatorsConfig.groups.find((g) => g.groupName === groupName); + if (group == null) { + const available = generatorsConfig.groups.map((g) => g.groupName).join(", "); + return cliContext.failAndThrow(`Group "${groupName}" not found. Available groups: ${available}`); + } + + const generatorWithGithub = group.generators.find((g) => g.raw?.github != null && isGithubSelfhosted(g.raw.github)); + + if (generatorWithGithub?.raw?.github == null || !isGithubSelfhosted(generatorWithGithub.raw.github)) { + return cliContext.failAndThrow( + `No generator in group "${groupName}" has a self-hosted github configuration (uri + token).` + ); + } + + const resolveEnv = (value: T): T => + replaceEnvVariables(value, { + onError: (message) => cliContext.failAndThrow(message) + }); + const githubConfig = resolveEnv(generatorWithGithub.raw.github); + + cliContext.logger.info( + `Using github config from group "${groupName}" generator "${generatorWithGithub.name}" (mode: ${githubConfig.mode ?? "pull-request"})` + ); + + return { + githubRepo: githubConfig.uri, + token: githubConfig.token, + mode: githubConfig.mode === "push" ? "push" : "pull-request", + branch: githubConfig.branch + }; +} diff --git a/packages/cli/cli/versions.yml b/packages/cli/cli/versions.yml index d6d7a05d9352..b1d2d2a30fa0 100644 --- a/packages/cli/cli/versions.yml +++ b/packages/cli/cli/versions.yml @@ -1,4 +1,17 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 3.88.0 + changelogEntry: + - summary: | + Add Replay support for preserving SDK customizations across regenerations. + Replay detects user edits via `.fern/replay.lock`, applies them with 3-way + merge, and creates PRs with conflict resolution guidance when needed. + + New CLI commands: `fern replay init`, `fern replay status`, `fern replay forget`, + `fern replay reset`. New `--no-replay` flag on `fern generate` to skip patch + application. Gated behind `replay: { enabled: true }` in `generators.yml`. + type: feat + createdAt: "2026-02-24" + irVersion: 65 - version: 3.87.0 changelogEntry: diff --git a/packages/cli/configuration-loader/src/generators-yml/convertGeneratorsConfiguration.ts b/packages/cli/configuration-loader/src/generators-yml/convertGeneratorsConfiguration.ts index 6588849b9e40..702a404b39d6 100644 --- a/packages/cli/configuration-loader/src/generators-yml/convertGeneratorsConfiguration.ts +++ b/packages/cli/configuration-loader/src/generators-yml/convertGeneratorsConfiguration.ts @@ -94,7 +94,8 @@ export async function convertGeneratorsConfiguration({ github: rawGeneratorsConfiguration.whitelabel.github } : undefined, - ai: rawGeneratorsConfiguration.ai + ai: rawGeneratorsConfiguration.ai, + replay: rawGeneratorsConfiguration.replay }; } diff --git a/packages/cli/configuration/src/generators-yml/GeneratorsConfiguration.ts b/packages/cli/configuration/src/generators-yml/GeneratorsConfiguration.ts index a44963d4adab..04ee324acf26 100644 --- a/packages/cli/configuration/src/generators-yml/GeneratorsConfiguration.ts +++ b/packages/cli/configuration/src/generators-yml/GeneratorsConfiguration.ts @@ -27,6 +27,7 @@ export interface GeneratorsConfiguration { groups: GeneratorGroup[]; whitelabel: FernFiddle.WhitelabelConfig | undefined; ai: generatorsYml.AiServicesSchema | undefined; + replay: generatorsYml.ReplayConfigSchema | undefined; rawConfiguration: GeneratorsConfigurationSchema; absolutePathToConfiguration: AbsoluteFilePath; diff --git a/packages/cli/configuration/src/generators-yml/schemas/api/resources/generators/types/GeneratorsConfigurationSchema.ts b/packages/cli/configuration/src/generators-yml/schemas/api/resources/generators/types/GeneratorsConfigurationSchema.ts index 1976846756e9..4f23a19b29f0 100644 --- a/packages/cli/configuration/src/generators-yml/schemas/api/resources/generators/types/GeneratorsConfigurationSchema.ts +++ b/packages/cli/configuration/src/generators-yml/schemas/api/resources/generators/types/GeneratorsConfigurationSchema.ts @@ -21,6 +21,11 @@ export interface GeneratorsConfigurationSchema { aliases?: Record; groups?: Record; reviewers?: GeneratorsYml.ReviewersSchema; + /** + * Configuration for SDK customization replay. + * Automatically preserves user customizations across SDK regenerations. + */ + replay?: GeneratorsYml.ReplayConfigSchema; ai?: GeneratorsYml.AiServicesSchema; /** * If true, automatically release SDKs when changes are detected. diff --git a/packages/cli/configuration/src/generators-yml/schemas/api/resources/index.ts b/packages/cli/configuration/src/generators-yml/schemas/api/resources/index.ts index 340699ff6c0d..286490264a61 100644 --- a/packages/cli/configuration/src/generators-yml/schemas/api/resources/index.ts +++ b/packages/cli/configuration/src/generators-yml/schemas/api/resources/index.ts @@ -7,5 +7,7 @@ export * as group from "./group/index.js"; export * from "./group/types/index.js"; export * as license from "./license/index.js"; export * from "./license/types/index.js"; +export * as replay from "./replay/index.js"; +export * from "./replay/types/index.js"; export * as reviewers from "./reviewers/index.js"; export * from "./reviewers/types/index.js"; diff --git a/packages/cli/configuration/src/generators-yml/schemas/api/resources/replay/index.ts b/packages/cli/configuration/src/generators-yml/schemas/api/resources/replay/index.ts new file mode 100644 index 000000000000..2f88e3015854 --- /dev/null +++ b/packages/cli/configuration/src/generators-yml/schemas/api/resources/replay/index.ts @@ -0,0 +1 @@ +export * from "./types/index.js"; diff --git a/packages/cli/configuration/src/generators-yml/schemas/api/resources/replay/types/ReplayConfigSchema.ts b/packages/cli/configuration/src/generators-yml/schemas/api/resources/replay/types/ReplayConfigSchema.ts new file mode 100644 index 000000000000..974c8a8ba0d2 --- /dev/null +++ b/packages/cli/configuration/src/generators-yml/schemas/api/resources/replay/types/ReplayConfigSchema.ts @@ -0,0 +1,6 @@ +// This file was auto-generated by Fern from our API Definition. + +export interface ReplayConfigSchema { + /** Whether to enable replay for this SDK */ + enabled: boolean; +} diff --git a/packages/cli/configuration/src/generators-yml/schemas/api/resources/replay/types/index.ts b/packages/cli/configuration/src/generators-yml/schemas/api/resources/replay/types/index.ts new file mode 100644 index 000000000000..4ccaad34a1fa --- /dev/null +++ b/packages/cli/configuration/src/generators-yml/schemas/api/resources/replay/types/index.ts @@ -0,0 +1 @@ +export * from "./ReplayConfigSchema.js"; diff --git a/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/generators/types/GeneratorsConfigurationSchema.ts b/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/generators/types/GeneratorsConfigurationSchema.ts index a70f42c19072..2cbc3a3ad053 100644 --- a/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/generators/types/GeneratorsConfigurationSchema.ts +++ b/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/generators/types/GeneratorsConfigurationSchema.ts @@ -6,6 +6,7 @@ import type * as serializers from "../../../index.js"; import { AiServicesSchema } from "../../ai/types/AiServicesSchema.js"; import { AuthSchemeDeclarationSchema } from "../../fernDefinition/resources/auth/types/AuthSchemeDeclarationSchema.js"; import { GeneratorGroupSchema } from "../../group/types/GeneratorGroupSchema.js"; +import { ReplayConfigSchema } from "../../replay/types/ReplayConfigSchema.js"; import { ReviewersSchema } from "../../reviewers/types/ReviewersSchema.js"; import { ApiConfigurationSchema } from "./ApiConfigurationSchema.js"; import { ApiDefinitionSettingsSchema } from "./ApiDefinitionSettingsSchema.js"; @@ -29,6 +30,7 @@ export const GeneratorsConfigurationSchema: core.serialization.ObjectSchema< .optional(), groups: core.serialization.record(core.serialization.string(), GeneratorGroupSchema).optional(), reviewers: ReviewersSchema.optional(), + replay: ReplayConfigSchema.optional(), ai: AiServicesSchema.optional(), autorelease: core.serialization.boolean().optional(), openapi: GeneratorsOpenApiSchema.optional(), @@ -49,6 +51,7 @@ export declare namespace GeneratorsConfigurationSchema { aliases?: Record | null; groups?: Record | null; reviewers?: ReviewersSchema.Raw | null; + replay?: ReplayConfigSchema.Raw | null; ai?: AiServicesSchema.Raw | null; autorelease?: boolean | null; openapi?: GeneratorsOpenApiSchema.Raw | null; diff --git a/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/index.ts b/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/index.ts index 340699ff6c0d..286490264a61 100644 --- a/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/index.ts +++ b/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/index.ts @@ -7,5 +7,7 @@ export * as group from "./group/index.js"; export * from "./group/types/index.js"; export * as license from "./license/index.js"; export * from "./license/types/index.js"; +export * as replay from "./replay/index.js"; +export * from "./replay/types/index.js"; export * as reviewers from "./reviewers/index.js"; export * from "./reviewers/types/index.js"; diff --git a/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/replay/index.ts b/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/replay/index.ts new file mode 100644 index 000000000000..2f88e3015854 --- /dev/null +++ b/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/replay/index.ts @@ -0,0 +1 @@ +export * from "./types/index.js"; diff --git a/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/replay/types/ReplayConfigSchema.ts b/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/replay/types/ReplayConfigSchema.ts new file mode 100644 index 000000000000..61cb65e4b9c1 --- /dev/null +++ b/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/replay/types/ReplayConfigSchema.ts @@ -0,0 +1,18 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as GeneratorsYml from "../../../../api/index.js"; +import * as core from "../../../../core/index.js"; +import type * as serializers from "../../../index.js"; + +export const ReplayConfigSchema: core.serialization.ObjectSchema< + serializers.ReplayConfigSchema.Raw, + GeneratorsYml.ReplayConfigSchema +> = core.serialization.object({ + enabled: core.serialization.boolean(), +}); + +export declare namespace ReplayConfigSchema { + export interface Raw { + enabled: boolean; + } +} diff --git a/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/replay/types/index.ts b/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/replay/types/index.ts new file mode 100644 index 000000000000..4ccaad34a1fa --- /dev/null +++ b/packages/cli/configuration/src/generators-yml/schemas/serialization/resources/replay/types/index.ts @@ -0,0 +1 @@ +export * from "./ReplayConfigSchema.js"; diff --git a/packages/cli/generation/local-generation/local-workspace-runner/package.json b/packages/cli/generation/local-generation/local-workspace-runner/package.json index f71b1e1e1ade..992ef43f5b2d 100644 --- a/packages/cli/generation/local-generation/local-workspace-runner/package.json +++ b/packages/cli/generation/local-generation/local-workspace-runner/package.json @@ -45,6 +45,7 @@ "@fern-api/csharp-dynamic-snippets": "workspace:*", "@fern-api/docker-utils": "workspace:*", "@fern-api/fs-utils": "workspace:*", + "@fern-api/generator-cli": "workspace:*", "@fern-api/github": "workspace:*", "@fern-api/go-dynamic-snippets": "workspace:*", "@fern-api/ir-generator": "workspace:*", @@ -55,6 +56,7 @@ "@fern-api/php-dynamic-snippets": "workspace:*", "@fern-api/python-dynamic-snippets": "workspace:*", "@fern-api/remote-workspace-runner": "workspace:*", + "@fern-api/ruby-dynamic-snippets": "workspace:*", "@fern-api/rust-dynamic-snippets": "workspace:*", "@fern-api/swift-dynamic-snippets": "workspace:*", @@ -64,7 +66,6 @@ "@fern-api/workspace-loader": "workspace:*", "@fern-fern/fiddle-sdk": "catalog:", "@fern-fern/generator-exec-sdk": "catalog:", - "@octokit/rest": "catalog:", "chalk": "catalog:", "decompress": "catalog:", "semver": "^7.6.3", diff --git a/packages/cli/generation/local-generation/local-workspace-runner/src/runLocalGenerationForWorkspace.ts b/packages/cli/generation/local-generation/local-workspace-runner/src/runLocalGenerationForWorkspace.ts index cf2bc3fa1397..f81a4216a987 100644 --- a/packages/cli/generation/local-generation/local-workspace-runner/src/runLocalGenerationForWorkspace.ts +++ b/packages/cli/generation/local-generation/local-workspace-runner/src/runLocalGenerationForWorkspace.ts @@ -3,9 +3,10 @@ import { FernToken, getAccessToken } from "@fern-api/auth"; import { SourceResolverImpl } from "@fern-api/cli-source-resolver"; import { fernConfigJson, GeneratorInvocation, generatorsYml } from "@fern-api/configuration"; import { createVenusService } from "@fern-api/core"; -import { assertNever, ContainerRunner, replaceEnvVariables } from "@fern-api/core-utils"; +import { ContainerRunner, replaceEnvVariables } from "@fern-api/core-utils"; import { AbsoluteFilePath, join, RelativeFilePath } from "@fern-api/fs-utils"; -import { ClonedRepository, cloneRepository, parseRepository } from "@fern-api/github"; +import { logReplaySummary, type PipelineLogger, PostGenerationPipeline } from "@fern-api/generator-cli"; +import { cloneRepository, parseRepository } from "@fern-api/github"; import { generateIntermediateRepresentation } from "@fern-api/ir-generator"; import { FernIr, PublishTarget } from "@fern-api/ir-sdk"; import { getDynamicGeneratorConfig } from "@fern-api/remote-workspace-runner"; @@ -15,7 +16,6 @@ import { AbstractAPIWorkspace, getBaseOpenAPIWorkspaceSettingsFromGeneratorInvocation } from "@fern-api/workspace-loader"; -import { Octokit } from "@octokit/rest"; import chalk from "chalk"; import * as fs from "fs/promises"; import os from "os"; @@ -35,7 +35,9 @@ export async function runLocalGenerationForWorkspace({ context, absolutePathToPreview, runner, - ai + ai, + replay, + noReplay }: { token: FernToken | undefined; projectConfig: fernConfigJson.ProjectConfig; @@ -48,6 +50,8 @@ export async function runLocalGenerationForWorkspace({ runner: ContainerRunner | undefined; inspect: boolean; ai: generatorsYml.AiServicesSchema | undefined; + replay?: generatorsYml.ReplayConfigSchema | undefined; + noReplay?: boolean; }): Promise { const results = await Promise.all( generatorGroup.generators.map(async (generatorInvocation) => { @@ -279,14 +283,55 @@ export async function runLocalGenerationForWorkspace({ interactiveTaskContext.logger.info(chalk.green("Wrote files to " + absolutePathToLocalOutput)); + // Run post-generation pipeline (replay + GitHub) when outputting to a self-hosted GitHub repo if (selfhostedGithubConfig != null && shouldCommit) { - await postProcessGithubSelfHosted( - interactiveTaskContext, - selfhostedGithubConfig, - absolutePathToLocalOutput, - autoVersioningCommitMessage, - generatorInvocation.name + const pipelineLogger: PipelineLogger = { + debug: (msg) => interactiveTaskContext.logger.debug(msg), + info: (msg) => interactiveTaskContext.logger.info(msg), + warn: (msg) => interactiveTaskContext.logger.warn(msg), + error: (msg) => interactiveTaskContext.logger.error(msg) + }; + + const pipeline = new PostGenerationPipeline( + { + outputDir: absolutePathToLocalOutput, + replay: { enabled: replay?.enabled === true, skipApplication: noReplay, stageOnly: false }, + github: { + enabled: true, + uri: selfhostedGithubConfig.uri, + token: selfhostedGithubConfig.token, + mode: selfhostedGithubConfig.mode ?? "push", + branch: selfhostedGithubConfig.branch, + commitMessage: autoVersioningCommitMessage, + previewMode: selfhostedGithubConfig.previewMode, + generatorName: generatorInvocation.name + }, + cliVersion: workspace.cliVersion ?? "unknown", + generatorVersions: { + [generatorInvocation.name]: generatorInvocation.version + }, + generatorName: generatorInvocation.name + }, + pipelineLogger ); + + const pipelineResult = await pipeline.run(); + + // Log replay summary + if (pipelineResult.steps.replay != null) { + logReplaySummary(pipelineResult.steps.replay, { + debug: (msg) => interactiveTaskContext.logger.debug(msg), + info: (msg) => interactiveTaskContext.logger.info(chalk.cyan(msg)), + warn: (msg) => interactiveTaskContext.logger.warn(chalk.yellow(msg)), + error: (msg) => interactiveTaskContext.logger.error(chalk.red(msg)) + }); + } + + if (!pipelineResult.success) { + interactiveTaskContext.failAndThrow( + `Post-generation pipeline failed: ${pipelineResult.errors?.join(", ")}` + ); + } } }); }) @@ -340,300 +385,6 @@ function resolveAbsolutePathToLocalPreview( return absolutePathToPreview ? join(absolutePathToPreview, RelativeFilePath.of(subfolderName)) : undefined; } -function parseCommitMessageForPR(commitMessage: string): { prTitle: string; prBody: string } { - const lines = commitMessage.split("\n"); - const prTitle = lines[0]?.trim() || "SDK Generation"; - const prBody = lines.slice(1).join("\n").trim() || "Automated SDK generation by Fern"; - return { prTitle, prBody }; -} - -const FERN_BOT_NAME = "fern-api"; -const FERN_BOT_EMAIL = "115122769+fern-api[bot]@users.noreply.github.com"; -const FERN_BOT_LOGIN = "fern-api[bot]"; - -interface ExistingPullRequest { - number: number; - headBranch: string; - htmlUrl: string; -} - -async function findExistingUpdatablePR( - octokit: Octokit, - owner: string, - repo: string, - baseBranch: string, - context: TaskContext, - branchPrefix: string -): Promise { - try { - const { data: pulls } = await octokit.pulls.list({ - owner, - repo, - state: "open", - base: baseBranch, - sort: "updated", - direction: "desc", - per_page: 20 - }); - - for (const pr of pulls) { - const prAuthor = pr.user?.login; - if (prAuthor !== FERN_BOT_LOGIN) { - context.logger.debug(`PR #${pr.number} skipped: author ${prAuthor} is not ${FERN_BOT_LOGIN}`); - continue; - } - - if (!pr.head.ref.startsWith(branchPrefix)) { - context.logger.debug( - `PR #${pr.number} skipped: branch ${pr.head.ref} does not start with ${branchPrefix}` - ); - continue; - } - - const hasOnlyGenerationCommits = await checkPRHasOnlyGenerationCommits( - octokit, - owner, - repo, - pr.number, - context - ); - - if (hasOnlyGenerationCommits) { - context.logger.debug(`Found existing updatable PR #${pr.number} with branch ${pr.head.ref}`); - return { - number: pr.number, - headBranch: pr.head.ref, - htmlUrl: pr.html_url - }; - } else { - context.logger.debug(`PR #${pr.number} skipped: contains non-generation commits`); - } - } - - return undefined; - } catch (error) { - context.logger.debug(`Error finding existing PRs: ${error instanceof Error ? error.message : String(error)}`); - return undefined; - } -} - -async function checkPRHasOnlyGenerationCommits( - octokit: Octokit, - owner: string, - repo: string, - pullNumber: number, - context: TaskContext -): Promise { - try { - const { data: commits } = await octokit.pulls.listCommits({ - owner, - repo, - pull_number: pullNumber, - per_page: 100 - }); - - for (const commit of commits) { - const authorLogin = commit.author?.login; - const authorEmail = commit.commit.author?.email; - - const isGenerationCommit = - authorLogin === FERN_BOT_LOGIN || - authorEmail === FERN_BOT_EMAIL || - commit.commit.author?.name === FERN_BOT_NAME; - - if (!isGenerationCommit) { - context.logger.debug( - `Commit ${commit.sha.substring(0, 7)} is not a generation commit: ` + - `author=${authorLogin}, email=${authorEmail}` - ); - return false; - } - } - - return true; - } catch (error) { - context.logger.debug(`Error checking PR commits: ${error instanceof Error ? error.message : String(error)}`); - return false; - } -} - -function sanitizeGeneratorNameForBranch(generatorName: string): string { - return generatorName.replace(/\//g, "-"); -} - -async function postProcessGithubSelfHosted( - context: TaskContext, - selfhostedGithubConfig: SelhostedGithubConfig, - absolutePathToLocalOutput: AbsoluteFilePath, - commitMessage?: string, - generatorName?: string -): Promise { - try { - context.logger.debug("Starting GitHub self-hosted flow in directory: " + absolutePathToLocalOutput); - const repository = ClonedRepository.createAtPath(absolutePathToLocalOutput); - const now = new Date(); - const formattedDate = now.toISOString().replace("T", "_").replace(/:/g, "-").replace("Z", "").replace(".", "_"); - const sanitizedName = generatorName != null ? sanitizeGeneratorNameForBranch(generatorName) : undefined; - const branchPrefix = sanitizedName != null ? `fern-bot/${sanitizedName}/` : "fern-bot/"; - const newPrBranch = `${branchPrefix}${formattedDate}`; - // Ensure git commits are attributed to a bot user so pushes/PRs have a consistent author. - try { - // Use repository helper to set git user/email if available - await repository.setUserAndEmail({ - name: FERN_BOT_NAME, - email: FERN_BOT_EMAIL - }); - } catch (_other) { - // pass - } - - const mode = selfhostedGithubConfig.mode ?? "push"; - switch (mode) { - case "pull-request": { - const baseBranch = selfhostedGithubConfig.branch ?? (await repository.getDefaultBranch()); - - const octokit = new Octokit({ - auth: selfhostedGithubConfig.token - }); - const parsedRepo = parseRepository(selfhostedGithubConfig.uri); - const { owner, repo } = parsedRepo; - - const existingPR = await findExistingUpdatablePR( - octokit, - owner, - repo, - baseBranch, - context, - branchPrefix - ); - - let prBranch: string; - let isUpdatingExistingPR = false; - - if (existingPR != null) { - context.logger.info( - `Found existing updatable PR #${existingPR.number}, will update branch ${existingPR.headBranch}` - ); - prBranch = existingPR.headBranch; - isUpdatingExistingPR = true; - await repository.checkoutRemoteBranch(prBranch); - } else { - context.logger.debug(`No existing updatable PR found, creating new branch ${newPrBranch}`); - prBranch = newPrBranch; - await repository.checkout(prBranch); - } - - context.logger.debug("Checking for .fernignore file..."); - const fernignorePath = join(absolutePathToLocalOutput, RelativeFilePath.of(".fernignore")); - try { - await fs.access(fernignorePath); - context.logger.debug(".fernignore already exists"); - } catch { - context.logger.debug("Creating .fernignore file..."); - await fs.writeFile(fernignorePath, "# Specify files that shouldn't be modified by Fern\n", "utf-8"); - } - - context.logger.debug("Committing changes..."); - const finalCommitMessage = commitMessage ?? "SDK Generation"; - await repository.commitAllChanges(finalCommitMessage); - context.logger.debug( - `Committed changes to local copy of GitHub repository at ${absolutePathToLocalOutput}` - ); - - if (!selfhostedGithubConfig.previewMode) { - await repository.push(); - const pushedBranch = await repository.getCurrentBranch(); - context.logger.info( - `Pushed branch: https://github.com/${selfhostedGithubConfig.uri}/tree/${pushedBranch}` - ); - } - - if (isUpdatingExistingPR && existingPR != null) { - context.logger.info(`Updated existing pull request: ${existingPR.htmlUrl}`); - - const { prTitle, prBody } = parseCommitMessageForPR(finalCommitMessage); - try { - await octokit.pulls.update({ - owner, - repo, - pull_number: existingPR.number, - title: prTitle, - body: prBody - }); - context.logger.debug(`Updated PR #${existingPR.number} title and body`); - } catch (error) { - context.logger.debug( - `Failed to update PR title/body: ${error instanceof Error ? error.message : String(error)}` - ); - } - } else { - const head = `${owner}:${prBranch}`; - const { prTitle, prBody } = parseCommitMessageForPR(finalCommitMessage); - - try { - const { data: pullRequest } = await octokit.pulls.create({ - owner, - repo, - title: prTitle, - body: prBody, - head, - base: baseBranch, - draft: false - }); - - context.logger.info(`Created pull request: ${pullRequest.html_url}`); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - if (message.includes("A pull request already exists for")) { - context.failWithoutThrowing(`A pull request already exists for ${head}`); - } else { - throw error; - } - } - } - break; - } - case "push": { - if (selfhostedGithubConfig.branch != null) { - context.logger.debug(`Checking out branch ${selfhostedGithubConfig.branch}`); - await repository.checkout(selfhostedGithubConfig.branch); - } - - context.logger.debug("Checking for .fernignore file..."); - const fernignorePath = join(absolutePathToLocalOutput, RelativeFilePath.of(".fernignore")); - try { - await fs.access(fernignorePath); - context.logger.debug(".fernignore already exists"); - } catch { - context.logger.debug("Creating .fernignore file..."); - await fs.writeFile(fernignorePath, "# Specify files that shouldn't be modified by Fern\n", "utf-8"); - } - - context.logger.debug("Committing changes..."); - const finalCommitMessage = commitMessage ?? "SDK Generation"; - await repository.commitAllChanges(finalCommitMessage); - context.logger.debug( - `Committed changes to local copy of GitHub repository at ${absolutePathToLocalOutput}` - ); - - if (!selfhostedGithubConfig.previewMode) { - await repository.pushWithRebasingRemote(); - - const pushedBranch = await repository.getCurrentBranch(); - context.logger.info( - `Pushed branch: https://github.com/${selfhostedGithubConfig.uri}/tree/${pushedBranch}` - ); - } - break; - } - default: - assertNever(mode); - } - } catch (error) { - context.failAndThrow(`Error during GitHub self-hosted flow: ${String(error)}`); - } -} - export async function getWorkspaceTempDir(): Promise { return tmp.dir({ // use the /private prefix on osx so that docker can access the tmpdir diff --git a/packages/cli/yaml/generators-validator/src/ast/visitGeneratorsYamlAst.ts b/packages/cli/yaml/generators-validator/src/ast/visitGeneratorsYamlAst.ts index ffa19db024c3..336d5c43e329 100644 --- a/packages/cli/yaml/generators-validator/src/ast/visitGeneratorsYamlAst.ts +++ b/packages/cli/yaml/generators-validator/src/ast/visitGeneratorsYamlAst.ts @@ -26,6 +26,7 @@ export async function visitGeneratorsYamlAst( "api-settings": noop, ai: noop, autorelease: noop, + replay: noop, groups: async (groups) => { await visitGeneratorGroups({ groups, visitor, nodePath: ["groups"], cliVersion }); } diff --git a/packages/commons/github/src/ClonedRepository.ts b/packages/commons/github/src/ClonedRepository.ts index 9f3bf6eb40b7..a33d43025c06 100644 --- a/packages/commons/github/src/ClonedRepository.ts +++ b/packages/commons/github/src/ClonedRepository.ts @@ -75,6 +75,12 @@ export class ClonedRepository { await this.git.raw([...args, ...(Array.isArray(files) ? files : [files])]); } + public async restoreFilesFromCommit(commitSha: string, files: string | string[]): Promise { + await this.git.cwd(this.clonePath); + const fileList = Array.isArray(files) ? files : [files]; + await this.git.raw(["checkout", commitSha, "--", ...fileList]); + } + public async commit(message?: string): Promise { await this.git.cwd(this.clonePath); await this.git.commit(message ?? `Automated commit`, undefined, { @@ -248,6 +254,60 @@ export class ClonedRepository { await this.git.push("origin", branch, { "--set-upstream": null }); } + // Only used by replay's PR mode to push synthetic divergent commits to a fern-bot/ branch. + // Uses --force-with-lease (not --force) so we fail safely if the branch was updated + // by someone else since we cloned. Never targets main or user branches. + public async forcePush(): Promise { + await this.git.cwd(this.clonePath); + const currentBranch = await this.getCurrentBranch(); + await this.git.push("origin", currentBranch, ["--force-with-lease"]); + } + + public async createBranchFromHead(branchName: string): Promise { + await this.git.cwd(this.clonePath); + await this.git.checkoutLocalBranch(branchName); + } + + public async createBranchFromCommit(branchName: string, commitSha: string): Promise { + await this.git.cwd(this.clonePath); + await this.git.raw(["checkout", "-b", branchName, commitSha]); + } + + public async commitTree(treeSha: string, parentSha: string, message: string): Promise { + await this.git.cwd(this.clonePath); + const result = await this.git.raw(["commit-tree", treeSha, "-p", parentSha, "-m", message]); + return result.trim(); + } + + public async getHeadSha(): Promise { + await this.git.cwd(this.clonePath); + const result = await this.git.revparse(["HEAD"]); + return result.trim(); + } + + public async getHeadTreeHash(): Promise { + await this.git.cwd(this.clonePath); + const result = await this.git.raw(["rev-parse", "HEAD^{tree}"]); + return result.trim(); + } + + public async getCommitTreeHash(commitSha: string): Promise { + await this.git.cwd(this.clonePath); + const result = await this.git.raw(["rev-parse", `${commitSha}^{tree}`]); + return result.trim(); + } + + // This tag is a moving pointer (fern-generation-base--) that tracks the latest + // clean generation SHA. --force is required because: (1) the tag is intentionally overwritten + // each generation, and (2) --force-with-lease doesn't work for tags since git tag -f overwrites + // the local ref first, making the lease check always see a stale value. + // Only called from replay's PR mode; the caller wraps this in a try/catch. + public async createAndPushTag(tagName: string, commitSha: string): Promise { + await this.git.cwd(this.clonePath); + await this.git.tag(["-f", tagName, commitSha]); + await this.git.push("origin", `refs/tags/${tagName}`, ["--force"]); + } + public async overwriteLocalContents(sourceDirectoryPath: string): Promise { const [sourceContents, destContents] = await Promise.all([ readdir(sourceDirectoryPath), diff --git a/packages/generator-cli/build.mjs b/packages/generator-cli/build.mjs index f8075d0b7a06..9f4b89c2bc68 100644 --- a/packages/generator-cli/build.mjs +++ b/packages/generator-cli/build.mjs @@ -6,7 +6,7 @@ async function main() { await tsup.build({ entry: ["src/cli.ts", "src/api.ts"], format: ["cjs"], - dts: true, + dts: { compilerOptions: { composite: false } }, // Bundle all workspace dependencies to avoid ESM resolution issues noExternal: ["@fern-api/fs-utils", "@fern-api/github"], minify: false, diff --git a/packages/generator-cli/package.json b/packages/generator-cli/package.json index 3b1e1a7eeed4..b74efae6733c 100644 --- a/packages/generator-cli/package.json +++ b/packages/generator-cli/package.json @@ -7,15 +7,24 @@ "directory": "packages/generator-cli" }, "type": "commonjs", - "main": "dist/api.js", - "source": "src/index.ts", - "types": "dist/api.d.ts", + "exports": { + ".": { + "development": "./src/api.ts", + "source": "./src/api.ts", + "types": "./lib/api.d.ts", + "default": "./lib/api.js" + } + }, + "main": "lib/api.js", + "source": "src/api.ts", + "types": "lib/api.d.ts", "bin": { "generator-cli": "bin/cli" }, "files": ["bin", "dist"], "scripts": { - "clean": "rm -rf ./lib && rm -rf ./dist", + "clean": "rm -rf ./lib && rm -rf ./dist && tsc --build --clean", + "compile": "tsc --build", "depcheck": "knip --config ../../knip.json --no-config-hints", "dist:cli": "node build.mjs", "test": "vitest --run --passWithNoTests --globals --disable-console-intercept", @@ -25,9 +34,10 @@ "@fern-api/configs": "workspace:*", "@fern-api/fs-utils": "workspace:*", "@fern-api/github": "workspace:*", - "@octokit/rest": "catalog:", - "@types/jest": "catalog:", - "@types/node": "catalog:", + "@fern-api/replay": "^0.6.0", + "@octokit/rest": "^20.1.2", + "@types/jest": "^29.5.11", + "@types/node": "^22.0.0", "@types/yargs": "^17.0.32", "es-toolkit": "catalog:", "esbuild": "catalog:", diff --git a/packages/generator-cli/src/api.ts b/packages/generator-cli/src/api.ts index 4349819aa2b1..9c1b45537244 100644 --- a/packages/generator-cli/src/api.ts +++ b/packages/generator-cli/src/api.ts @@ -1,3 +1,4 @@ +export { type ConflictDetail, type ReplayReport } from "@fern-api/replay"; export { type GenerateReadmeParams, type GenerateReadmeToStreamParams, @@ -13,3 +14,24 @@ export { export { type GithubPrParams, githubPr } from "./api/github-pr.js"; export { type GithubPushParams, githubPush } from "./api/github-push.js"; export { type GithubReleaseParams, githubRelease } from "./api/github-release.js"; +export { + consolePipelineLogger, + formatReplayPrBody, + type GithubStepConfig, + type GithubStepResult, + logReplaySummary, + type PipelineConfig, + type PipelineContext, + type PipelineLogger, + type PipelineResult, + PostGenerationPipeline, + type ReplayStepResult +} from "./pipeline/index.js"; +export { + type BootstrapLogEntry, + formatBootstrapSummary, + type ReplayInitParams, + type ReplayInitResult, + replayInit +} from "./replay/replay-init.js"; +export { type ReplayRunParams, type ReplayRunResult, replayRun } from "./replay/replay-run.js"; diff --git a/packages/generator-cli/src/cli.ts b/packages/generator-cli/src/cli.ts index fe24314f8853..f17dcaa1ad47 100644 --- a/packages/generator-cli/src/cli.ts +++ b/packages/generator-cli/src/cli.ts @@ -5,10 +5,18 @@ import path from "path"; import { hideBin } from "yargs/helpers"; import yargs from "yargs/yargs"; import { githubRelease } from "./api/github-release.js"; -import { generateReadmeToStream, generateReferenceToStream, githubPr, githubPush } from "./api.js"; +import { + formatBootstrapSummary, + generateReadmeToStream, + generateReferenceToStream, + githubPr, + githubPush, + replayInit +} from "./api.js"; import { loadGitHubConfig } from "./configuration/loadGitHubConfig.js"; import { loadReadmeConfig } from "./configuration/loadReadmeConfig.js"; import { loadReferenceConfig } from "./configuration/loadReferenceConfig.js"; +import { type PipelineConfig, PostGenerationPipeline } from "./pipeline/index.js"; void yargs(hideBin(process.argv)) // eslint-disable-next-line turbo/no-undeclared-env-vars @@ -142,21 +150,290 @@ void yargs(hideBin(process.argv)) async (argv) => { if (argv.config == null) { process.stderr.write("missing required arguments; please specify the --config flag\n"); - const wd = cwd(); - const githubConfig = await loadGitHubConfig({ - absolutePathToConfig: resolve(wd, argv.config) - }); - await githubRelease({ githubConfig }); process.exit(1); } const wd = cwd(); - // Implementation for github release command - process.stderr.write(`Creating release on GitHub with config: ${resolve(wd, argv.config)}\n`); + const githubConfig = await loadGitHubConfig({ + absolutePathToConfig: resolve(wd, argv.config) + }); + await githubRelease({ githubConfig }); process.exit(0); } ) .demandCommand(); }) + .command("pipeline", "Post-generation pipeline operations", (yargs) => { + return yargs + .command( + "run", + "Run the post-generation pipeline", + (subYargs) => { + return subYargs + .option("config", { + string: true, + required: true, + description: "Path to pipeline config JSON" + }) + .option("output-dir", { + string: true, + required: true, + description: "Path to SDK output directory" + }) + .option("result-file", { + string: true, + required: false, + description: "Path to write result JSON" + }); + }, + async (argv) => { + if (argv.config == null || argv["output-dir"] == null) { + process.stderr.write( + "missing required arguments; please specify --config and --output-dir flags\n" + ); + process.exit(1); + } + + try { + const wd = cwd(); + + // Read pipeline config from file + const configContent = await readFile(resolve(wd, argv.config), "utf-8"); + const config: PipelineConfig = JSON.parse(configContent); + + // Override outputDir from CLI arg + config.outputDir = resolve(wd, argv["output-dir"]); + + // Run pipeline + const pipeline = new PostGenerationPipeline(config); + const result = await pipeline.run(); + + // Write result if specified + if (argv["result-file"] != null) { + const resultPath = resolve(wd, argv["result-file"]); + await mkdir(path.dirname(resultPath), { recursive: true }); + await fs.promises.writeFile(resultPath, JSON.stringify(result, null, 2)); + } + + // Log summary to stderr + process.stderr.write(`Pipeline ${result.success ? "succeeded" : "failed"}\n`); + + if (result.steps.replay != null) { + const replayResult = result.steps.replay; + process.stderr.write( + `Replay: ${replayResult.patchesApplied} patches applied (${replayResult.patchesWithConflicts} conflicts)\n` + ); + } + + if (result.steps.github != null) { + const githubResult = result.steps.github; + if (githubResult.prUrl != null) { + const action = githubResult.updatedExistingPr ? "Updated" : "Created"; + process.stderr.write(`GitHub: ${action} PR: ${githubResult.prUrl}\n`); + } else if (githubResult.branchUrl != null) { + process.stderr.write(`GitHub: Pushed to ${githubResult.branchUrl}\n`); + } + } + + if (result.errors != null && result.errors.length > 0) { + process.stderr.write(`Errors:\n${result.errors.map((e) => ` - ${e}`).join("\n")}\n`); + } + + // Exit with appropriate code + process.exit(result.success ? 0 : 1); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + process.stderr.write(`Pipeline failed: ${errorMessage}\n`); + process.exit(1); + } + } + ) + .demandCommand(); + }) + .command({ + command: "replay", + describe: false, + builder: (yargs) => { + return yargs + .command( + "init", + false, // hidden from --help + (subYargs) => { + return subYargs + .option("github", { + type: "string", + required: true, + description: "GitHub repository (e.g., owner/repo)" + }) + .option("token", { + type: "string", + required: true, + description: "GitHub token with push and PR permissions" + }) + .option("dry-run", { + type: "boolean", + default: false, + description: "Report what would happen without creating a PR" + }) + .option("max-commits", { + type: "number", + description: "Max commits to scan for generation history" + }) + .option("pr-title", { + type: "string", + description: "Custom title for the PR" + }) + .option("pr-body", { + type: "string", + description: "Custom body for the PR" + }) + .option("force", { + type: "boolean", + default: false, + description: "Overwrite existing lockfile if Replay is already initialized" + }) + .option("import-history", { + type: "boolean", + default: false, + description: "Scan git history for existing patches (migration)" + }); + }, + async (argv) => { + if (argv.github == null || argv.token == null) { + process.stderr.write( + "missing required arguments; please specify --github and --token flags\n" + ); + process.exit(1); + } + + try { + const result = await replayInit({ + githubRepo: argv.github, + token: argv.token, + dryRun: argv["dry-run"], + maxCommitsToScan: argv["max-commits"], + prTitle: argv["pr-title"], + prBody: argv["pr-body"], + force: argv.force, + importHistory: argv["import-history"] + }); + + const logEntries = formatBootstrapSummary(result); + for (const entry of logEntries) { + process.stderr.write(`${entry.message}\n`); + } + + if (!result.bootstrap.generationCommit) { + process.exit(1); + } + + if (argv["dry-run"]) { + process.stderr.write("\nDry run complete. No PR created.\n"); + } + + // Write result as JSON to stdout for programmatic consumption + process.stdout.write(JSON.stringify(result, null, 2) + "\n"); + process.exit(0); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + process.stderr.write(`Replay init failed: ${errorMessage}\n`); + process.exit(1); + } + } + ) + .command( + "bootstrap ", + false, // hidden from --help + (subYargs) => { + return subYargs + .positional("sdk-dir", { + type: "string", + description: "Path to the SDK output directory", + demandOption: true + }) + .option("dry-run", { + type: "boolean", + default: false, + description: "Show what would happen without making changes" + }) + .option("fernignore-action", { + type: "string", + choices: ["migrate", "delete", "skip"], + default: "skip", + description: "How to handle .fernignore: migrate, delete, or skip" + }) + .option("import-history", { + type: "boolean", + default: false, + description: "Scan git history for existing patches (migration)" + }); + }, + async (argv) => { + const sdkDir = argv["sdk-dir"]; + if (sdkDir == null) { + process.stderr.write("Missing required argument: sdk-dir\n"); + process.exit(1); + } + + try { + const { bootstrap } = await import("@fern-api/replay"); + const outputDir = resolve(cwd(), sdkDir); + const fernignoreAction = argv["fernignore-action"] as + | "migrate" + | "delete" + | "skip" + | undefined; + const result = await bootstrap(outputDir, { + dryRun: argv["dry-run"], + fernignoreAction, + importHistory: argv["import-history"] + }); + + if (result.generationCommit) { + process.stderr.write( + `Found generation commit: ${result.generationCommit.sha.slice(0, 7)} "${result.generationCommit.message}"\n` + ); + process.stderr.write( + `Scanned commits since: ${result.scannedSinceGeneration.slice(0, 7)} (last generation)\n` + ); + if (result.staleGenerationsSkipped > 0) { + process.stderr.write( + `Skipped ${result.staleGenerationsSkipped} older generation(s) — only tracking recent commits\n` + ); + } + } + + process.stderr.write( + `Patches detected: ${result.patchesDetected}, created: ${result.patchesCreated}\n` + ); + + if (result.warnings && result.warnings.length > 0) { + for (const warning of result.warnings) { + process.stderr.write(`Warning: ${warning}\n`); + } + } + + if (argv["dry-run"]) { + process.stderr.write("\nDry run complete. No changes made.\n"); + } else { + process.stderr.write( + `\nBootstrap complete! Lockfile saved to ${outputDir}/.fern/replay.lock\n` + ); + } + + process.exit(0); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + process.stderr.write(`Bootstrap failed: ${errorMessage}\n`); + process.exit(1); + } + } + ) + .demandCommand(); + }, + handler: () => { + // parent command — subcommands handle execution + } + }) .demandCommand() .showHelpOnFail(true) .parse(); diff --git a/packages/generator-cli/src/pipeline/PipelineLogger.ts b/packages/generator-cli/src/pipeline/PipelineLogger.ts new file mode 100644 index 000000000000..f896b40633a2 --- /dev/null +++ b/packages/generator-cli/src/pipeline/PipelineLogger.ts @@ -0,0 +1,21 @@ +/** + * Logger interface for pipeline steps. + * Abstracts away the logging implementation so steps work with both + * the CLI's TaskContext and the standalone CLI subprocess (console). + */ +export interface PipelineLogger { + debug(message: string): void; + info(message: string): void; + warn(message: string): void; + error(message: string): void; +} + +/** + * Default logger implementation using stderr (to avoid mixing with JSON result output on stdout). + */ +export const consolePipelineLogger: PipelineLogger = { + debug: (msg) => process.stderr.write(`[debug] ${msg}\n`), + info: (msg) => process.stderr.write(`[info] ${msg}\n`), + warn: (msg) => process.stderr.write(`[warn] ${msg}\n`), + error: (msg) => process.stderr.write(`[error] ${msg}\n`) +}; diff --git a/packages/generator-cli/src/pipeline/PostGenerationPipeline.ts b/packages/generator-cli/src/pipeline/PostGenerationPipeline.ts new file mode 100644 index 000000000000..a05350946e32 --- /dev/null +++ b/packages/generator-cli/src/pipeline/PostGenerationPipeline.ts @@ -0,0 +1,91 @@ +import { consolePipelineLogger, type PipelineLogger } from "./PipelineLogger"; +import { BaseStep } from "./steps/BaseStep"; +import { GithubStep } from "./steps/GithubStep"; +import { ReplayStep } from "./steps/ReplayStep"; +import type { + FernignoreStepResult, + GithubStepResult, + PipelineConfig, + PipelineContext, + PipelineResult, + ReplayStepResult +} from "./types"; + +export class PostGenerationPipeline { + private steps: BaseStep[] = []; + + constructor( + private readonly config: PipelineConfig, + private readonly logger: PipelineLogger = consolePipelineLogger + ) { + // Disallow push mode + replay: push mode force-pushes to the base branch, + // which is incompatible with replay's 3-way merge workflow. + if (config.replay?.enabled && config.github?.mode === "push") { + this.logger.warn( + "Replay is not supported with GitHub push mode. Disabling replay to prevent force push to base branch." + ); + config.replay.enabled = false; + } + + if (config.replay?.enabled) { + this.steps.push( + new ReplayStep( + config.outputDir, + this.logger, + config.replay, + config.cliVersion, + config.generatorVersions, + config.generatorName + ) + ); + } + + // Phase 2: FernignoreStep (not implemented yet) + // if (config.fernignore?.enabled) { + // this.steps.push(new FernignoreStep(config.outputDir, this.logger)); + // } + + if (config.github?.enabled) { + this.steps.push(new GithubStep(config.outputDir, this.logger, config.github)); + } + } + + async run(): Promise { + const result: PipelineResult = { + success: true, + steps: {} + }; + + const pipelineContext: PipelineContext = { + previousStepResults: {} + }; + + for (const step of this.steps) { + try { + const stepResult = await step.execute(pipelineContext); + + if (step.name === "replay") { + result.steps.replay = stepResult as ReplayStepResult; + pipelineContext.previousStepResults.replay = stepResult as ReplayStepResult; + } else if (step.name === "fernignore") { + result.steps.fernignore = stepResult as FernignoreStepResult; + } else if (step.name === "github") { + result.steps.github = stepResult as GithubStepResult; + } + + if (!stepResult.success) { + result.success = false; + result.errors = result.errors ?? []; + result.errors.push(stepResult.errorMessage ?? `${step.name} step failed`); + } + } catch (error) { + result.success = false; + result.errors = result.errors ?? []; + const errorMessage = error instanceof Error ? error.message : String(error); + result.errors.push(`${step.name} step error: ${errorMessage}`); + } + } + + return result; + } +} diff --git a/packages/generator-cli/src/pipeline/github/constants.ts b/packages/generator-cli/src/pipeline/github/constants.ts new file mode 100644 index 000000000000..53fdf6d8c633 --- /dev/null +++ b/packages/generator-cli/src/pipeline/github/constants.ts @@ -0,0 +1,3 @@ +export const FERN_BOT_NAME = "fern-api"; +export const FERN_BOT_EMAIL = "115122769+fern-api[bot]@users.noreply.github.com"; +export const FERN_BOT_LOGIN = "fern-api[bot]"; diff --git a/packages/generator-cli/src/pipeline/github/createReplayBranch.ts b/packages/generator-cli/src/pipeline/github/createReplayBranch.ts new file mode 100644 index 000000000000..b8aa26acd7c9 --- /dev/null +++ b/packages/generator-cli/src/pipeline/github/createReplayBranch.ts @@ -0,0 +1,78 @@ +import type { ClonedRepository } from "@fern-api/github"; + +import type { PipelineLogger } from "../PipelineLogger"; + +export async function createReplayBranch( + repository: ClonedRepository, + branchName: string, + commitMessage: string | undefined, + replayConflictInfo: + | { + previousGenerationSha: string; + currentGenerationSha: string; + hasConflicts: boolean; + baseBranchHead?: string; + } + | undefined, + logger: PipelineLogger +): Promise { + if (replayConflictInfo?.hasConflicts) { + // Parent must be previousGenerationSha to create a divergent fork in history. + // GitHub computes merge base = previousGen, then shows real 3-way diff between + // main's path (previousGen → mainHEAD) and the branch (previousGen → synthetic). + // Using baseBranchHead (= main HEAD) would make the branch linear, showing + // conflict markers as plain text instead of real merge conflicts. + const parentSha = replayConflictInfo.previousGenerationSha; + // Use baseBranchHead for lockfile restore — it's on main's lineage and has the + // correct lockfile state (pre-replay). Fall back to previousGenerationSha. + const lockfileRestoreSha = replayConflictInfo.baseBranchHead ?? replayConflictInfo.previousGenerationSha; + + logger.debug(`Creating divergent PR branch with generation tree (parent: ${parentSha.substring(0, 7)})`); + + // Use the pure generation tree — NOT HEAD's tree (which has conflict markers as text). + // With the generation tree parented off previousGenerationSha, GitHub computes a real + // 3-way merge: base=previousGen, main=previousGen+user edits, PR=newGen. Files where + // both sides diverged show as real merge conflicts in the PR "Files changed" view. + const genTreeHash = await repository.getCommitTreeHash(replayConflictInfo.currentGenerationSha); + const syntheticCommitSha = await repository.commitTree( + genTreeHash, + parentSha, + `[fern-generated] ${commitMessage ?? "Update SDK"}` + ); + + await repository.createBranchFromCommit(branchName, syntheticCommitSha); + + // Restore the lockfile from the base branch so it matches main. + // The HEAD tree has a replay-updated lockfile which would cause a spurious + // lockfile conflict in the PR. Main's lockfile is the source of truth. + try { + await repository.restoreFilesFromCommit(lockfileRestoreSha, ".fern/replay.lock"); + await repository.commitAllChanges(`[fern-generated] ${commitMessage ?? "Update SDK"}`); + } catch (error) { + logger.debug( + `Could not restore lockfile from base branch: ${error instanceof Error ? error.message : String(error)}` + ); + } + + return syntheticCommitSha; + } else if (replayConflictInfo != null) { + const parentSha = replayConflictInfo.previousGenerationSha; + + logger.debug(`Creating linear PR branch from HEAD (no conflicts)`); + + await repository.createBranchFromHead(branchName); + + const genTreeHash = await repository.getCommitTreeHash(replayConflictInfo.currentGenerationSha); + const tagCommitSha = await repository.commitTree( + genTreeHash, + parentSha, + `[fern-generated] ${commitMessage ?? "Update SDK"}` + ); + + return tagCommitSha; + } else { + // No previous generation info — use existing linear behavior + await repository.createBranchFromHead(branchName); + return undefined; + } +} diff --git a/packages/generator-cli/src/pipeline/github/findExistingUpdatablePR.ts b/packages/generator-cli/src/pipeline/github/findExistingUpdatablePR.ts new file mode 100644 index 000000000000..a307469f9ed5 --- /dev/null +++ b/packages/generator-cli/src/pipeline/github/findExistingUpdatablePR.ts @@ -0,0 +1,116 @@ +import { Octokit } from "@octokit/rest"; + +import type { PipelineLogger } from "../PipelineLogger"; +import { FERN_BOT_LOGIN } from "./constants"; + +export interface ExistingPullRequest { + number: number; + headBranch: string; + htmlUrl: string; + isDraft: boolean; + nodeId: string; +} + +export async function findExistingUpdatablePR( + octokit: Octokit, + owner: string, + repo: string, + baseBranch: string, + logger: PipelineLogger +): Promise { + try { + const { data: pulls } = await octokit.pulls.list({ + owner, + repo, + state: "open", + base: baseBranch, + sort: "updated", + direction: "desc", + per_page: 20 + }); + + for (const pr of pulls) { + const prAuthor = pr.user?.login; + if (prAuthor !== FERN_BOT_LOGIN) { + // In self-hosted mode, PRs may be authored by the token owner, not fern-api[bot]. + // Don't skip — let the commit check determine if PR is updatable. + logger.debug(`PR #${pr.number}: author ${prAuthor} is not ${FERN_BOT_LOGIN}, checking commits anyway`); + } + + if (!pr.head.ref.startsWith("fern-bot/")) { + logger.debug(`PR #${pr.number} skipped: branch ${pr.head.ref} does not start with fern-bot/`); + continue; + } + + const hasOnlyGenerationCommits = await checkPRHasOnlyGenerationCommits( + octokit, + owner, + repo, + pr.number, + logger + ); + + if (hasOnlyGenerationCommits) { + logger.debug(`Found existing updatable PR #${pr.number} with branch ${pr.head.ref}`); + return { + number: pr.number, + headBranch: pr.head.ref, + htmlUrl: pr.html_url, + isDraft: pr.draft ?? false, + nodeId: pr.node_id + }; + } else { + logger.debug(`PR #${pr.number} skipped: contains non-generation commits`); + } + } + + return undefined; + } catch (error) { + logger.debug(`Error finding existing PRs: ${error instanceof Error ? error.message : String(error)}`); + return undefined; + } +} + +export async function checkPRHasOnlyGenerationCommits( + octokit: Octokit, + owner: string, + repo: string, + pullNumber: number, + logger: PipelineLogger +): Promise { + try { + const { data: commits } = await octokit.pulls.listCommits({ + owner, + repo, + pull_number: pullNumber, + per_page: 100 + }); + + for (const commit of commits) { + const authorLogin = commit.author?.login; + const authorEmail = commit.commit.author?.email; + const commitMessage = commit.commit.message ?? ""; + + const isFernAuthor = + authorLogin === FERN_BOT_LOGIN || + authorEmail === "115122769+fern-api[bot]@users.noreply.github.com" || + commit.commit.author?.name === "fern-api"; + + const isFernCommitMessage = + commitMessage.startsWith("[fern-generated]") || commitMessage.startsWith("[fern-replay]"); + + if (!isFernAuthor && !isFernCommitMessage) { + logger.debug( + `Commit ${commit.sha.substring(0, 7)} is not a generation commit: ` + + `author=${authorLogin}, email=${authorEmail}, message="${commitMessage.slice(0, 40)}"` + ); + return false; + } + } + + return true; + } catch (error) { + logger.debug(`Error checking PR commits: ${error instanceof Error ? error.message : String(error)}`); + return false; + } +} diff --git a/packages/generator-cli/src/pipeline/github/index.ts b/packages/generator-cli/src/pipeline/github/index.ts new file mode 100644 index 000000000000..7d1092ba8cad --- /dev/null +++ b/packages/generator-cli/src/pipeline/github/index.ts @@ -0,0 +1,9 @@ +export { FERN_BOT_EMAIL, FERN_BOT_LOGIN, FERN_BOT_NAME } from "./constants"; +export { createReplayBranch } from "./createReplayBranch"; +export { + checkPRHasOnlyGenerationCommits, + type ExistingPullRequest, + findExistingUpdatablePR +} from "./findExistingUpdatablePR"; +export { parseCommitMessageForPR } from "./parseCommitMessage"; +export { postConflictComments } from "./postConflictComments"; diff --git a/packages/generator-cli/src/pipeline/github/parseCommitMessage.ts b/packages/generator-cli/src/pipeline/github/parseCommitMessage.ts new file mode 100644 index 000000000000..5280697ba7fb --- /dev/null +++ b/packages/generator-cli/src/pipeline/github/parseCommitMessage.ts @@ -0,0 +1,6 @@ +export function parseCommitMessageForPR(commitMessage: string): { prTitle: string; prBody: string } { + const lines = commitMessage.split("\n"); + const prTitle = lines[0]?.trim() || "SDK Generation"; + const prBody = lines.slice(1).join("\n").trim() || "Automated SDK generation by Fern"; + return { prTitle, prBody }; +} diff --git a/packages/generator-cli/src/pipeline/github/postConflictComments.ts b/packages/generator-cli/src/pipeline/github/postConflictComments.ts new file mode 100644 index 000000000000..b635a7406f90 --- /dev/null +++ b/packages/generator-cli/src/pipeline/github/postConflictComments.ts @@ -0,0 +1,129 @@ +import type { Octokit } from "@octokit/rest"; + +import type { PipelineLogger } from "../PipelineLogger"; +import { formatConflictReason, patchDescription } from "../replay-summary"; +import type { ReplayStepResult } from "../types"; + +/** + * Resolve guidance for a conflict reason. Suggests whether the user likely wants + * to keep their customization, take the new generation, or combine both. + */ +function resolveGuidance(conflictReason: string | undefined): string { + switch (conflictReason) { + case "same-line-edit": + return "You likely want to combine both -- keep your logic while incorporating the generation's structural changes."; + case "new-file-both": + return "Compare both versions and merge any unique content. If you created the file intentionally, your version is likely correct."; + case "base-generation-mismatch": + return "Review carefully -- the generated code changed significantly. You may need to re-apply your customization on top of the new structure."; + case "patch-apply-failed": + return "Your customization could not be applied automatically. Re-apply your changes manually on top of the new generated code."; + default: + return "Review both sides and keep whichever version (or combination) is correct for your use case."; + } +} + +interface ConflictFileEntry { + file: string; + customization: string; + reason: string; + guidance: string; +} + +/** + * Build the markdown body for the single conflict summary comment. + * Returns undefined if there are no conflict files to report. + */ +function buildConflictCommentBody( + conflictDetails: NonNullable +): string | undefined { + const entries: ConflictFileEntry[] = []; + + for (const detail of conflictDetails) { + const description = patchDescription(detail); + for (const file of detail.files) { + if (file.status === "conflict") { + entries.push({ + file: file.file, + customization: description, + reason: formatConflictReason(file.conflictReason), + guidance: resolveGuidance(file.conflictReason) + }); + } + } + } + + if (entries.length === 0) { + return undefined; + } + + const parts: string[] = []; + + parts.push("## Conflict Resolution Guide\n"); + parts.push( + `This PR has **${entries.length} file${entries.length === 1 ? "" : "s"}** where your customizations conflict with the new SDK generation. ` + + "Below is per-file guidance to help you resolve each conflict.\n" + ); + parts.push( + '> In GitHub\'s conflict editor, `<<<<<<< HEAD` ("Accept current") = **new generated code**, ' + + '`>>>>>>> main` ("Accept incoming") = **your customization**.\n' + ); + + parts.push("| File | Your customization | Why it conflicted | Suggested resolution |"); + parts.push("|------|-------------------|-------------------|---------------------|"); + for (const entry of entries) { + parts.push(`| \`${entry.file}\` | ${entry.customization} | ${entry.reason} | ${entry.guidance} |`); + } + + parts.push("\n---"); + parts.push("*Posted by Fern Replay to help resolve SDK customization conflicts.*"); + + return parts.join("\n"); +} + +/** + * Post a single summary comment on a PR listing all conflicting files with + * per-file context and resolution guidance. + * + * This uses issue comments (not review comments) to avoid fragility around + * specific line numbers and to consolidate all guidance into one notification. + * + * Should be called after PR creation/update, only when conflicts exist. + */ +export async function postConflictComments( + octokit: Octokit, + owner: string, + repo: string, + prNumber: number, + replayResult: ReplayStepResult | undefined, + logger: PipelineLogger +): Promise { + if (replayResult == null) { + return; + } + + const conflictDetails = replayResult.conflictDetails; + if (conflictDetails == null || conflictDetails.length === 0) { + return; + } + + const body = buildConflictCommentBody(conflictDetails); + if (body == null) { + logger.debug("No conflict files to comment on -- skipping conflict comment"); + return; + } + + try { + await octokit.issues.createComment({ + owner, + repo, + issue_number: prNumber, + body + }); + logger.debug(`Posted conflict resolution guide comment on PR #${prNumber}`); + } catch (error) { + logger.debug( + `Could not post conflict comment on PR #${prNumber}: ${error instanceof Error ? error.message : String(error)}` + ); + } +} diff --git a/packages/generator-cli/src/pipeline/index.ts b/packages/generator-cli/src/pipeline/index.ts new file mode 100644 index 000000000000..99d8f5327e20 --- /dev/null +++ b/packages/generator-cli/src/pipeline/index.ts @@ -0,0 +1,16 @@ +export { consolePipelineLogger, type PipelineLogger } from "./PipelineLogger"; +export { PostGenerationPipeline } from "./PostGenerationPipeline"; +export { formatReplayPrBody, logReplaySummary } from "./replay-summary"; +export type { + ConflictInfo, + FernignoreStepConfig, + FernignoreStepResult, + GithubStepConfig, + GithubStepResult, + PipelineConfig, + PipelineContext, + PipelineResult, + ReplayStepConfig, + ReplayStepResult, + StepResult +} from "./types"; diff --git a/packages/generator-cli/src/pipeline/replay-summary.ts b/packages/generator-cli/src/pipeline/replay-summary.ts new file mode 100644 index 000000000000..5cea4cc9eaa4 --- /dev/null +++ b/packages/generator-cli/src/pipeline/replay-summary.ts @@ -0,0 +1,147 @@ +import type { PipelineLogger } from "./PipelineLogger"; +import type { ReplayStepResult } from "./types"; + +function plural(n: number, word: string): string { + return `${n} ${word}${n === 1 ? "" : "s"}`; +} + +export function formatConflictReason(reason: string | undefined): string { + switch (reason) { + case "same-line-edit": + return "The new generation changed the same lines you edited"; + case "new-file-both": + return "You and the generator both created this file"; + case "base-generation-mismatch": + return "The generated code changed significantly around your edit"; + case "patch-apply-failed": + return "Your change could not be applied to the new generated code"; + default: + return "Your edit overlaps with changes in the new generation"; + } +} + +export function patchDescription(detail: { patchMessage: string; files: Array<{ file: string }> }): string { + if (detail.patchMessage && detail.patchMessage !== "update") { + return detail.patchMessage; + } + const firstFile = detail.files[0]?.file; + return firstFile != null ? `changes in ${firstFile}` : "customization"; +} + +export function logReplaySummary(result: ReplayStepResult, logger: PipelineLogger): void { + if (!result.executed) { + return; + } + + const applied = result.patchesApplied ?? 0; + const absorbed = result.patchesAbsorbed ?? 0; + const conflicts = result.patchesWithConflicts ?? 0; + const preserved = applied - absorbed; + + logger.debug( + `Replay: flow=${result.flow}, detected=${result.patchesDetected ?? 0}, applied=${applied}, absorbed=${absorbed}, conflicts=${conflicts}` + ); + + if (preserved > 0) { + const absorbedNote = absorbed > 0 ? ` (${plural(absorbed, "customization")} now part of generated code)` : ""; + logger.info(`Replay: ${plural(preserved, "customization")} preserved${absorbedNote}`); + } else if (absorbed > 0) { + logger.info(`Replay: ${plural(absorbed, "customization")} now part of generated code`); + } + + if (conflicts > 0) { + const totalFiles = (result.conflictDetails ?? []).reduce((sum, d) => sum + d.files.length, 0); + logger.warn( + `Replay: ${plural(totalFiles, "file")} ${totalFiles === 1 ? "has" : "have"} merge conflicts — resolve in the PR or locally` + ); + for (const detail of result.conflictDetails ?? []) { + logger.warn(` "${patchDescription(detail)}":`); + for (const file of detail.files) { + logger.warn(` ${file.file} — ${formatConflictReason(file.conflictReason)}`); + } + } + } + + for (const warning of result.warnings ?? []) { + logger.warn(`Replay: ${warning}`); + } +} + +export function formatReplayPrBody( + result: ReplayStepResult | undefined, + options?: { branchName?: string } +): string | undefined { + if (result == null || !result.executed) { + return undefined; + } + + const applied = result.patchesApplied ?? 0; + const absorbed = result.patchesAbsorbed ?? 0; + const conflicts = result.patchesWithConflicts ?? 0; + const preserved = applied - absorbed; + + if (preserved === 0 && conflicts === 0) { + return undefined; + } + + const parts: string[] = []; + + if (preserved > 0 && conflicts === 0) { + parts.push(`\u2705 ${plural(preserved, "customization")} automatically preserved in this update.`); + } else if (preserved > 0) { + parts.push( + `\u2705 ${plural(preserved, "customization")} automatically preserved, but ${plural(conflicts, "file")} ${conflicts === 1 ? "needs" : "need"} your attention.` + ); + } + + // Conflict detail + if (conflicts > 0) { + const allFiles = (result.conflictDetails ?? []).flatMap((d) => d.files); + const totalFiles = allFiles.length; + + parts.push(`\n### \u26a0\ufe0f Action required: ${plural(totalFiles, "file")} with customization conflicts\n`); + parts.push( + `You previously customized ${totalFiles === 1 ? "a file" : "files"} in this SDK. This generation changed the same code, so your ${totalFiles === 1 ? "edit" : "edits"} couldn't be applied automatically.\n` + ); + parts.push( + `> \u26a0\ufe0f **Label guide:** In GitHub's conflict editor, \`<<<<<<< HEAD\` ("Accept current") = new generated code. \`>>>>>>> main\` ("Accept incoming") = your customization.\n` + ); + + parts.push(`| File | Your customization | Why it conflicted |`); + parts.push(`|------|-------------------|-------------------|`); + for (const detail of result.conflictDetails ?? []) { + const description = patchDescription(detail); + for (const f of detail.files) { + parts.push(`| \`${f.file}\` | ${description} | ${formatConflictReason(f.conflictReason)} |`); + } + } + + const branch = options?.branchName ?? ""; + parts.push(`\n#### How to fix\n`); + + parts.push(`**Option A: Resolve in GitHub (recommended for simple conflicts)**\n`); + parts.push(`1. Click the **Resolve conflicts** button below`); + parts.push(`2. For each file, choose which code to keep:`); + parts.push(` - \`<<<<<<< HEAD\` / "Accept current changes" = **new generated code**`); + parts.push(` - \`>>>>>>> main\` / "Accept incoming changes" = **your customization**`); + parts.push(` - Or manually combine both, then delete the conflict markers`); + parts.push(`3. Click **Mark as resolved** for each file, then **Commit merge**\n`); + + parts.push(`**Option B: Resolve locally**\n`); + parts.push(`1. Fetch and check out this branch:`); + parts.push(` \`\`\`sh`); + parts.push(` git fetch origin && git checkout ${branch}`); + parts.push(` \`\`\``); + parts.push(`2. Merge the base branch and resolve conflicts in your editor:`); + parts.push(` \`\`\`sh`); + parts.push(` git merge origin/main`); + parts.push(` \`\`\``); + parts.push(`3. Commit and push:`); + parts.push(` \`\`\`sh`); + parts.push(` git add -A && git commit -m "resolve conflicts" && git push`); + parts.push(` \`\`\`\n`); + parts.push(`Your resolved changes will be remembered on future SDK generations.`); + } + + return parts.join("\n"); +} diff --git a/packages/generator-cli/src/pipeline/steps/BaseStep.ts b/packages/generator-cli/src/pipeline/steps/BaseStep.ts new file mode 100644 index 000000000000..b3241ff58dee --- /dev/null +++ b/packages/generator-cli/src/pipeline/steps/BaseStep.ts @@ -0,0 +1,13 @@ +import type { PipelineLogger } from "../PipelineLogger"; +import type { PipelineContext, StepResult } from "../types"; + +export abstract class BaseStep { + abstract readonly name: string; + + constructor( + protected readonly outputDir: string, + protected readonly logger: PipelineLogger + ) {} + + abstract execute(context: PipelineContext): Promise; +} diff --git a/packages/generator-cli/src/pipeline/steps/FernignoreStep.ts b/packages/generator-cli/src/pipeline/steps/FernignoreStep.ts new file mode 100644 index 000000000000..ae65e3fc8451 --- /dev/null +++ b/packages/generator-cli/src/pipeline/steps/FernignoreStep.ts @@ -0,0 +1,23 @@ +import type { FernignoreStepResult, PipelineContext } from "../types"; +import { BaseStep } from "./BaseStep"; + +/** + * Step that preserves files listed in .fernignore during regeneration. + * Phase 2: Not implemented yet - this is a placeholder. + * + * Will implement the git rm/reset/restore logic to preserve .fernignore paths. + * Logic will be extracted from: + * packages/cli/generation/local-generation/local-workspace-runner/src/LocalTaskHandler.ts + */ +export class FernignoreStep extends BaseStep { + readonly name = "fernignore"; + + async execute(_context: PipelineContext): Promise { + // TODO: Phase 2 - Implement fernignore preservation + // - Read .fernignore file + // - Parse glob patterns + // - Execute git rm/reset/restore flow + // - Return paths that were preserved + throw new Error("FernignoreStep not implemented yet - Phase 2"); + } +} diff --git a/packages/generator-cli/src/pipeline/steps/GithubStep.ts b/packages/generator-cli/src/pipeline/steps/GithubStep.ts new file mode 100644 index 000000000000..62cf0b633b73 --- /dev/null +++ b/packages/generator-cli/src/pipeline/steps/GithubStep.ts @@ -0,0 +1,587 @@ +import { ClonedRepository, parseRepository } from "@fern-api/github"; +import { Octokit } from "@octokit/rest"; +import { access, writeFile } from "fs/promises"; +import { join } from "path"; +import { FERN_BOT_EMAIL, FERN_BOT_NAME } from "../github/constants"; +import { createReplayBranch } from "../github/createReplayBranch"; +import type { ExistingPullRequest } from "../github/findExistingUpdatablePR"; +import { findExistingUpdatablePR } from "../github/findExistingUpdatablePR"; +import { parseCommitMessageForPR } from "../github/parseCommitMessage"; +import { postConflictComments } from "../github/postConflictComments"; +import type { PipelineLogger } from "../PipelineLogger"; +import { formatReplayPrBody } from "../replay-summary"; +import type { GithubStepConfig, GithubStepResult, PipelineContext, ReplayStepResult } from "../types"; +import { BaseStep } from "./BaseStep"; + +export class GithubStep extends BaseStep { + readonly name = "github"; + + constructor( + outputDir: string, + logger: PipelineLogger, + private readonly config: GithubStepConfig + ) { + super(outputDir, logger); + } + + async execute(context: PipelineContext): Promise { + const replayResult = context.previousStepResults.replay; + const skipCommit = this.config.skipCommit ?? this.deriveSkipCommit(replayResult); + const replayConflictInfo = this.config.replayConflictInfo ?? this.deriveReplayConflictInfo(replayResult); + + try { + this.logger.debug("Starting GitHub self-hosted flow in directory: " + this.outputDir); + const repository = ClonedRepository.createAtPath(this.outputDir); + + const now = new Date(); + const formattedDate = now + .toISOString() + .replace("T", "_") + .replace(/:/g, "-") + .replace("Z", "") + .replace(".", "_"); + const newPrBranch = `fern-bot/${formattedDate}`; + + try { + await repository.setUserAndEmail({ + name: FERN_BOT_NAME, + email: FERN_BOT_EMAIL + }); + } catch { + // pass + } + + const mode = this.config.mode; + switch (mode) { + case "pull-request": + return await this.executePullRequestMode( + repository, + newPrBranch, + skipCommit, + replayConflictInfo, + replayResult + ); + case "push": + return await this.executePushMode(repository); + default: { + const exhaustive: never = mode; + throw new Error(`Unexpected GitHub mode: ${String(exhaustive)}`); + } + } + } catch (error) { + const message = `Error during GitHub self-hosted flow: ${String(error)}`; + this.logger.error(message); + return { + executed: true, + success: false, + errorMessage: message + }; + } + } + + private async executePullRequestMode( + repository: ClonedRepository, + newPrBranch: string, + skipCommit: boolean, + replayConflictInfo: + | { + previousGenerationSha: string; + currentGenerationSha: string; + hasConflicts: boolean; + baseBranchHead?: string; + } + | undefined, + replayResult: ReplayStepResult | undefined + ): Promise { + const baseBranch = this.config.branch ?? (await repository.getDefaultBranch()); + const octokit = new Octokit({ auth: this.config.token }); + const { owner, repo } = parseRepository(this.config.uri); + + const existingPR = await findExistingUpdatablePR(octokit, owner, repo, baseBranch, this.logger); + + let prBranch: string; + let isUpdatingExistingPR = false; + let generationBaseSha: string | undefined; + + if (existingPR != null) { + this.logger.info( + `Found existing updatable PR #${existingPR.number}, will update branch ${existingPR.headBranch}` + ); + prBranch = existingPR.headBranch; + isUpdatingExistingPR = true; + if (skipCommit) { + generationBaseSha = await createReplayBranch( + repository, + prBranch, + this.config.commitMessage, + replayConflictInfo, + this.logger + ); + } else { + await repository.checkoutRemoteBranch(prBranch); + } + } else { + this.logger.debug(`No existing updatable PR found, creating new branch ${newPrBranch}`); + prBranch = newPrBranch; + if (skipCommit) { + generationBaseSha = await createReplayBranch( + repository, + prBranch, + this.config.commitMessage, + replayConflictInfo, + this.logger + ); + } else { + await repository.checkout(prBranch); + } + } + + if (!skipCommit) { + await this.ensureFernignore(); + + this.logger.debug("Committing changes..."); + const finalCommitMessage = this.config.commitMessage ?? "SDK Generation"; + await repository.commitAllChanges(finalCommitMessage); + this.logger.debug(`Committed changes to local copy of GitHub repository at ${this.outputDir}`); + } + + const result: GithubStepResult = { + executed: true, + success: true, + updatedExistingPr: isUpdatingExistingPR + }; + + if (!this.config.previewMode) { + if (skipCommit && isUpdatingExistingPR) { + await repository.forcePush(); + } else { + await repository.push(); + } + const pushedBranch = await repository.getCurrentBranch(); + result.branchUrl = `https://github.com/${this.config.uri}/tree/${pushedBranch}`; + this.logger.info(`Pushed branch: ${result.branchUrl}`); + + if (generationBaseSha != null) { + try { + const sanitizedName = this.config.generatorName?.replace(/\//g, "--"); + const tagName = + sanitizedName != null ? `fern-generation-base--${sanitizedName}` : "fern-generation-base"; + await repository.createAndPushTag(tagName, generationBaseSha); + this.logger.debug(`Pushed ${tagName} tag for generation tracking`); + result.generationBaseTagSha = generationBaseSha; + } catch (error) { + this.logger.debug( + `Could not push generation tag: ${error instanceof Error ? error.message : String(error)}` + ); + } + } + } + + // Runs after push so head SHA is available on remote + if (!this.config.previewMode) { + const headSha = await repository.getHeadSha(); + await this.postReplayConflictStatus(octokit, owner, repo, headSha, replayConflictInfo, replayResult); + + if (isUpdatingExistingPR && existingPR != null) { + await this.togglePrDraftState(octokit, existingPR, replayConflictInfo); + } + } + + const finalCommitMessage = this.config.commitMessage ?? "SDK Generation"; + const { prTitle, prBody } = parseCommitMessageForPR(finalCommitMessage); + const replaySection = formatReplayPrBody(replayResult, { branchName: prBranch }); + const enrichedBody = replaySection != null ? prBody + "\n\n---\n\n" + replaySection : prBody; + + if (isUpdatingExistingPR && existingPR != null) { + this.logger.info(`Updated existing pull request: ${existingPR.htmlUrl}`); + result.prUrl = existingPR.htmlUrl; + result.prNumber = existingPR.number; + + try { + await octokit.pulls.update({ + owner, + repo, + pull_number: existingPR.number, + title: prTitle, + body: enrichedBody + }); + this.logger.debug(`Updated PR #${existingPR.number} title and body`); + } catch (error) { + this.logger.debug( + `Failed to update PR title/body: ${error instanceof Error ? error.message : String(error)}` + ); + } + } else { + const head = `${owner}:${prBranch}`; + + const hasConflicts = replayConflictInfo?.hasConflicts === true; + try { + const { data: pullRequest } = await octokit.pulls.create({ + owner, + repo, + title: prTitle, + body: enrichedBody, + head, + base: baseBranch, + draft: hasConflicts + }); + + this.logger.info(`Created pull request: ${pullRequest.html_url}`); + result.prUrl = pullRequest.html_url; + result.prNumber = pullRequest.number; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + if (message.includes("A pull request already exists for")) { + this.logger.warn(`A pull request already exists for ${head}`); + } else { + throw error; + } + } + } + + if (!this.config.previewMode && replayConflictInfo?.hasConflicts === true && result.prNumber != null) { + await postConflictComments(octokit, owner, repo, result.prNumber, replayResult, this.logger); + } + + if ( + !this.config.previewMode && + result.prNumber != null && + replayConflictInfo?.hasConflicts === true && + replayResult != null + ) { + await this.postWebEditorFallbackComment( + octokit, + owner, + repo, + result.prNumber, + prBranch, + baseBranch, + replayResult + ); + } + + return result; + } + + private async executePushMode(repository: ClonedRepository): Promise { + if (this.config.branch != null) { + this.logger.debug(`Checking out branch ${this.config.branch}`); + await repository.checkout(this.config.branch); + } + + await this.ensureFernignore(); + + this.logger.debug("Committing changes..."); + const finalCommitMessage = this.config.commitMessage ?? "SDK Generation"; + await repository.commitAllChanges(finalCommitMessage); + this.logger.debug(`Committed changes to local copy of GitHub repository at ${this.outputDir}`); + + const result: GithubStepResult = { + executed: true, + success: true + }; + + if (!this.config.previewMode) { + await repository.pushWithRebasingRemote(); + + const pushedBranch = await repository.getCurrentBranch(); + result.branchUrl = `https://github.com/${this.config.uri}/tree/${pushedBranch}`; + this.logger.info(`Pushed branch: ${result.branchUrl}`); + } + + return result; + } + + private async ensureFernignore(): Promise { + this.logger.debug("Checking for .fernignore file..."); + const fernignorePath = join(this.outputDir, ".fernignore"); + try { + await access(fernignorePath); + this.logger.debug(".fernignore already exists"); + } catch { + this.logger.debug("Creating .fernignore file..."); + await writeFile(fernignorePath, "# Specify files that shouldn't be modified by Fern\n", "utf-8"); + } + } + + private deriveSkipCommit(replayResult: ReplayStepResult | undefined): boolean { + if (replayResult == null) { + return false; + } + return replayResult.executed && replayResult.flow != null && replayResult.flow !== "first-generation"; + } + + private deriveReplayConflictInfo(replayResult: ReplayStepResult | undefined): + | { + previousGenerationSha: string; + currentGenerationSha: string; + hasConflicts: boolean; + baseBranchHead?: string; + } + | undefined { + if (replayResult == null) { + return undefined; + } + const hasConflicts = (replayResult.patchesWithConflicts ?? 0) > 0; + const previousGenerationSha = replayResult.previousGenerationSha; + const currentGenerationSha = replayResult.currentGenerationSha; + if (previousGenerationSha != null && currentGenerationSha != null) { + return { + previousGenerationSha, + currentGenerationSha, + hasConflicts, + baseBranchHead: replayResult.baseBranchHead + }; + } + return undefined; + } + + private async togglePrDraftState( + octokit: Octokit, + existingPR: ExistingPullRequest, + replayConflictInfo: + | { + previousGenerationSha: string; + currentGenerationSha: string; + hasConflicts: boolean; + baseBranchHead?: string; + } + | undefined + ): Promise { + const hasConflicts = replayConflictInfo?.hasConflicts === true; + + if (hasConflicts && !existingPR.isDraft) { + await this.convertPrToDraft(octokit, existingPR.nodeId, existingPR.number); + } else if (!hasConflicts && existingPR.isDraft) { + await this.markPrReady(octokit, existingPR.nodeId, existingPR.number); + } + } + + private async convertPrToDraft(octokit: Octokit, nodeId: string, prNumber: number): Promise { + try { + await octokit.graphql( + `mutation($id: ID!) { + convertPullRequestToDraft(input: {pullRequestId: $id}) { + pullRequest { isDraft } + } + }`, + { id: nodeId } + ); + this.logger.info(`Converted PR #${prNumber} to draft due to replay conflicts`); + } catch (error) { + this.logger.debug( + `Could not convert PR to draft: ${error instanceof Error ? error.message : String(error)}` + ); + } + } + + private async markPrReady(octokit: Octokit, nodeId: string, prNumber: number): Promise { + try { + await octokit.graphql( + `mutation($id: ID!) { + markPullRequestReadyForReview(input: {pullRequestId: $id}) { + pullRequest { isDraft } + } + }`, + { id: nodeId } + ); + this.logger.info(`Marked PR #${prNumber} as ready (conflicts resolved)`); + } catch (error) { + this.logger.debug(`Could not mark PR as ready: ${error instanceof Error ? error.message : String(error)}`); + } + } + + private async postReplayConflictStatus( + octokit: Octokit, + owner: string, + repo: string, + sha: string, + replayConflictInfo: + | { + previousGenerationSha: string; + currentGenerationSha: string; + hasConflicts: boolean; + baseBranchHead?: string; + } + | undefined, + replayResult: ReplayStepResult | undefined + ): Promise { + if (replayResult == null || !replayResult.executed) { + return; + } + + const hasConflicts = replayConflictInfo?.hasConflicts === true; + const conflictFileCount = (replayResult.conflictDetails ?? []).reduce( + (sum, detail) => sum + detail.files.length, + 0 + ); + const sanitizedName = this.config.generatorName?.replace(/\//g, "--"); + const context = + sanitizedName != null ? `fern / sdk customizations / ${sanitizedName}` : "fern / sdk customizations"; + + try { + await octokit.repos.createCommitStatus({ + owner, + repo, + sha, + state: hasConflicts ? "failure" : "success", + context, + description: hasConflicts + ? `${conflictFileCount} file(s) need manual conflict resolution — see PR description` + : "All customizations applied" + }); + this.logger.debug(`Posted ${hasConflicts ? "failing" : "passing"} commit status (${context})`); + } catch (error) { + this.logger.debug( + `Could not post commit status: ${error instanceof Error ? error.message : String(error)}` + ); + } + } + + private async postWebEditorFallbackComment( + octokit: Octokit, + owner: string, + repo: string, + prNumber: number, + branchName: string, + baseBranch: string, + replayResult: ReplayStepResult + ): Promise { + try { + const webEditorLikelyDisabled = await this.isWebEditorLikelyDisabled( + octokit, + owner, + repo, + prNumber, + replayResult + ); + + if (!webEditorLikelyDisabled) { + this.logger.debug(`PR #${prNumber}: web conflict editor appears usable, skipping fallback comment`); + return; + } + + const comment = buildWebEditorFallbackComment(branchName, baseBranch); + await octokit.issues.createComment({ + owner, + repo, + issue_number: prNumber, + body: comment + }); + this.logger.info(`Posted web-editor fallback comment on PR #${prNumber}`); + } catch (error) { + this.logger.debug( + `Could not post web-editor fallback comment: ${error instanceof Error ? error.message : String(error)}` + ); + } + } + + private async isWebEditorLikelyDisabled( + octokit: Octokit, + owner: string, + repo: string, + prNumber: number, + replayResult: ReplayStepResult + ): Promise { + const conflictDetails = replayResult.conflictDetails ?? []; + const allConflictFiles = conflictDetails.flatMap((d) => d.files); + const totalConflictFiles = allConflictFiles.length; + + const hasFileDeletionConflict = allConflictFiles.some((f) => { + const status = (f.status ?? "").toLowerCase(); + const reason = (f.conflictReason ?? "").toLowerCase(); + return status === "skipped" || reason.includes("delete") || reason.includes("removed"); + }); + + if (hasFileDeletionConflict) { + this.logger.debug(`PR #${prNumber}: detected file deletion conflict, web editor likely disabled`); + return true; + } + + if (totalConflictFiles >= WEB_EDITOR_FILE_THRESHOLD) { + this.logger.debug( + `PR #${prNumber}: ${totalConflictFiles} conflicting files exceeds threshold (${WEB_EDITOR_FILE_THRESHOLD}), web editor likely disabled` + ); + return true; + } + + const totalHunks = (replayResult.conflicts ?? []).reduce( + (sum, fileConflict) => sum + fileConflict.conflicts.length, + 0 + ); + if (totalHunks >= WEB_EDITOR_HUNK_THRESHOLD) { + this.logger.debug( + `PR #${prNumber}: ${totalHunks} conflict hunks exceeds threshold (${WEB_EDITOR_HUNK_THRESHOLD}), web editor likely disabled` + ); + return true; + } + + const mergeableState = await this.pollMergeableState(octokit, owner, repo, prNumber); + if (mergeableState.mergeable === null) { + this.logger.debug(`PR #${prNumber}: mergeable state still null after polling, web editor likely disabled`); + return true; + } + + return false; + } + + private async pollMergeableState( + octokit: Octokit, + owner: string, + repo: string, + prNumber: number + ): Promise<{ mergeable: boolean | null; mergeableState: string }> { + for (let attempt = 0; attempt < MERGEABLE_POLL_MAX_ATTEMPTS; attempt++) { + const { data: pr } = await octokit.pulls.get({ + owner, + repo, + pull_number: prNumber + }); + + if (pr.mergeable != null) { + return { + mergeable: pr.mergeable, + mergeableState: pr.mergeable_state + }; + } + + if (attempt < MERGEABLE_POLL_MAX_ATTEMPTS - 1) { + await sleep(MERGEABLE_POLL_DELAY_MS); + } + } + + return { mergeable: null, mergeableState: "unknown" }; + } +} + +const WEB_EDITOR_FILE_THRESHOLD = 20; +const WEB_EDITOR_HUNK_THRESHOLD = 50; +const MERGEABLE_POLL_MAX_ATTEMPTS = 3; +const MERGEABLE_POLL_DELAY_MS = 1500; + +function buildWebEditorFallbackComment(branchName: string, baseBranch: string): string { + const lines = [ + `> **Note:** These conflicts may be too complex for GitHub's web editor. To resolve locally:`, + `>`, + `> 1. Check out this branch:`, + `> \`\`\`sh`, + `> git fetch origin && git checkout ${branchName}`, + `> \`\`\``, + `> 2. Merge the base branch:`, + `> \`\`\`sh`, + `> git merge origin/${baseBranch}`, + `> \`\`\``, + `> 3. Resolve conflicts in your editor (VS Code, IntelliJ, etc. have built-in merge tools)`, + `> 4. Commit and push:`, + `> \`\`\`sh`, + `> git add -A && git commit -m "resolve conflicts" && git push`, + `> \`\`\``, + `>`, + `> **Label guide:** In your editor's merge view, "current" = new generated code, "incoming" = your customization.` + ]; + return lines.join("\n"); +} + +function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/packages/generator-cli/src/pipeline/steps/ReplayStep.ts b/packages/generator-cli/src/pipeline/steps/ReplayStep.ts new file mode 100644 index 000000000000..63434cd3ee09 --- /dev/null +++ b/packages/generator-cli/src/pipeline/steps/ReplayStep.ts @@ -0,0 +1,84 @@ +import { replayRun } from "../../replay/replay-run"; +import type { PipelineLogger } from "../PipelineLogger"; +import type { PipelineContext, ReplayStepConfig, ReplayStepResult } from "../types"; +import { BaseStep } from "./BaseStep"; + +export class ReplayStep extends BaseStep { + readonly name = "replay"; + + constructor( + outputDir: string, + logger: PipelineLogger, + private readonly config: ReplayStepConfig, + private readonly cliVersion?: string, + private readonly generatorVersions?: Record, + private readonly generatorName?: string + ) { + super(outputDir, logger); + } + + async execute(_context: PipelineContext): Promise { + const result = await replayRun({ + outputDir: this.outputDir, + cliVersion: this.cliVersion, + generatorVersions: this.generatorVersions, + stageOnly: this.config.stageOnly ?? false, + generatorName: this.generatorName, + skipApplication: this.config.skipApplication, + logger: this.logger + }); + + if (result.report == null) { + return { + executed: true, + success: true, + previousGenerationSha: result.previousGenerationSha ?? undefined, + currentGenerationSha: result.currentGenerationSha ?? undefined, + baseBranchHead: result.baseBranchHead ?? undefined, + flow: "first-generation", + patchesDetected: 0, + patchesApplied: 0, + patchesWithConflicts: 0, + conflicts: [] + }; + } + + const report = result.report; + return { + executed: true, + success: true, + previousGenerationSha: result.previousGenerationSha ?? undefined, + currentGenerationSha: result.currentGenerationSha ?? undefined, + baseBranchHead: result.baseBranchHead ?? undefined, + flow: report.flow, + patchesDetected: report.patchesDetected, + patchesApplied: report.patchesApplied, + patchesWithConflicts: report.patchesWithConflicts, + patchesAbsorbed: report.patchesAbsorbed, + patchesRepointed: report.patchesRepointed, + patchesContentRebased: report.patchesContentRebased, + patchesKeptAsUserOwned: report.patchesKeptAsUserOwned, + conflicts: report.conflicts.map((fileResult) => ({ + filePath: fileResult.file, + conflicts: + fileResult.conflicts?.map((conflict) => ({ + startLine: conflict.startLine, + endLine: conflict.endLine, + ours: conflict.ours, + theirs: conflict.theirs + })) ?? [] + })), + conflictDetails: report.conflictDetails?.map((detail) => ({ + patchId: detail.patchId, + patchMessage: detail.patchMessage, + reason: detail.reason, + files: detail.files.map((f) => ({ + file: f.file, + status: f.status, + conflictReason: f.conflictReason + })) + })), + warnings: report.warnings + }; + } +} diff --git a/packages/generator-cli/src/pipeline/types.ts b/packages/generator-cli/src/pipeline/types.ts new file mode 100644 index 000000000000..bed777e0bbc5 --- /dev/null +++ b/packages/generator-cli/src/pipeline/types.ts @@ -0,0 +1,124 @@ +export interface PipelineConfig { + outputDir: string; + + // Step configs (optional, modular) + replay?: ReplayStepConfig; + fernignore?: FernignoreStepConfig; // PHASE 2: not implemented yet + github?: GithubStepConfig; + + // Global metadata + cliVersion?: string; + generatorVersions?: Record; + generatorName?: string; +} + +export interface PipelineContext { + previousStepResults: { + replay?: ReplayStepResult; + fernignore?: FernignoreStepResult; + }; +} + +export interface ReplayStepConfig { + enabled: boolean; + stageOnly?: boolean; + skipApplication?: boolean; +} + +export interface FernignoreStepConfig { + enabled: boolean; + customContents?: string; +} + +export interface GithubStepConfig { + enabled: boolean; + /** GitHub repository URI (e.g. "owner/repo") */ + uri: string; + /** GitHub auth token */ + token: string; + /** Output mode */ + mode: "push" | "pull-request"; + /** Target branch (base branch for PRs, push target for push mode) */ + branch?: string; + /** Commit message for the generation */ + commitMessage?: string; + /** Skip push/PR creation, just prepare branches locally */ + previewMode?: boolean; + /** Generator name for namespaced fern-generation-base tag */ + generatorName?: string; + /** Explicit override: whether replay already created commits (derived from replay context if omitted) */ + skipCommit?: boolean; + /** Explicit override: replay conflict info (derived from replay context if omitted) */ + replayConflictInfo?: { + previousGenerationSha: string; + currentGenerationSha: string; + hasConflicts: boolean; + baseBranchHead?: string; + }; +} + +export interface PipelineResult { + success: boolean; + steps: { + replay?: ReplayStepResult; + fernignore?: FernignoreStepResult; + github?: GithubStepResult; + }; + errors?: string[]; + warnings?: string[]; +} + +export interface ReplayStepResult extends StepResult { + flow?: "first-generation" | "no-patches" | "normal-regeneration" | "skip-application"; + patchesDetected?: number; + patchesApplied?: number; + patchesWithConflicts?: number; + patchesAbsorbed?: number; + patchesRepointed?: number; + patchesContentRebased?: number; + patchesKeptAsUserOwned?: number; + previousGenerationSha?: string; + currentGenerationSha?: string; + baseBranchHead?: string; + conflicts?: ConflictInfo[]; + conflictDetails?: Array<{ + patchId: string; + patchMessage: string; + reason?: string; + files: Array<{ + file: string; + status: string; + conflictReason?: string; + }>; + }>; + warnings?: string[]; +} + +export interface ConflictInfo { + filePath: string; + conflicts: Array<{ + startLine: number; + endLine: number; + ours: string[]; + theirs: string[]; + }>; +} + +export interface FernignoreStepResult extends StepResult { + pathsPreserved?: string[]; +} + +export interface GithubStepResult extends StepResult { + commitSha?: string; + branchUrl?: string; + prUrl?: string; + prNumber?: number; + updatedExistingPr?: boolean; + generationBaseTagSha?: string; +} + +export interface StepResult { + executed: boolean; + success: boolean; + errorMessage?: string; +} diff --git a/packages/generator-cli/src/replay/fernignore.ts b/packages/generator-cli/src/replay/fernignore.ts new file mode 100644 index 000000000000..3550a8dd0a92 --- /dev/null +++ b/packages/generator-cli/src/replay/fernignore.ts @@ -0,0 +1,37 @@ +import { existsSync, readFileSync, writeFileSync } from "fs"; +import { readFile, writeFile } from "fs/promises"; +import { join } from "path"; + +export const REPLAY_FERNIGNORE_ENTRIES = [".fern/replay.lock", ".fern/replay.yml"]; + +export async function ensureReplayFernignoreEntries(outputDir: string): Promise { + const fernignorePath = join(outputDir, ".fernignore"); + let content = ""; + try { + content = await readFile(fernignorePath, "utf-8"); + } catch { + // .fernignore doesn't exist yet + } + const lines = content.split("\n"); + const toAdd = REPLAY_FERNIGNORE_ENTRIES.filter((entry) => !lines.some((line) => line.trim() === entry)); + if (toAdd.length > 0) { + const suffix = content.length > 0 && !content.endsWith("\n") ? "\n" : ""; + await writeFile(fernignorePath, content + suffix + toAdd.join("\n") + "\n", "utf-8"); + return true; + } + return false; +} + +export function ensureReplayFernignoreEntriesSync(outputDir: string): void { + const fernignorePath = join(outputDir, ".fernignore"); + let content = ""; + if (existsSync(fernignorePath)) { + content = readFileSync(fernignorePath, "utf-8"); + } + const lines = content.split("\n"); + const toAdd = REPLAY_FERNIGNORE_ENTRIES.filter((entry) => !lines.some((line) => line.trim() === entry)); + if (toAdd.length > 0) { + const suffix = content.length > 0 && !content.endsWith("\n") ? "\n" : ""; + writeFileSync(fernignorePath, content + suffix + toAdd.join("\n") + "\n", "utf-8"); + } +} diff --git a/packages/generator-cli/src/replay/replay-init.ts b/packages/generator-cli/src/replay/replay-init.ts new file mode 100644 index 000000000000..055dabcf4bee --- /dev/null +++ b/packages/generator-cli/src/replay/replay-init.ts @@ -0,0 +1,266 @@ +import { cloneRepository, parseRepository } from "@fern-api/github"; +import { type BootstrapResult, bootstrap, FERN_BOT_EMAIL, FERN_BOT_NAME, GitClient } from "@fern-api/replay"; +import { Octokit } from "@octokit/rest"; +import { existsSync } from "fs"; +import { join } from "path"; +import tmp from "tmp-promise"; +import { ensureReplayFernignoreEntriesSync } from "./fernignore"; + +export interface ReplayInitParams { + /** GitHub repo URI (e.g., "fern-demo/fern-replay-testbed-java-sdk") */ + githubRepo: string; + /** GitHub token with push + PR permissions */ + token: string; + /** Report what would happen but don't create PR */ + dryRun?: boolean; + /** Max commits to scan for generation history */ + maxCommitsToScan?: number; + /** Title for the PR */ + prTitle?: string; + /** Body for the PR */ + prBody?: string; + /** Overwrite existing lockfile if present. Default: false */ + force?: boolean; + /** Scan git history for existing patches (migration). Default: false */ + importHistory?: boolean; +} + +export interface ReplayInitResult { + /** Bootstrap result from fern-replay */ + bootstrap: BootstrapResult; + /** URL of the created PR, if not dry-run */ + prUrl?: string; + /** Branch name used for the PR */ + branch?: string; +} + +/** + * Initialize Replay for an SDK repository. + * + * Creates a branch and opens a PR with the replay lockfile. + * + * Flow: + * 1. Clone the SDK repo + * 2. Run bootstrap() to scan history and create lockfile + * 3. Ensure .fernignore has replay entries + * 4. Commit with [fern-replay] prefix (recognized by isGenerationCommit) + * 5. Push branch and open PR + * + * The [fern-replay] commit message prefix ensures the bootstrap commit + * is NOT treated as a user customization patch by ReplayDetector. + */ +export async function replayInit(params: ReplayInitParams): Promise { + const { githubRepo, token, dryRun } = params; + + // 1. Clone the repo into a temp directory + const tmpDir = await tmp.dir({ unsafeCleanup: true }); + const repoPath = tmpDir.path; + const repository = await cloneRepository({ + githubRepository: githubRepo, + installationToken: token, + targetDirectory: repoPath + }); + + const defaultBranch = await repository.getDefaultBranch(); + + // 2. Run bootstrap + const bootstrapResult = await bootstrap(repoPath, { + dryRun, + fernignoreAction: "skip", + maxCommitsToScan: params.maxCommitsToScan, + force: params.force ?? false, + importHistory: params.importHistory + }); + + if (!bootstrapResult.generationCommit) { + return { bootstrap: bootstrapResult }; + } + + if (dryRun) { + return { bootstrap: bootstrapResult }; + } + + // 3. Ensure .fernignore has replay entries + ensureReplayFernignoreEntriesSync(repoPath); + + const commitMessage = `[fern-replay] Initialize Replay\n\nTracked ${bootstrapResult.patchesCreated} customization patch(es).\nBase generation: ${bootstrapResult.generationCommit.sha.slice(0, 7)}`; + + // Only add files that actually exist (replay.yml is only created during fernignore migration) + const filesToAdd = [".fern/replay.lock", ".fern/replay.yml", ".fernignore"].filter((f) => + existsSync(join(repoPath, f)) + ); + + // Create branch, commit, push, create PR + const branchName = `fern-replay/init-${Date.now()}`; + await repository.checkoutOrCreateLocal(branchName); + + const git = new GitClient(repoPath); + await git.exec(["add", ...filesToAdd]); + await git.exec([ + "-c", + `user.name=${FERN_BOT_NAME}`, + "-c", + `user.email=${FERN_BOT_EMAIL}`, + "commit", + "-m", + commitMessage + ]); + + await repository.pushUpstream(branchName); + + // Create PR + const parsed = parseRepository(githubRepo); + const octokit = new Octokit({ auth: token }); + const prTitle = params.prTitle ?? "[fern-replay] Initialize Replay for SDK customizations"; + const prBody = params.prBody ?? buildPrBody(bootstrapResult); + + const { data: pr } = await octokit.pulls.create({ + owner: parsed.owner, + repo: parsed.repo, + head: branchName, + base: defaultBranch, + title: prTitle, + body: prBody + }); + + return { + bootstrap: bootstrapResult, + prUrl: pr.html_url, + branch: branchName + }; +} + +export interface BootstrapLogEntry { + level: "info" | "warn"; + message: string; +} + +/** + * Formats a BootstrapResult into structured log entries. + * Used by both the generator-cli CLI and the fern CLI to produce + * consistent output without duplicating formatting logic. + */ +export function formatBootstrapSummary(result: ReplayInitResult): BootstrapLogEntry[] { + const entries: BootstrapLogEntry[] = []; + const bs = result.bootstrap; + + if (!bs.generationCommit) { + const lockfileExists = bs.warnings.some((w) => w.includes("lockfile already exists")); + if (lockfileExists) { + entries.push({ + level: "warn", + message: "Replay is already initialized. Use --force to re-initialize from scratch." + }); + } else { + entries.push({ level: "warn", message: "No generation commits found in repository history." }); + entries.push({ + level: "info", + message: "Replay requires at least one SDK generation commit to establish a baseline." + }); + } + return entries; + } + + entries.push({ + level: "info", + message: `Generation commit: ${bs.generationCommit.sha.slice(0, 7)} "${bs.generationCommit.message.slice(0, 60)}"` + }); + entries.push({ + level: "info", + message: `Scanned commits since: ${bs.scannedSinceGeneration.slice(0, 7)} (last generation)` + }); + if (bs.staleGenerationsSkipped > 0) { + entries.push({ + level: "info", + message: `Skipped ${bs.staleGenerationsSkipped} older generation(s) — only tracking recent commits` + }); + } + if (bs.patchesCreated === 0 && bs.patches.length === 0) { + entries.push({ + level: "info", + message: "Clean start — tracking future edits only" + }); + } else { + entries.push({ level: "info", message: `Patches created: ${bs.patchesCreated}` }); + + if (bs.patches.length > 0) { + entries.push({ level: "info", message: "\nPatches:" }); + for (const patch of bs.patches) { + entries.push({ + level: "info", + message: ` ${patch.id}: "${patch.original_message.slice(0, 50)}" (${patch.files.length} files)` + }); + } + } + } + + if (bs.warnings.length > 0) { + entries.push({ level: "info", message: "\nWarnings:" }); + for (const w of bs.warnings) { + entries.push({ level: "warn", message: ` ${w}` }); + } + } + + if (result.prUrl) { + entries.push({ level: "info", message: `\nPR created: ${result.prUrl}` }); + entries.push({ level: "info", message: "Merge the PR to enable Replay for this repository." }); + } else if (result.branch) { + entries.push({ level: "info", message: `\nPushed to branch: ${result.branch}` }); + } + + return entries; +} + +function buildPrBody(result: BootstrapResult): string { + const lines: string[] = [ + "## Replay Initialization", + "", + "This PR initializes [Fern Replay](https://buildwithfern.com) for this SDK repository.", + "Replay automatically preserves your customizations across SDK regenerations.", + "", + `**Base generation commit:** \`${result.generationCommit?.sha.slice(0, 7)}\`` + ]; + + if (result.patchesCreated === 0 && result.patches.length === 0) { + lines.push( + "", + "Clean start — no historical patches imported. Future customizations will be tracked automatically.", + "" + ); + } else { + lines.push(`**Customization patches tracked:** ${result.patchesCreated}`, ""); + + if (result.staleGenerationsSkipped > 0) { + lines.push( + `> **Note:** Replay tracks customizations made since the last generation commit (\`${result.scannedSinceGeneration.slice(0, 7)}\`).`, + `> ${result.staleGenerationsSkipped} older generation(s) were skipped — commits regenerated over multiple times are not tracked.`, + "" + ); + } + + if (result.patches.length > 0) { + lines.push("### Tracked Patches"); + lines.push(""); + for (const patch of result.patches) { + lines.push( + `- **${patch.id}**: ${patch.original_message.slice(0, 60)} (${patch.files.length} file${patch.files.length === 1 ? "" : "s"})` + ); + } + lines.push(""); + } + } + + if (result.warnings.length > 0) { + lines.push("### Warnings"); + lines.push(""); + for (const w of result.warnings) { + lines.push(`- ${w}`); + } + lines.push(""); + } + + lines.push("---"); + lines.push("Once merged, your customizations will be automatically preserved on future `fern generate` runs."); + + return lines.join("\n"); +} diff --git a/packages/generator-cli/src/replay/replay-run.ts b/packages/generator-cli/src/replay/replay-run.ts new file mode 100644 index 000000000000..3367bd516b24 --- /dev/null +++ b/packages/generator-cli/src/replay/replay-run.ts @@ -0,0 +1,193 @@ +import { LockfileManager, type ReplayReport, ReplayService } from "@fern-api/replay"; +import { execFileSync } from "child_process"; +import { existsSync } from "fs"; +import { join } from "path"; +import type { PipelineLogger } from "../pipeline/PipelineLogger"; +import { ensureReplayFernignoreEntries } from "./fernignore"; + +export interface ReplayRunParams { + outputDir: string; + cliVersion?: string; + generatorVersions?: Record; + /** Don't commit, just stage changes. Default: false */ + stageOnly?: boolean; + /** Generator name for namespaced tag lookup (e.g. "fernapi/fern-typescript-node-sdk") */ + generatorName?: string; + /** Commit generation + update lockfile, skip patch detection/application */ + skipApplication?: boolean; + /** Optional logger for diagnostic output */ + logger?: PipelineLogger; +} + +export interface ReplayRunResult { + /** null if replay wasn't initialized (no lockfile) */ + report: ReplayReport | null; + /** Whether .fernignore was updated */ + fernignoreUpdated: boolean; + /** SHA of the [fern-generated] commit before this replay run (null if first generation or unreadable) */ + previousGenerationSha: string | null; + /** SHA of the [fern-generated] commit created by this replay run (null if replay didn't run or unreadable) */ + currentGenerationSha: string | null; + /** SHA of main's HEAD before replay ran. Always on main's lineage, stable after squash merges. */ + baseBranchHead: string | null; +} + +export async function replayRun(params: ReplayRunParams): Promise { + const { + outputDir, + cliVersion, + generatorVersions, + stageOnly = false, + generatorName, + skipApplication, + logger + } = params; + const lockfilePath = join(outputDir, ".fern", "replay.lock"); + + if (!existsSync(lockfilePath)) { + return { + report: null, + fernignoreUpdated: false, + previousGenerationSha: null, + currentGenerationSha: null, + baseBranchHead: null + }; + } + + // Capture main's HEAD before replay modifies anything. + // This is always on main's lineage, unlike generation SHAs which + // may end up on dead branches after squash merges. + const baseBranchHead = gitRevParse(outputDir, "HEAD"); + + let previousGenerationSha: string | null = null; + let prevBaseBranchHead: string | null = null; + try { + const lockManager = new LockfileManager(outputDir); + if (lockManager.exists()) { + const lock = lockManager.read(); + previousGenerationSha = lock.current_generation; + const latestGen = lock.generations.find((g) => g.commit_sha === lock.current_generation); + prevBaseBranchHead = latestGen?.base_branch_head ?? null; + } + } catch { + // If lockfile can't be read, proceed without SHA + } + + // Always check tag — old generation IS reachable, so !isReachable is not a valid gate + if (previousGenerationSha != null) { + const sanitizedName = generatorName?.replace(/\//g, "--"); + const namespacedTag = sanitizedName != null ? `fern-generation-base--${sanitizedName}` : null; + const tagSha = + (namespacedTag != null ? gitRevParse(outputDir, namespacedTag) : null) ?? + gitRevParse(outputDir, "fern-generation-base"); + + let shouldSync = false; + + if (tagSha != null) { + const tagParent = gitRevParse(outputDir, `${tagSha}^`); + if (tagParent === prevBaseBranchHead || tagParent === previousGenerationSha) { + const tagDistance = gitDiffNameOnly(outputDir, tagSha, "HEAD").length; + const lockDistance = gitDiffNameOnly( + outputDir, + prevBaseBranchHead ?? previousGenerationSha, + "HEAD" + ).length; + shouldSync = tagDistance < lockDistance; + } + } + + if (shouldSync && tagSha != null) { + const syncService = new ReplayService(outputDir, { enabled: true }); + await syncService.syncFromDivergentMerge(tagSha, { + cliVersion, + generatorVersions, + baseBranchHead: baseBranchHead ?? undefined + }); + + try { + const freshLockManager = new LockfileManager(outputDir); + if (freshLockManager.exists()) { + previousGenerationSha = freshLockManager.read().current_generation; + } + } catch { + // proceed with original SHA + } + } + } + + const service = new ReplayService(outputDir, { + enabled: true + }); + + let report: ReplayReport; + try { + report = await service.runReplay({ + cliVersion, + generatorVersions, + stageOnly, + skipApplication, + baseBranchHead: baseBranchHead ?? undefined + }); + } catch (error) { + // Don't fail generation because of replay errors + logger?.warn("Replay failed, continuing without replay: " + String(error)); + return { + report: null, + fernignoreUpdated: false, + previousGenerationSha, + currentGenerationSha: null, + baseBranchHead + }; + } + + let currentGenerationSha: string | null = null; + let resolvedBaseBranchHead: string | null = baseBranchHead; + try { + const freshLockManager = new LockfileManager(outputDir); + if (freshLockManager.exists()) { + const freshLock = freshLockManager.read(); + currentGenerationSha = freshLock.current_generation; + const latestGen = freshLock.generations.find((g) => g.commit_sha === freshLock.current_generation); + if (latestGen?.base_branch_head) { + resolvedBaseBranchHead = latestGen.base_branch_head; + } + } + } catch { + // If lockfile can't be read, proceed with captured values + } + + const fernignoreUpdated = await ensureReplayFernignoreEntries(outputDir); + + return { + report, + fernignoreUpdated, + previousGenerationSha, + currentGenerationSha, + baseBranchHead: resolvedBaseBranchHead + }; +} + +function gitRevParse(cwd: string, rev: string): string | null { + try { + return execFileSync("git", ["rev-parse", "--verify", rev], { + cwd, + encoding: "utf-8", + stdio: "pipe" + }).trim(); + } catch { + return null; + } +} + +function gitDiffNameOnly(cwd: string, from: string, to: string): string[] { + try { + const output = execFileSync("git", ["diff", "--name-only", from, to], { + cwd, + encoding: "utf-8", + stdio: "pipe" + }); + return output.trim().split("\n").filter(Boolean); + } catch { + return []; + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8fbb838055a1..31fc522d0af2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -255,9 +255,6 @@ catalogs: '@inquirer/prompts': specifier: ^7.1.0 version: 7.10.1 - '@octokit/rest': - specifier: ^20.1.2 - version: 20.1.2 '@open-rpc/meta-schema': specifier: ^1.14.9 version: 1.14.9 @@ -321,9 +318,6 @@ catalogs: '@types/is-ci': specifier: ^3.0.4 version: 3.0.4 - '@types/jest': - specifier: ^29.5.11 - version: 29.5.14 '@types/js-yaml': specifier: ^4.0.8 version: 4.0.9 @@ -440,7 +434,7 @@ catalogs: version: 7.1.1 chalk: specifier: ^5.3.0 - version: 5.6.2 + version: 5.4.1 chardet: specifier: ^2.0.0 version: 2.1.1 @@ -458,7 +452,7 @@ catalogs: version: 10.1.0 cspell: specifier: ^9.6.4 - version: 9.6.4 + version: 9.7.0 dayjs: specifier: ^1.11.11 version: 1.11.19 @@ -557,7 +551,7 @@ catalogs: version: 3.2.2 knip: specifier: ^5.84.1 - version: 5.84.1 + version: 5.85.0 latest-version: specifier: ^9.0.0 version: 9.0.0 @@ -823,13 +817,13 @@ importers: version: 10.1.0 cspell: specifier: 'catalog:' - version: 9.6.4 + version: 9.7.0 husky: specifier: 'catalog:' version: 9.1.7 knip: specifier: 'catalog:' - version: 5.84.1(@types/node@25.2.1)(typescript@5.9.3) + version: 5.85.0(@types/node@25.2.1)(typescript@5.9.3) prettier: specifier: 'catalog:' version: 3.8.1 @@ -2286,6 +2280,173 @@ importers: specifier: 'catalog:' version: 4.0.18(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3) + generators/ruby/cli: + dependencies: + '@fern-api/base-generator': + specifier: workspace:* + version: link:../../base + '@fern-api/fs-utils': + specifier: workspace:* + version: link:../../../packages/commons/fs-utils + '@fern-api/logger': + specifier: workspace:* + version: link:../../../packages/cli/logger + '@fern-api/logging-execa': + specifier: workspace:* + version: link:../../../packages/commons/logging-execa + '@fern-fern/ir-sdk': + specifier: ^39 + version: 39.0.0 + tmp-promise: + specifier: ^3.0.3 + version: 3.0.3 + devDependencies: + '@fern-api/configs': + specifier: workspace:* + version: link:../../../packages/configs + '@types/node': + specifier: 18.15.3 + version: 18.15.3 + depcheck: + specifier: ^1.4.7 + version: 1.4.7 + typescript: + specifier: 5.9.3 + version: 5.9.3 + vitest: + specifier: ^4.0.8 + version: 4.0.18(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3) + + generators/ruby/codegen: + dependencies: + '@fern-api/fs-utils': + specifier: workspace:* + version: link:../../../packages/commons/fs-utils + '@fern-fern/generator-exec-sdk': + specifier: ^0.0.1167 + version: 0.0.1167 + '@fern-fern/ir-sdk': + specifier: ^39 + version: 39.0.0 + lodash-es: + specifier: ^4.17.21 + version: 4.17.23 + url-join: + specifier: ^4.0.1 + version: 4.0.1 + zod: + specifier: ^3.22.3 + version: 3.25.76 + devDependencies: + '@fern-api/configs': + specifier: workspace:* + version: link:../../../packages/configs + '@types/lodash-es': + specifier: ^4.17.12 + version: 4.17.12 + '@types/node': + specifier: 18.15.3 + version: 18.15.3 + '@types/url-join': + specifier: ^4.0.3 + version: 4.0.3 + depcheck: + specifier: ^1.4.7 + version: 1.4.7 + typescript: + specifier: 5.9.3 + version: 5.9.3 + vitest: + specifier: ^4.0.8 + version: 4.0.18(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3) + + generators/ruby/model: + devDependencies: + '@fern-api/base-generator': + specifier: workspace:* + version: link:../../base + '@fern-api/configs': + specifier: workspace:* + version: link:../../../packages/configs + '@fern-api/fs-utils': + specifier: workspace:* + version: link:../../../packages/commons/fs-utils + '@fern-api/logging-execa': + specifier: workspace:* + version: link:../../../packages/commons/logging-execa + '@fern-api/ruby-codegen': + specifier: workspace:* + version: link:../codegen + '@fern-api/ruby-generator-cli': + specifier: workspace:* + version: link:../cli + '@fern-fern/generator-exec-sdk': + specifier: ^0.0.1167 + version: 0.0.1167 + '@fern-fern/ir-sdk': + specifier: ^39 + version: 39.0.0 + '@types/node': + specifier: 18.15.3 + version: 18.15.3 + depcheck: + specifier: ^1.4.7 + version: 1.4.7 + typescript: + specifier: 5.9.3 + version: 5.9.3 + vitest: + specifier: ^4.0.8 + version: 4.0.18(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3) + zod: + specifier: ^3.22.3 + version: 3.25.76 + + generators/ruby/sdk: + devDependencies: + '@fern-api/base-generator': + specifier: workspace:* + version: link:../../base + '@fern-api/configs': + specifier: workspace:* + version: link:../../../packages/configs + '@fern-api/fern-ruby-model': + specifier: workspace:* + version: link:../model + '@fern-api/fs-utils': + specifier: workspace:* + version: link:../../../packages/commons/fs-utils + '@fern-api/logging-execa': + specifier: workspace:* + version: link:../../../packages/commons/logging-execa + '@fern-api/ruby-codegen': + specifier: workspace:* + version: link:../codegen + '@fern-api/ruby-generator-cli': + specifier: workspace:* + version: link:../cli + '@fern-fern/generator-exec-sdk': + specifier: ^0.0.1167 + version: 0.0.1167 + '@fern-fern/ir-sdk': + specifier: ^39 + version: 39.0.0 + '@types/node': + specifier: 18.15.3 + version: 18.15.3 + depcheck: + specifier: ^1.4.7 + version: 1.4.7 + typescript: + specifier: 5.9.3 + version: 5.9.3 + vitest: + specifier: ^4.0.8 + version: 4.0.18(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3) + zod: + specifier: ^3.22.3 + version: 3.25.76 + generators/rust/base: dependencies: '@fern-api/base-generator': @@ -2694,6 +2855,126 @@ importers: specifier: 'catalog:' version: 3.25.76 + generators/typescript-mcp/base: + devDependencies: + '@fern-api/base-generator': + specifier: workspace:* + version: link:../../base + '@fern-api/configs': + specifier: workspace:* + version: link:../../../packages/configs + '@fern-api/fs-utils': + specifier: workspace:* + version: link:../../../packages/commons/fs-utils + '@fern-api/logger': + specifier: workspace:* + version: link:../../../packages/cli/logger + '@fern-api/logging-execa': + specifier: workspace:* + version: link:../../../packages/commons/logging-execa + '@fern-api/typescript-ast': + specifier: workspace:* + version: link:../../typescript-v2/ast + '@fern-api/typescript-formatter': + specifier: workspace:* + version: link:../../typescript-v2/formatter + '@fern-fern/ir-sdk': + specifier: ^58.2.0 + version: 58.3.0 + '@types/lodash-es': + specifier: ^4.17.12 + version: 4.17.12 + '@types/node': + specifier: 18.15.3 + version: 18.15.3 + depcheck: + specifier: ^1.4.7 + version: 1.4.7 + lodash-es: + specifier: ^4.17.21 + version: 4.17.23 + typescript: + specifier: 5.9.3 + version: 5.9.3 + vitest: + specifier: ^4.0.8 + version: 4.0.18(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3) + + generators/typescript-mcp/model: + devDependencies: + '@fern-api/base-generator': + specifier: workspace:* + version: link:../../base + '@fern-api/configs': + specifier: workspace:* + version: link:../../../packages/configs + '@fern-api/fs-utils': + specifier: workspace:* + version: link:../../../packages/commons/fs-utils + '@fern-api/typescript-ast': + specifier: workspace:* + version: link:../../typescript-v2/ast + '@fern-api/typescript-mcp-base': + specifier: workspace:* + version: link:../base + '@fern-fern/ir-sdk': + specifier: ^58.2.0 + version: 58.3.0 + '@types/node': + specifier: 18.15.3 + version: 18.15.3 + depcheck: + specifier: ^1.4.7 + version: 1.4.7 + typescript: + specifier: 5.9.3 + version: 5.9.3 + vitest: + specifier: ^4.0.8 + version: 4.0.18(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3) + + generators/typescript-mcp/server: + devDependencies: + '@fern-api/base-generator': + specifier: workspace:* + version: link:../../base + '@fern-api/configs': + specifier: workspace:* + version: link:../../../packages/configs + '@fern-api/fs-utils': + specifier: workspace:* + version: link:../../../packages/commons/fs-utils + '@fern-api/typescript-ast': + specifier: workspace:* + version: link:../../typescript-v2/ast + '@fern-api/typescript-mcp-base': + specifier: workspace:* + version: link:../base + '@fern-api/typescript-mcp-model': + specifier: workspace:* + version: link:../model + '@fern-fern/generator-exec-sdk': + specifier: ^0.0.1167 + version: 0.0.1167 + '@fern-fern/ir-sdk': + specifier: ^58.2.0 + version: 58.3.0 + '@fern-typescript/sdk-generator-cli': + specifier: workspace:* + version: link:../../typescript/sdk/cli + '@types/node': + specifier: 18.15.3 + version: 18.15.3 + depcheck: + specifier: ^1.4.7 + version: 1.4.7 + typescript: + specifier: 5.9.3 + version: 5.9.3 + vitest: + specifier: ^4.0.8 + version: 4.0.18(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3) + generators/typescript-v2/ast: dependencies: zod: @@ -4741,6 +5022,9 @@ importers: '@fern-api/fs-utils': specifier: workspace:* version: link:../../commons/fs-utils + '@fern-api/generator-cli': + specifier: workspace:* + version: link:../../generator-cli '@fern-api/generators-validator': specifier: workspace:* version: link:../yaml/generators-validator @@ -4890,7 +5174,7 @@ importers: version: 7.1.1 chalk: specifier: 'catalog:' - version: 5.6.2 + version: 5.4.1 cli-progress: specifier: 'catalog:' version: 3.12.0 @@ -4968,7 +5252,7 @@ importers: dependencies: chalk: specifier: 'catalog:' - version: 5.6.2 + version: 5.4.1 devDependencies: '@fern-api/configs': specifier: workspace:* @@ -5029,7 +5313,7 @@ importers: version: 0.0.58 chalk: specifier: 'catalog:' - version: 5.6.2 + version: 5.4.1 find-up: specifier: 'catalog:' version: 6.3.0 @@ -5217,7 +5501,7 @@ importers: version: 17.0.35 chalk: specifier: 'catalog:' - version: 5.6.2 + version: 5.4.1 find-up: specifier: 'catalog:' version: 6.3.0 @@ -5658,7 +5942,7 @@ importers: version: link:../task-context chalk: specifier: 'catalog:' - version: 5.6.2 + version: 5.4.1 cli-progress: specifier: 'catalog:' version: 3.12.0 @@ -5913,7 +6197,7 @@ importers: version: link:../../task-context chalk: specifier: 'catalog:' - version: 5.6.2 + version: 5.4.1 yaml: specifier: 2.3.3 version: 2.3.3 @@ -6045,7 +6329,7 @@ importers: version: link:../../task-context chalk: specifier: 'catalog:' - version: 5.6.2 + version: 5.4.1 lodash-es: specifier: 'catalog:' version: 4.17.23 @@ -6497,6 +6781,9 @@ importers: '@fern-api/fs-utils': specifier: workspace:* version: link:../../../../commons/fs-utils + '@fern-api/generator-cli': + specifier: workspace:* + version: link:../../../../generator-cli '@fern-api/github': specifier: workspace:* version: link:../../../../commons/github @@ -6554,12 +6841,9 @@ importers: '@fern-fern/generator-exec-sdk': specifier: 'catalog:' version: 0.0.1167 - '@octokit/rest': - specifier: 'catalog:' - version: 20.1.2 chalk: specifier: 'catalog:' - version: 5.6.2 + version: 5.4.1 decompress: specifier: 'catalog:' version: 4.2.1 @@ -6704,7 +6988,7 @@ importers: version: 1.13.5 chalk: specifier: 'catalog:' - version: 5.6.2 + version: 5.4.1 form-data: specifier: ^4.0.4 version: 4.0.5 @@ -6835,7 +7119,7 @@ importers: version: 1.13.5 chalk: specifier: 'catalog:' - version: 5.6.2 + version: 5.4.1 fs-extra: specifier: 'catalog:' version: 11.3.3 @@ -6937,7 +7221,7 @@ importers: version: 7.1.1 chalk: specifier: 'catalog:' - version: 5.6.2 + version: 5.4.1 inquirer: specifier: ^9.2.23 version: 9.3.8(@types/node@18.15.3) @@ -7069,7 +7353,7 @@ importers: version: link:../workspace/loader chalk: specifier: 'catalog:' - version: 5.6.2 + version: 5.4.1 devDependencies: '@fern-api/configs': specifier: workspace:* @@ -7426,7 +7710,7 @@ importers: version: 1.13.5 chalk: specifier: 'catalog:' - version: 5.6.2 + version: 5.4.1 js-yaml: specifier: 'catalog:' version: 4.1.1 @@ -7514,7 +7798,7 @@ importers: version: link:../../task-context chalk: specifier: 'catalog:' - version: 5.6.2 + version: 5.4.1 js-yaml: specifier: 'catalog:' version: 4.1.1 @@ -7636,7 +7920,7 @@ importers: version: 1.4.6 chalk: specifier: 'catalog:' - version: 5.6.2 + version: 5.4.1 chardet: specifier: 'catalog:' version: 2.1.1 @@ -8167,15 +8451,18 @@ importers: '@fern-api/github': specifier: workspace:* version: link:../commons/github + '@fern-api/replay': + specifier: ^0.6.0 + version: 0.6.0 '@octokit/rest': - specifier: 'catalog:' + specifier: ^20.1.2 version: 20.1.2 '@types/jest': - specifier: 'catalog:' + specifier: ^29.5.11 version: 29.5.14 '@types/node': - specifier: 'catalog:' - version: 18.15.3 + specifier: ^22.0.0 + version: 22.19.11 '@types/yargs': specifier: ^17.0.32 version: 17.0.35 @@ -8199,7 +8486,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.0.18(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3) + version: 4.0.18(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3) yargs: specifier: ^17.4.1 version: 17.7.2 @@ -8223,7 +8510,7 @@ importers: version: 18.15.3 tsdown: specifier: 'catalog:' - version: 0.20.1(oxc-resolver@11.17.1)(typescript@5.9.3) + version: 0.20.1(oxc-resolver@11.18.0)(typescript@5.9.3) typescript: specifier: 'catalog:' version: 5.9.3 @@ -8311,7 +8598,7 @@ importers: version: 6.0.0 minimatch: specifier: '>=10.2.1' - version: 10.2.1 + version: 10.2.2 devDependencies: '@fern-api/api-workspace-commons': specifier: workspace:* @@ -8381,7 +8668,7 @@ importers: version: 3.0.1(ajv@8.18.0) chalk: specifier: 'catalog:' - version: 5.6.2 + version: 5.4.1 console-table-printer: specifier: 'catalog:' version: 2.15.0 @@ -8460,10 +8747,18 @@ importers: packages: + '@babel/code-frame@7.28.6': + resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==} + engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.6': + resolution: {integrity: sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.29.0': resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} engines: {node: '>=6.9.0'} @@ -8472,6 +8767,10 @@ packages: resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} engines: {node: '>=6.9.0'} + '@babel/generator@7.28.6': + resolution: {integrity: sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==} + engines: {node: '>=6.9.0'} + '@babel/generator@7.29.1': resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} engines: {node: '>=6.9.0'} @@ -8591,6 +8890,11 @@ packages: resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} engines: {node: '>=6.9.0'} + '@babel/parser@7.28.6': + resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/parser@7.29.0': resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} engines: {node: '>=6.0.0'} @@ -9008,6 +9312,10 @@ packages: resolution: {integrity: sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==} engines: {node: '>=6.9.0'} + '@babel/types@7.28.6': + resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} + engines: {node: '>=6.9.0'} + '@babel/types@7.29.0': resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} @@ -9144,36 +9452,36 @@ packages: '@cacheable/utils@2.3.3': resolution: {integrity: sha512-JsXDL70gQ+1Vc2W/KUFfkAJzgb4puKwwKehNLuB+HrNKWf91O736kGfxn4KujXCCSuh6mRRL4XEB0PkAFjWS0A==} - '@cspell/cspell-bundled-dicts@9.6.4': - resolution: {integrity: sha512-OIiPQuB7XQ6rnUv4KaCwHr9vNwbh6VZ4GfgQjcThT0oz0hkL6E5Ar3tq54K9jyqE9ylcHqpRuXUgnKgio6Hlig==} + '@cspell/cspell-bundled-dicts@9.7.0': + resolution: {integrity: sha512-s7h1vo++Q3AsfQa3cs0u/KGwm3SYInuIlC4kjlCBWjQmb4KddiZB5O1u0+3TlA7GycHb5M4CR7MDfHUICgJf+w==} engines: {node: '>=20'} - '@cspell/cspell-json-reporter@9.6.4': - resolution: {integrity: sha512-rGYSDnDWACrUyovfN8M/LM8CCFSKjYd2kehbNS7YMPk0Jk+rLk6sgt5WYu3ty45otXCkiO07bjUo/81wBLet7A==} + '@cspell/cspell-json-reporter@9.7.0': + resolution: {integrity: sha512-6xpGXlMtQA3hV2BCAQcPkpx9eI12I0o01i9eRqSSEDKtxuAnnrejbcCpL+5OboAjTp3/BSeNYSnhuWYLkSITWQ==} engines: {node: '>=20'} - '@cspell/cspell-performance-monitor@9.6.4': - resolution: {integrity: sha512-exuqxV1IVfZkasg57ZjUbaHeZDd6Mdbsbe5FBT3+XaVnRij+wpY2oEW9+kIOL5MOQE3bgQKgu37iMtA1NlCrGA==} + '@cspell/cspell-performance-monitor@9.7.0': + resolution: {integrity: sha512-w1PZIFXuvjnC6mQHyYAFnrsn5MzKnEcEkcK1bj4OG00bAt7WX2VUA/eNNt9c1iHozCQ+FcRYlfbGxuBmNyzSgw==} engines: {node: '>=20.18'} - '@cspell/cspell-pipe@9.6.4': - resolution: {integrity: sha512-vVxajTG9Ko01oHk8HPsMLajcLrd9AfkOk6vdgFI4FD7ZPq1CY0hfTmfmJ8bzZ4/QkqXglTvePdSgHQVJeltwWw==} + '@cspell/cspell-pipe@9.7.0': + resolution: {integrity: sha512-iiisyRpJciU9SOHNSi0ZEK0pqbEMFRatI/R4O+trVKb+W44p4MNGClLVRWPGUmsFbZKPJL3jDtz0wPlG0/JCZA==} engines: {node: '>=20'} - '@cspell/cspell-resolver@9.6.4': - resolution: {integrity: sha512-3xsgZEqqH9Uj8ZYLBnWbnsHz8wphgaeuWKcNDqgwoMjvwTMQLGoXjHht8Jx5yxd2e080lB7fJax8TaBdCzmFFA==} + '@cspell/cspell-resolver@9.7.0': + resolution: {integrity: sha512-uiEgS238mdabDnwavo6HXt8K98jlh/jpm7NONroM9NTr9rzck2VZKD2kXEj85wDNMtRsRXNoywTjwQ8WTB6/+w==} engines: {node: '>=20'} - '@cspell/cspell-service-bus@9.6.4': - resolution: {integrity: sha512-oGNEzP1gJ43rLklJQjOk5PsfX0mZkLjV19djGptb9xZQeC2qAUxnaAbZtWt5CE8ni2iiTaRmgNRbUqAhRCnjew==} + '@cspell/cspell-service-bus@9.7.0': + resolution: {integrity: sha512-fkqtaCkg4jY/FotmzjhIavbXuH0AgUJxZk78Ktf4XlhqOZ4wDeUWrCf220bva4mh3TWiLx/ae9lIlpl59Vx6hA==} engines: {node: '>=20'} - '@cspell/cspell-types@9.6.4': - resolution: {integrity: sha512-lf6d+BdMkJIFCxx2FpajLpqVGGyaGUNFU6jhEM6QUPeGuoA5et2kJXrL0NSY2uWLOVyYYc/FPjzlbe8trA9tBQ==} + '@cspell/cspell-types@9.7.0': + resolution: {integrity: sha512-Tdfx4eH2uS+gv9V9NCr3Rz+c7RSS6ntXp3Blliud18ibRUlRxO9dTaOjG4iv4x0nAmMeedP1ORkEpeXSkh2QiQ==} engines: {node: '>=20'} - '@cspell/cspell-worker@9.6.4': - resolution: {integrity: sha512-anacKDOZzDfPzuDeFOXGI2tFBYiRRCSnIZP/AOyJ9zTvEQcqq5p/ak18nJ5OQyDr2NG7ovJiCDT5YNiH2Vdg/g==} + '@cspell/cspell-worker@9.7.0': + resolution: {integrity: sha512-cjEApFF0aOAa1vTUk+e7xP8ofK7iC7hsRzj1FmvvVQz8PoLWPRaq+1bT89ypPsZQvavqm5sIgb97S60/aW4TVg==} engines: {node: '>=20.18'} '@cspell/dict-ada@4.1.1': @@ -9215,8 +9523,8 @@ packages: '@cspell/dict-docker@1.1.17': resolution: {integrity: sha512-OcnVTIpHIYYKhztNTyK8ShAnXTfnqs43hVH6p0py0wlcwRIXe5uj4f12n7zPf2CeBI7JAlPjEsV0Rlf4hbz/xQ==} - '@cspell/dict-dotnet@5.0.11': - resolution: {integrity: sha512-LSVKhpFf/ASTWJcfYeS0Sykcl1gVMsv2Z5Eo0TnTMSTLV3738HH+66pIsjUTChqU6SF3gKPuCe6EOaRYqb/evA==} + '@cspell/dict-dotnet@5.0.12': + resolution: {integrity: sha512-FiV934kNieIjGTkiApu/WKvLYi/KBpvfWB2TSqpDQtmXZlt3uSa5blwblO1ZC8OvjH8RCq/31H5IdEYmTaZS7A==} '@cspell/dict-elixir@4.0.8': resolution: {integrity: sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q==} @@ -9278,8 +9586,8 @@ packages: '@cspell/dict-kotlin@1.1.1': resolution: {integrity: sha512-J3NzzfgmxRvEeOe3qUXnSJQCd38i/dpF9/t3quuWh6gXM+krsAXP75dY1CzDmS8mrJAlBdVBeAW5eAZTD8g86Q==} - '@cspell/dict-latex@5.0.0': - resolution: {integrity: sha512-HUrIqUVohM6P0+5b7BsdAdb0STIv0aaFBvguI7pLcreljlcX3FSPUxea7ticzNlCNeVrEaiEn/ws9m6rYUeuNw==} + '@cspell/dict-latex@5.1.0': + resolution: {integrity: sha512-qxT4guhysyBt0gzoliXYEBYinkAdEtR2M7goRaUH0a7ltCsoqqAeEV8aXYRIdZGcV77gYSobvu3jJL038tlPAw==} '@cspell/dict-lorem-ipsum@4.0.5': resolution: {integrity: sha512-9a4TJYRcPWPBKkQAJ/whCu4uCAEgv/O2xAaZEI0n4y1/l18Yyx8pBKoIX5QuVXjjmKEkK7hi5SxyIsH7pFEK9Q==} @@ -9304,8 +9612,8 @@ packages: '@cspell/dict-node@5.0.9': resolution: {integrity: sha512-hO+ga+uYZ/WA4OtiMEyKt5rDUlUyu3nXMf8KVEeqq2msYvAPdldKBGH7lGONg6R/rPhv53Rb+0Y1SLdoK1+7wQ==} - '@cspell/dict-npm@5.2.34': - resolution: {integrity: sha512-M2MtfmYeHIPBuC8esMU4JQXHKma7Xt7VyBWUk67B62KDu61sxebQ2HeizdqmN2sLEJsTkq3bZT5PGzHpZ0LEWQ==} + '@cspell/dict-npm@5.2.35': + resolution: {integrity: sha512-w0VIDUvzHSTt4S9pfvSatApxtCesLMFrDUYD0Wjtw91EBRkB2wVw/RV3q1Ni9Nzpx6pCFpcB7c1xBY8l22cyiQ==} '@cspell/dict-php@4.1.1': resolution: {integrity: sha512-EXelI+4AftmdIGtA8HL8kr4WlUE11OqCSVlnIgZekmTkEGSZdYnkFdiJ5IANSALtlQ1mghKjz+OFqVs6yowgWA==} @@ -9334,8 +9642,8 @@ packages: '@cspell/dict-shell@1.1.2': resolution: {integrity: sha512-WqOUvnwcHK1X61wAfwyXq04cn7KYyskg90j4lLg3sGGKMW9Sq13hs91pqrjC44Q+lQLgCobrTkMDw9Wyl9nRFA==} - '@cspell/dict-software-terms@5.1.20': - resolution: {integrity: sha512-TEk1xHvetTI4pv7Vzje1D322m6QEjaH2P6ucOOf6q7EJCppQIdC0lZSXkgHJAFU5HGSvEXSzvnVeW2RHW86ziQ==} + '@cspell/dict-software-terms@5.1.22': + resolution: {integrity: sha512-ELi8wqyDAreDpo17Pu45AuKvcrhqPCkGeL+DMuSVxEimBwEqPB+KeQ89DkVap6QDJHl3LCth0pjv8uOgS1dtdQ==} '@cspell/dict-sql@2.2.1': resolution: {integrity: sha512-qDHF8MpAYCf4pWU8NKbnVGzkoxMNrFqBHyG/dgrlic5EQiKANCLELYtGlX5auIMDLmTf1inA0eNtv74tyRJ/vg==} @@ -9358,24 +9666,24 @@ packages: '@cspell/dict-zig@1.0.0': resolution: {integrity: sha512-XibBIxBlVosU06+M6uHWkFeT0/pW5WajDRYdXG2CgHnq85b0TI/Ks0FuBJykmsgi2CAD3Qtx8UHFEtl/DSFnAQ==} - '@cspell/dynamic-import@9.6.4': - resolution: {integrity: sha512-1VnL9ahT3s17DLWl4MeO1pYg7zcVT3X9cKynI2/U86zNK5xMGS5icvjp7X65tsCAVNcWOtkqVFfrxi7kWxn67g==} + '@cspell/dynamic-import@9.7.0': + resolution: {integrity: sha512-Ws36IYvtS/8IN3x6K9dPLvTmaArodRJmzTn2Rkf2NaTnIYWhRuFzsP3SVVO59NN3fXswAEbmz5DSbVUe8bPZHg==} engines: {node: '>=20'} - '@cspell/filetypes@9.6.4': - resolution: {integrity: sha512-a1aZ/8vGnhTknxTukjzo3m8CISyHW2MWnbedywg5SDEl5RMJitmzX90QZiQdSvEcqzqmtoAgSEZNBT2LX2gIKg==} + '@cspell/filetypes@9.7.0': + resolution: {integrity: sha512-Ln9e/8wGOyTeL3DCCs6kwd18TSpTw3kxsANjTrzLDASrX4cNmAdvc9J5dcIuBHPaqOAnRQxuZbzUlpRh73Y24w==} engines: {node: '>=20'} - '@cspell/rpc@9.6.4': - resolution: {integrity: sha512-vGI1788Rx5Yml9N1/pD4zGd8Vrchi2Y01ADf9NiiOaNVVdf4PU1GCssLCsiIzhYQneErpQ8pJi/mS2F/QMZbRA==} + '@cspell/rpc@9.7.0': + resolution: {integrity: sha512-VnZ4ABgQeoS4RwofcePkDP7L6tf3Kh5D7LQKoyRM4R6XtfSsYefym6XKaRl3saGtthH5YyjgNJ0Tgdjen4wAAw==} engines: {node: '>=20.18'} - '@cspell/strong-weak-map@9.6.4': - resolution: {integrity: sha512-AQrUbA0JUOEQgwItnfUQ6Ydk0hWY/uV3VhLwZWyrnT9eiQynmTnRTHtOCkkSl9+M4P0N4Raa2eGFRLcPAFksaw==} + '@cspell/strong-weak-map@9.7.0': + resolution: {integrity: sha512-5xbvDASjklrmy88O6gmGXgYhpByCXqOj5wIgyvwZe2l83T1bE+iOfGI4pGzZJ/mN+qTn1DNKq8BPBPtDgb7Q2Q==} engines: {node: '>=20'} - '@cspell/url@9.6.4': - resolution: {integrity: sha512-h6VMlb7bDyGJfwLtipxxtHlT+ojzUXZz14AqZ/NEzY3LfOhfJTGpRcWLYFsgG/L0Ma4qjsYbPJt/Sj1C14j0VA==} + '@cspell/url@9.7.0': + resolution: {integrity: sha512-ZaaBr0pTvNxmyUbIn+nVPXPr383VqJzfUDMWicgTjJIeo2+T2hOq2kNpgpvTIrWtZrsZnSP8oXms1+sKTjcvkw==} engines: {node: '>=20'} '@csstools/css-parser-algorithms@3.0.5': @@ -9655,6 +9963,11 @@ packages: '@fern-api/logger@0.4.24-rc1': resolution: {integrity: sha512-yh0E2F3K3IPnJZcE4dv+u8I51iKgTgv/reinKo4K5YmYEG1iLtw5vBEYMOPkQmsYFPAKIh++OMB/6TrsahMWew==} + '@fern-api/replay@0.6.0': + resolution: {integrity: sha512-tPiku+TkjrCd7Z3xwnwoCLaJWqFJSRUNzoujjk2Gqxnp5LQnsoG4sN+DPahSri+W5BYHW1SCC4dVOdtUtzYZIg==} + engines: {node: '>=18'} + hasBin: true + '@fern-api/ui-core-utils@0.145.12-b50d999d1': resolution: {integrity: sha512-S1sE+Fs6kc/7F1Gzwsz7sZlAhMLIsaMeVDQ68038YXfzsLhrsg3DEVPvil5T3KxH/qV80Uso25q1D1W/LwXo6Q==} @@ -9683,9 +9996,15 @@ packages: '@fern-fern/generators-sdk@0.114.0-5745f9e74': resolution: {integrity: sha512-FG7SU7ul1Q6EELAPsixGvbq8VP4Xjl42cd1VmLBlYXds5EtYxG/SZCu2QLsGgPMrkCFJ6V6VQgzXFTU3RoYR9A==} + '@fern-fern/ir-sdk@39.0.0': + resolution: {integrity: sha512-cx8g0hX3gS/JveQPKanSWAyhM8SGhVOiQEcovqmcF/j9wvpjO8FXXNNI1+ZR1Wdmkcggx9UIqXDbS7jzefh6Rw==} + '@fern-fern/ir-sdk@53.9.0': resolution: {integrity: sha512-WAXXL+XVnDO4a3nfZRbRTr0+xz88jIYEZYTbJ7sYWLS7DLjmU780EFMMwO8ySmCdI0R2yRfeSY0qaGvuomXg6g==} + '@fern-fern/ir-sdk@58.3.0': + resolution: {integrity: sha512-HZXzhxk/NV70R9LMr9SD7QQClrY2YxO2xOzMvS42ODYZWxMWZ/r4MQ2/1e5HN3ROyotJZkpI9QMOmFmgNbTrTw==} + '@fern-fern/ir-sdk@61.7.0': resolution: {integrity: sha512-LHz8xJRISnuMVzLZHjwh1ziEofE0smL08Ddoe/YHnod8p7DPS/mpOcQKEfzqUn2mdLB6iqUBKB74NMchP/nM+Q==} @@ -10447,103 +10766,103 @@ packages: '@oxc-project/types@0.110.0': resolution: {integrity: sha512-6Ct21OIlrEnFEJk5LT4e63pk3btsI6/TusD/GStLi7wYlGJNOl1GI9qvXAnRAxQU9zqA2Oz+UwhfTOU2rPZVow==} - '@oxc-resolver/binding-android-arm-eabi@11.17.1': - resolution: {integrity: sha512-+VuZyMYYaap5uDAU1xDU3Kul0FekLqpBS8kI5JozlWfYQKnc/HsZg2gHPkQrj0SC9lt74WMNCfOzZZJlYXSdEQ==} + '@oxc-resolver/binding-android-arm-eabi@11.18.0': + resolution: {integrity: sha512-EhwJNzbfLwQQIeyak3n08EB3UHknMnjy1dFyL98r3xlorje2uzHOT2vkB5nB1zqtTtzT31uSot3oGZFfODbGUg==} cpu: [arm] os: [android] - '@oxc-resolver/binding-android-arm64@11.17.1': - resolution: {integrity: sha512-YlDDTjvOEKhom/cRSVsXsMVeXVIAM9PJ/x2mfe08rfuS0iIEfJd8PngKbEIhG72WPxleUa+vkEZj9ncmC14z3Q==} + '@oxc-resolver/binding-android-arm64@11.18.0': + resolution: {integrity: sha512-esOPsT9S9B6vEMMp1qR9Yz5UepQXljoWRJYoyp7GV/4SYQOSTpN0+V2fTruxbMmzqLK+fjCEU2x3SVhc96LQLQ==} cpu: [arm64] os: [android] - '@oxc-resolver/binding-darwin-arm64@11.17.1': - resolution: {integrity: sha512-HOYYLSY4JDk14YkXaz/ApgJYhgDP4KsG8EZpgpOxdszGW9HmIMMY/vXqVKYW74dSH+GQkIXYxBrEh3nv+XODVg==} + '@oxc-resolver/binding-darwin-arm64@11.18.0': + resolution: {integrity: sha512-iJknScn8fRLRhGR6VHG31bzOoyLihSDmsJHRjHwRUL0yF1MkLlvzmZ+liKl9MGl+WZkZHaOFT5T1jNlLSWTowQ==} cpu: [arm64] os: [darwin] - '@oxc-resolver/binding-darwin-x64@11.17.1': - resolution: {integrity: sha512-JHPJbsa5HvPq2/RIdtGlqfaG9zV2WmgvHrKTYmlW0L5esqtKCBuetFudXTBzkNcyD69kSZLzH92AzTr6vFHMFg==} + '@oxc-resolver/binding-darwin-x64@11.18.0': + resolution: {integrity: sha512-3rMweF2GQLzkaUoWgFKy1fRtk0dpj4JDqucoZLJN9IZG+TC+RZg7QMwG5WKMvmEjzdYmOTw1L1XqZDVXF2ksaQ==} cpu: [x64] os: [darwin] - '@oxc-resolver/binding-freebsd-x64@11.17.1': - resolution: {integrity: sha512-UD1FRC8j8xZstFXYsXwQkNmmg7vUbee006IqxokwDUUA+xEgKZDpLhBEiVKM08Urb+bn7Q0gn6M1pyNR0ng5mg==} + '@oxc-resolver/binding-freebsd-x64@11.18.0': + resolution: {integrity: sha512-TfXsFby4QvpGwmUP66+X+XXQsycddZe9ZUUu/vHhq2XGI1EkparCSzjpYW1Nz5fFncbI5oLymQLln/qR+qxyOw==} cpu: [x64] os: [freebsd] - '@oxc-resolver/binding-linux-arm-gnueabihf@11.17.1': - resolution: {integrity: sha512-wFWC1wyf2ROFWTxK5x0Enm++DSof3EBQ/ypyAesMDLiYxOOASDoMOZG1ylWUnlKaCt5W7eNOWOzABpdfFf/ssA==} + '@oxc-resolver/binding-linux-arm-gnueabihf@11.18.0': + resolution: {integrity: sha512-WolOILquy9DJsHcfFMHeA5EjTCI9A7JoERFJru4UI2zKZcnfNPo5GApzYwiloscEp/s+fALPmyRntswUns0qHg==} cpu: [arm] os: [linux] - '@oxc-resolver/binding-linux-arm-musleabihf@11.17.1': - resolution: {integrity: sha512-k/hUif0GEBk/csSqCfTPXb8AAVs1NNWCa/skBghvNbTtORcWfOVqJ3mM+2pE189+enRm4UnryLREu5ysI0kXEQ==} + '@oxc-resolver/binding-linux-arm-musleabihf@11.18.0': + resolution: {integrity: sha512-r+5nHJyPdiBqOGTYAFyuq5RtuAQbm4y69GYWNG/uup9Cqr7RG9Ak0YZgGEbkQsc+XBs00ougu/D1+w3UAYIWHA==} cpu: [arm] os: [linux] - '@oxc-resolver/binding-linux-arm64-gnu@11.17.1': - resolution: {integrity: sha512-Cwm6A071ww60QouJ9LoHAwBgEoZzHQ0Qaqk2E7WLfBdiQN9mLXIDhnrpn04hlRElRPhLiu/dtg+o5PPLvaINXQ==} + '@oxc-resolver/binding-linux-arm64-gnu@11.18.0': + resolution: {integrity: sha512-bUzg6QxljqMLLwsxYajAQEHW1LYRLdKOg/aykt14PSqUUOmfnOJjPdSLTiHIZCluVzPCQxv1LjoyRcoTAXfQaQ==} cpu: [arm64] os: [linux] - '@oxc-resolver/binding-linux-arm64-musl@11.17.1': - resolution: {integrity: sha512-+hwlE2v3m0r3sk93SchJL1uyaKcPjf+NGO/TD2DZUDo+chXx7FfaEj0nUMewigSt7oZ2sQN9Z4NJOtUa75HE5Q==} + '@oxc-resolver/binding-linux-arm64-musl@11.18.0': + resolution: {integrity: sha512-l43GVwls5+YR8WXOIez5x7Pp/MfhdkMOZOOjFUSWC/9qMnSLX1kd95j9oxDrkWdD321JdHTyd4eau5KQPxZM9w==} cpu: [arm64] os: [linux] - '@oxc-resolver/binding-linux-ppc64-gnu@11.17.1': - resolution: {integrity: sha512-bO+rsaE5Ox8cFyeL5Ct5tzot1TnQpFa/Wmu5k+hqBYSH2dNVDGoi0NizBN5QV8kOIC6O5MZr81UG4yW/2FyDTA==} + '@oxc-resolver/binding-linux-ppc64-gnu@11.18.0': + resolution: {integrity: sha512-ayj7TweYWi/azxWmRpUZGz41kKNvfkXam20UrFhaQDrSNGNqefQRODxhJn0iv6jt4qChh7TUxDIoavR6ftRsjw==} cpu: [ppc64] os: [linux] - '@oxc-resolver/binding-linux-riscv64-gnu@11.17.1': - resolution: {integrity: sha512-B/P+hxKQ1oX4YstI9Lyh4PGzqB87Ddqj/A4iyRBbPdXTcxa+WW3oRLx1CsJKLmHPdDk461Hmbghq1Bm3pl+8Aw==} + '@oxc-resolver/binding-linux-riscv64-gnu@11.18.0': + resolution: {integrity: sha512-2Jz7jpq6BBNlBBup3usZB6sZWEZOBbjWn++/bKC2lpAT+sTEwdTonnf3rNcb+XY7+v53jYB9pM8LEKVXZfr8BA==} cpu: [riscv64] os: [linux] - '@oxc-resolver/binding-linux-riscv64-musl@11.17.1': - resolution: {integrity: sha512-ulp2H3bFXzd/th2maH+QNKj5qgOhJ3v9Yspdf1svTw3CDOuuTl6sRKsWQ7MUw0vnkSNvQndtflBwVXgzZvURsQ==} + '@oxc-resolver/binding-linux-riscv64-musl@11.18.0': + resolution: {integrity: sha512-omw8/ISOc6ubR247iEMma4/JRfbY2I+nGJC59oKBhCIEZoyqEg/NmDSBc4ToMH+AsZDucqQUDOCku3k7pBiEag==} cpu: [riscv64] os: [linux] - '@oxc-resolver/binding-linux-s390x-gnu@11.17.1': - resolution: {integrity: sha512-LAXYVe3rKk09Zo9YKF2ZLBcH8sz8Oj+JIyiUxiHtq0hiYLMsN6dOpCf2hzQEjPAmsSEA/hdC1PVKeXo+oma8mQ==} + '@oxc-resolver/binding-linux-s390x-gnu@11.18.0': + resolution: {integrity: sha512-uFipBXaS+honSL5r5G/rlvVrkffUjpKwD3S/aIiwp64bylK3+RztgV+mM1blk+OT5gBRG864auhH6jCfrOo3ZA==} cpu: [s390x] os: [linux] - '@oxc-resolver/binding-linux-x64-gnu@11.17.1': - resolution: {integrity: sha512-3RAhxipMKE8RCSPn7O//sj440i+cYTgYbapLeOoDvQEt6R1QcJjTsFgI4iz99FhVj3YbPxlZmcLB5VW+ipyRTA==} + '@oxc-resolver/binding-linux-x64-gnu@11.18.0': + resolution: {integrity: sha512-bY4uMIoKRv8Ine3UiKLFPWRZ+fPCDamTHZFf5pNOjlfmTJIANtJo0mzWDUdFZLYhVgQdegrDL9etZbTMR8qieg==} cpu: [x64] os: [linux] - '@oxc-resolver/binding-linux-x64-musl@11.17.1': - resolution: {integrity: sha512-wpjMEubGU8r9VjZTLdZR3aPHaBqTl8Jl8F4DBbgNoZ+yhkhQD1/MGvY70v2TLnAI6kAHSvcqgfvaqKDa2iWsPQ==} + '@oxc-resolver/binding-linux-x64-musl@11.18.0': + resolution: {integrity: sha512-40IicL/aitfNOWur06x7Do41WcqFJ9VUNAciFjZCXzF6wR2i6uVsi6N19ecqgSRoLYFCAoRYi9F50QteIxCwKQ==} cpu: [x64] os: [linux] - '@oxc-resolver/binding-openharmony-arm64@11.17.1': - resolution: {integrity: sha512-XIE4w17RYAVIgx+9Gs3deTREq5tsmalbatYOOBGNdH7n0DfTE600c7wYXsp7ANc3BPDXsInnOzXDEPCvO1F6cg==} + '@oxc-resolver/binding-openharmony-arm64@11.18.0': + resolution: {integrity: sha512-DJIzYjUnSJtz4Trs/J9TnzivtPcUKn9AeL3YjHlM5+RvK27ZL9xISs3gg2VAo2nWU7ThuadC1jSYkWaZyONMwg==} cpu: [arm64] os: [openharmony] - '@oxc-resolver/binding-wasm32-wasi@11.17.1': - resolution: {integrity: sha512-Lqi5BlHX3zS4bpSOkIbOKVf7DIk6Gvmdifr2OuOI58eUUyP944M8/OyaB09cNpPy9Vukj7nmmhOzj8pwLgAkIg==} + '@oxc-resolver/binding-wasm32-wasi@11.18.0': + resolution: {integrity: sha512-57+R8Ioqc8g9k80WovoupOoyIOfLEceHTizkUcwOXspXLhiZ67ScM7Q8OuvhDoRRSZzH6yI0qML3WZwMFR3s7g==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@oxc-resolver/binding-win32-arm64-msvc@11.17.1': - resolution: {integrity: sha512-l6lTcLBQVj1HNquFpXSsrkCIM8X5Hlng5YNQJrg00z/KyovvDV5l3OFhoRyZ+aLBQ74zUnMRaJZC7xcBnHyeNg==} + '@oxc-resolver/binding-win32-arm64-msvc@11.18.0': + resolution: {integrity: sha512-t9Oa4BPptJqVlHTT1cV1frs+LY/vjsKhHI6ltj2EwoGM1TykJ0WW43UlQaU4SC8N+oTY8JRbAywVMNkfqjSu9w==} cpu: [arm64] os: [win32] - '@oxc-resolver/binding-win32-ia32-msvc@11.17.1': - resolution: {integrity: sha512-VTzVtfnCCsU/6GgvursWoyZrhe3Gj/RyXzDWmh4/U1Y3IW0u1FZbp+hCIlBL16pRPbDc5YvXVtCOnA41QOrOoQ==} + '@oxc-resolver/binding-win32-ia32-msvc@11.18.0': + resolution: {integrity: sha512-4maf/f6ea5IEtIXqGwSw38srRtVHTre9iKShG4gjzat7c3Iq6B1OppXMj8gNmTuM4n8Xh1hQM9z2hBELccJr1g==} cpu: [ia32] os: [win32] - '@oxc-resolver/binding-win32-x64-msvc@11.17.1': - resolution: {integrity: sha512-jRPVU+6/12baj87q2+UGRh30FBVBzqKdJ7rP/mSqiL1kpNQB9yZ1j0+m3sru1m+C8hiFK7lBFwjUtYUBI7+UpQ==} + '@oxc-resolver/binding-win32-x64-msvc@11.18.0': + resolution: {integrity: sha512-EhW8Su3AEACSw5HfzKMmyCtV0oArNrVViPdeOfvVYL9TrkL+/4c8fWHFTBtxUMUyCjhSG5xYNdwty1D/TAgL0Q==} cpu: [x64] os: [win32] @@ -10957,6 +11276,9 @@ packages: '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + '@types/minimatch@3.0.5': + resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} + '@types/minimatch@6.0.0': resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==} deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed. @@ -10973,6 +11295,9 @@ packages: '@types/node@18.15.3': resolution: {integrity: sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==} + '@types/node@22.19.11': + resolution: {integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==} + '@types/node@25.2.1': resolution: {integrity: sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg==} @@ -11166,6 +11491,21 @@ packages: '@vitest/utils@4.0.18': resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} + '@vue/compiler-core@3.5.27': + resolution: {integrity: sha512-gnSBQjZA+//qDZen+6a2EdHqJ68Z7uybrMf3SPjEGgG4dicklwDVmMC1AeIHxtLVPT7sn6sH1KOO+tS6gwOUeQ==} + + '@vue/compiler-dom@3.5.27': + resolution: {integrity: sha512-oAFea8dZgCtVVVTEC7fv3T5CbZW9BxpFzGGxC79xakTr6ooeEqmRuvQydIiDAkglZEAd09LgVf1RoDnL54fu5w==} + + '@vue/compiler-sfc@3.5.27': + resolution: {integrity: sha512-sHZu9QyDPeDmN/MRoshhggVOWE5WlGFStKFwu8G52swATgSny27hJRWteKDSUUzUH+wp+bmeNbhJnEAel/auUQ==} + + '@vue/compiler-ssr@3.5.27': + resolution: {integrity: sha512-Sj7h+JHt512fV1cTxKlYhg7qxBvack+BGncSpH+8vnN+KN95iPIcqB5rsbblX40XorP+ilO7VIKlkuu3Xq2vjw==} + + '@vue/shared@3.5.27': + resolution: {integrity: sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ==} + '@wasm-fmt/gofmt@0.4.9': resolution: {integrity: sha512-YiJKASbwWHD9Rt4MD1Xwb5DanB0MQLYXgxeNGocxE5nu7ZyzPgm1nTn0sFVCJoxYMsGn5ypyXakuHTQtiJu4Ag==} engines: {node: '>=16.17.0'} @@ -11260,6 +11600,10 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + array-differ@3.0.0: + resolution: {integrity: sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==} + engines: {node: '>=8'} + array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} @@ -11274,6 +11618,10 @@ packages: resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} engines: {node: '>=0.10.0'} + arrify@2.0.1: + resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} + engines: {node: '>=8'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -11337,9 +11685,9 @@ packages: balanced-match@2.0.0: resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} - balanced-match@4.0.3: - resolution: {integrity: sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==} - engines: {node: 20 || >=22} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} bare-events@2.8.2: resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==} @@ -11422,9 +11770,9 @@ packages: resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} engines: {node: '>=14.16'} - brace-expansion@5.0.2: - resolution: {integrity: sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==} - engines: {node: 20 || >=22} + brace-expansion@5.0.3: + resolution: {integrity: sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==} + engines: {node: 18 || 20 || >=22} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -11492,6 +11840,9 @@ packages: call-me-maybe@1.0.2: resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + callsite@1.0.0: + resolution: {integrity: sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -11504,6 +11855,10 @@ packages: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + camelcase@7.0.1: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} @@ -11530,6 +11885,10 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chalk@5.6.2: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} @@ -11598,6 +11957,9 @@ packages: resolution: {integrity: sha512-16KrBOV7bHmHdxcQiCvfUFYVFyEah4FI8vYT1Fr7CGSA4G+xBWMEfUEQJS1hxeHGtI9ju1Bzs9uXSbj5HZKArw==} engines: {node: '>=4'} + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -11720,44 +12082,44 @@ packages: resolution: {integrity: sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==} engines: {node: '>= 8'} - cspell-config-lib@9.6.4: - resolution: {integrity: sha512-MecJNR9bIlcPBhyZFsXP6Q2n8qQ2IR9N9HiIz0yh0gBNVydp3LR5JITP5Ji8m7hexmZzVeoXms/dVN74XbS95g==} + cspell-config-lib@9.7.0: + resolution: {integrity: sha512-pguh8A3+bSJ1OOrKCiQan8bvaaY125de76OEFz7q1Pq309lIcDrkoL/W4aYbso/NjrXaIw6OjkgPMGRBI/IgGg==} engines: {node: '>=20'} - cspell-dictionary@9.6.4: - resolution: {integrity: sha512-Ik9ZQVqV/fJfMt5X6IkC7yHGVH46/qjcqCNWwrMSwvROLM3SemNxxZoLvh0wi0GXz9WF1lHcxLJVdeKUk6QB8g==} + cspell-dictionary@9.7.0: + resolution: {integrity: sha512-k/Wz0so32+0QEqQe21V9m4BNXM5ZN6lz3Ix/jLCbMxFIPl6wT711ftjOWIEMFhvUOP0TWXsbzcuE9mKtS5mTig==} engines: {node: '>=20'} - cspell-gitignore@9.6.4: - resolution: {integrity: sha512-a8asE9BsjJgJ506WUGh5VHrTdVEE8hWELjCJB2atPrW6iY5e4aCIugy0gkRC1ZH9/TseadlmMLrFzHUkJUjzsg==} + cspell-gitignore@9.7.0: + resolution: {integrity: sha512-MtoYuH4ah4K6RrmaF834npMcRsTKw0658mC6yvmBacUQOmwB/olqyuxF3fxtbb55HDb7cXDQ35t1XuwwGEQeZw==} engines: {node: '>=20'} hasBin: true - cspell-glob@9.6.4: - resolution: {integrity: sha512-253VrjbR8QU15h8GtpDQLX5Ti9uNSuNod2T7f8YEElQOb9I/kUXoCj3Cq4P390IC99klqSHIDxHsxd77ex19lA==} + cspell-glob@9.7.0: + resolution: {integrity: sha512-LUeAoEsoCJ+7E3TnUmWBscpVQOmdwBejMlFn0JkXy6LQzxrybxXBKf65RSdIv1o5QtrhQIMa358xXYQG0sv/tA==} engines: {node: '>=20'} - cspell-grammar@9.6.4: - resolution: {integrity: sha512-rvZyTB45/XSRWx7eAsrvTTAZvBTREr/2G2JWVMdqrptFyq1XReAKHhw/x1HJkNgWC9LKAK3bVQJpjLsNG37U9A==} + cspell-grammar@9.7.0: + resolution: {integrity: sha512-oEYME+7MJztfVY1C06aGcJgEYyqBS/v/ETkQGPzf/c6ObSAPRcUbVtsXZgnR72Gru9aBckc70xJcD6bELdoWCA==} engines: {node: '>=20'} hasBin: true - cspell-io@9.6.4: - resolution: {integrity: sha512-bmvJ4yn5QK2FZWTkZA4sx2qJqIi8BrUUUV7W209drSwkYjhJtXqP0RyF6Qx4Xuu2D1s0UilEtO5Jd+E9UJkQ6w==} + cspell-io@9.7.0: + resolution: {integrity: sha512-V7x0JHAUCcJPRCH8c0MQkkaKmZD2yotxVyrNEx2SZTpvnKrYscLEnUUTWnGJIIf9znzISqw116PLnYu2c+zd6Q==} engines: {node: '>=20'} - cspell-lib@9.6.4: - resolution: {integrity: sha512-fUodKcIHTwvokuowB25XyFzBxlk73yj1QRw2por3BxDz9fAim1zAIohAPAnGuzj3LowYnTMjHLYE7RFDUSxy5A==} + cspell-lib@9.7.0: + resolution: {integrity: sha512-aTx/aLRpnuY1RJnYAu+A8PXfm1oIUdvAQ4W9E66bTgp1LWI+2G2++UtaPxRIgI0olxE9vcXqUnKpjOpO+5W9bQ==} engines: {node: '>=20'} - cspell-trie-lib@9.6.4: - resolution: {integrity: sha512-JKwyRtyybbaTrixwI1OgU5Hvva2Z5zHVWl92WBa9U7KijAyiD/Ehp3T3DCYuBwGks7egw7MgWPySkXXnpme6mw==} + cspell-trie-lib@9.7.0: + resolution: {integrity: sha512-a2YqmcraL3g6I/4gY7SYWEZfP73oLluUtxO7wxompk/kOG2K1FUXyQfZXaaR7HxVv10axT1+NrjhOmXpfbI6LA==} engines: {node: '>=20'} peerDependencies: - '@cspell/cspell-types': 9.6.4 + '@cspell/cspell-types': 9.7.0 - cspell@9.6.4: - resolution: {integrity: sha512-rZJmgcyBGKX3KcJ3KC9JYVHeKhDEVbmCheSp8eRGMYw6MCG9o7FHqQjGA/u4lEu4A0psr7ACP/5ym/QHyntRbA==} + cspell@9.7.0: + resolution: {integrity: sha512-ftxOnkd+scAI7RZ1/ksgBZRr0ouC7QRKtPQhD/PbLTKwAM62sSvRhE1bFsuW3VKBn/GilWzTjkJ40WmnDqH5iQ==} engines: {node: '>=20.18'} hasBin: true @@ -11880,6 +12242,11 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + depcheck@1.4.7: + resolution: {integrity: sha512-1lklS/bV5chOxwNKA/2XUUk/hPORp8zihZsXflr8x0kLwmcZ9Y9BsS6Hs3ssvA+2wUVbG0U2Ciqvm1SokNjPkA==} + engines: {node: '>=10'} + hasBin: true + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -11887,6 +12254,9 @@ packages: deprecation@2.3.1: resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} + deps-regex@0.2.0: + resolution: {integrity: sha512-PwuBojGMQAYbWkMXOY9Pd/NWCDNHVH12pnS7WHqZkTSeMESe4hwnKKRp0yR87g37113x4JPbo/oIvXY+s/f56Q==} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -11895,6 +12265,10 @@ packages: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + detect-file@1.0.0: + resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==} + engines: {node: '>=0.10.0'} + detect-indent@7.0.2: resolution: {integrity: sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==} engines: {node: '>=12.20'} @@ -12001,6 +12375,10 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -12142,6 +12520,9 @@ packages: estree-util-visit@2.0.0: resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -12180,6 +12561,10 @@ packages: resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} engines: {node: ^18.19.0 || >=20.5.0} + expand-tilde@2.0.2: + resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==} + engines: {node: '>=0.10.0'} + expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} @@ -12315,6 +12700,10 @@ packages: resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + findup-sync@5.0.0: + resolution: {integrity: sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==} + engines: {node: '>= 10.13.0'} + fix-dts-default-cjs-exports@1.0.1: resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} @@ -12470,14 +12859,22 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - global-directory@4.0.1: - resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} - engines: {node: '>=18'} + global-directory@5.0.0: + resolution: {integrity: sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==} + engines: {node: '>=20'} + + global-modules@1.0.0: + resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==} + engines: {node: '>=0.10.0'} global-modules@2.0.0: resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} engines: {node: '>=6'} + global-prefix@1.0.2: + resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==} + engines: {node: '>=0.10.0'} + global-prefix@3.0.0: resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} engines: {node: '>=6'} @@ -12604,6 +13001,10 @@ packages: resolution: {integrity: sha512-EQfezRg0NCZGNlhlDR3Evrw1FVL2G3LhU7EgPoxufQKruNBSYA8MiRPHeWbU+36o+Fhel0wMwM+sLEiBAlNLJA==} engines: {node: '>=10.0.0'} + homedir-polyfill@1.0.3: + resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} + engines: {node: '>=0.10.0'} + hookable@6.0.1: resolution: {integrity: sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw==} @@ -12722,9 +13123,9 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - ini@4.1.1: - resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ini@6.0.0: + resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} + engines: {node: ^20.17.0 || >=22.9.0} inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} @@ -12856,6 +13257,10 @@ packages: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} @@ -12999,8 +13404,8 @@ packages: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} - knip@5.84.1: - resolution: {integrity: sha512-F1+yACEsSapAwmQLzfD4i9uPsnI82P4p5ABpNQ9pcc4fpQtjHEX34XDtNl5863I4O6SCECpymylcWDHI3ouhQQ==} + knip@5.85.0: + resolution: {integrity: sha512-V2kyON+DZiYdNNdY6GALseiNCwX7dYdpz9Pv85AUn69Gk0UKCts+glOKWfe5KmaMByRjM9q17Mzj/KinTVOyxg==} engines: {node: '>=18.18.0'} hasBin: true peerDependencies: @@ -13366,9 +13771,9 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} - minimatch@10.2.1: - resolution: {integrity: sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A==} - engines: {node: 20 || >=22} + minimatch@10.2.2: + resolution: {integrity: sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==} + engines: {node: 18 || 20 || >=22} minimist-options@4.1.0: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} @@ -13404,6 +13809,10 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + multimatch@5.0.0: + resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} + engines: {node: '>=10'} + mute-stream@1.0.0: resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -13437,6 +13846,10 @@ packages: peerDependencies: react: '>=16' + node-diff3@3.2.0: + resolution: {integrity: sha512-vLh2xJFSyniBLYDEDbXKqD32fQ5vAxmYT4hco8t0EHQ4CQ4BDHhshi7kdvDc6Y1MwGSi1Mhl4unUukPbCayZdw==} + engines: {bun: '>=1.3.0', node: '>=18'} + node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -13559,8 +13972,8 @@ packages: resolution: {integrity: sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==} engines: {node: '>=16'} - oxc-resolver@11.17.1: - resolution: {integrity: sha512-pyRXK9kH81zKlirHufkFhOFBZRks8iAMLwPH8gU7lvKFiuzUH9L8MxDEllazwOb8fjXMcWjY1PMDfMJ2/yh5cw==} + oxc-resolver@11.18.0: + resolution: {integrity: sha512-Fv/b05AfhpYoCDvsog6tgsDm2yIwIeJafpMFLncNwKHRYu+Y1xQu5Q/rgUn7xBfuhNgjtPO7C0jCf7p2fLDj1g==} p-finally@1.0.0: resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} @@ -13631,6 +14044,10 @@ packages: resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} engines: {node: '>=18'} + parse-passwd@1.0.0: + resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} + engines: {node: '>=0.10.0'} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -13730,6 +14147,9 @@ packages: pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + please-upgrade-node@3.2.0: + resolution: {integrity: sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==} + pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -13934,6 +14354,10 @@ packages: resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -14023,6 +14447,13 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + require-package-name@2.0.1: + resolution: {integrity: sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==} + + resolve-dir@1.0.1: + resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==} + engines: {node: '>=0.10.0'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -14116,6 +14547,9 @@ packages: resolution: {integrity: sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==} hasBin: true + semver-compare@1.0.0: + resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} + semver-diff@4.0.0: resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} engines: {node: '>=12'} @@ -14133,6 +14567,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + send@0.19.2: resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} engines: {node: '>= 0.8.0'} @@ -14753,6 +15192,9 @@ packages: unconfig-core@7.4.2: resolution: {integrity: sha512-VgPCvLWugINbXvMQDf8Jh0mlbvNjNC6eSUziHsBCMpxR05OPrNrvDnyatdMjRgcHaaNsCqz+wjNXxNw1kRLHUg==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} @@ -15107,6 +15549,10 @@ packages: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -15148,12 +15594,20 @@ packages: snapshots: + '@babel/code-frame@7.28.6': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/compat-data@7.28.6': {} + '@babel/compat-data@7.29.0': {} '@babel/core@7.29.0': @@ -15176,6 +15630,14 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/generator@7.28.6': + dependencies: + '@babel/parser': 7.28.6 + '@babel/types': 7.28.6 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + '@babel/generator@7.29.1': dependencies: '@babel/parser': 7.29.0 @@ -15195,11 +15657,11 @@ snapshots: '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.28.6 '@babel/helper-compilation-targets@7.28.6': dependencies: - '@babel/compat-data': 7.29.0 + '@babel/compat-data': 7.28.6 '@babel/helper-validator-option': 7.27.1 browserslist: 4.28.1 lru-cache: 5.1.1 @@ -15238,23 +15700,23 @@ snapshots: '@babel/helper-environment-visitor@7.24.7': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.28.6 '@babel/helper-function-name@7.24.7': dependencies: '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/types': 7.28.6 '@babel/helper-globals@7.28.0': {} '@babel/helper-hoist-variables@7.24.7': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.28.6 '@babel/helper-member-expression-to-functions@7.28.5': dependencies: '@babel/traverse': 7.23.2 - '@babel/types': 7.29.0 + '@babel/types': 7.28.6 transitivePeerDependencies: - supports-color @@ -15276,7 +15738,7 @@ snapshots: '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.28.6 '@babel/helper-plugin-utils@7.28.6': {} @@ -15301,13 +15763,13 @@ snapshots: '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: '@babel/traverse': 7.23.2 - '@babel/types': 7.29.0 + '@babel/types': 7.28.6 transitivePeerDependencies: - supports-color '@babel/helper-split-export-declaration@7.24.7': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.28.6 '@babel/helper-string-parser@7.27.1': {} @@ -15323,7 +15785,7 @@ snapshots: dependencies: '@babel/template': 7.28.6 '@babel/traverse': 7.23.2 - '@babel/types': 7.29.0 + '@babel/types': 7.28.6 transitivePeerDependencies: - supports-color @@ -15332,6 +15794,10 @@ snapshots: '@babel/template': 7.28.6 '@babel/types': 7.29.0 + '@babel/parser@7.28.6': + dependencies: + '@babel/types': 7.28.6 + '@babel/parser@7.29.0': dependencies: '@babel/types': 7.29.0 @@ -15829,7 +16295,7 @@ snapshots: dependencies: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 - '@babel/types': 7.29.0 + '@babel/types': 7.28.6 esutils: 2.0.3 '@babel/preset-typescript@7.28.5(@babel/core@7.29.0)': @@ -15848,25 +16314,30 @@ snapshots: '@babel/template@7.28.6': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.28.6 + '@babel/parser': 7.28.6 + '@babel/types': 7.28.6 '@babel/traverse@7.23.2': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 + '@babel/code-frame': 7.28.6 + '@babel/generator': 7.28.6 '@babel/helper-environment-visitor': 7.24.7 '@babel/helper-function-name': 7.24.7 '@babel/helper-hoist-variables': 7.24.7 '@babel/helper-split-export-declaration': 7.24.7 - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 + '@babel/parser': 7.28.6 + '@babel/types': 7.28.6 debug: 4.4.3 globals: 11.12.0 transitivePeerDependencies: - supports-color + '@babel/types@7.28.6': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@7.29.0': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -15989,7 +16460,7 @@ snapshots: hashery: 1.4.0 keyv: 5.6.0 - '@cspell/cspell-bundled-dicts@9.6.4': + '@cspell/cspell-bundled-dicts@9.7.0': dependencies: '@cspell/dict-ada': 4.1.1 '@cspell/dict-al': 1.1.1 @@ -16004,7 +16475,7 @@ snapshots: '@cspell/dict-data-science': 2.0.13 '@cspell/dict-django': 4.1.6 '@cspell/dict-docker': 1.1.17 - '@cspell/dict-dotnet': 5.0.11 + '@cspell/dict-dotnet': 5.0.12 '@cspell/dict-elixir': 4.0.8 '@cspell/dict-en-common-misspellings': 2.1.12 '@cspell/dict-en-gb-mit': 3.1.18 @@ -16025,14 +16496,14 @@ snapshots: '@cspell/dict-julia': 1.1.1 '@cspell/dict-k8s': 1.0.12 '@cspell/dict-kotlin': 1.1.1 - '@cspell/dict-latex': 5.0.0 + '@cspell/dict-latex': 5.1.0 '@cspell/dict-lorem-ipsum': 4.0.5 '@cspell/dict-lua': 4.0.8 '@cspell/dict-makefile': 1.0.5 '@cspell/dict-markdown': 2.0.14(@cspell/dict-css@4.0.19)(@cspell/dict-html-symbol-entities@4.0.5)(@cspell/dict-html@4.0.14)(@cspell/dict-typescript@3.2.3) '@cspell/dict-monkeyc': 1.0.12 '@cspell/dict-node': 5.0.9 - '@cspell/dict-npm': 5.2.34 + '@cspell/dict-npm': 5.2.35 '@cspell/dict-php': 4.1.1 '@cspell/dict-powershell': 5.0.15 '@cspell/dict-public-licenses': 2.0.15 @@ -16042,7 +16513,7 @@ snapshots: '@cspell/dict-rust': 4.1.2 '@cspell/dict-scala': 5.0.9 '@cspell/dict-shell': 1.1.2 - '@cspell/dict-software-terms': 5.1.20 + '@cspell/dict-software-terms': 5.1.22 '@cspell/dict-sql': 2.2.1 '@cspell/dict-svelte': 1.0.7 '@cspell/dict-swift': 2.0.6 @@ -16051,25 +16522,25 @@ snapshots: '@cspell/dict-vue': 3.0.5 '@cspell/dict-zig': 1.0.0 - '@cspell/cspell-json-reporter@9.6.4': + '@cspell/cspell-json-reporter@9.7.0': dependencies: - '@cspell/cspell-types': 9.6.4 + '@cspell/cspell-types': 9.7.0 - '@cspell/cspell-performance-monitor@9.6.4': {} + '@cspell/cspell-performance-monitor@9.7.0': {} - '@cspell/cspell-pipe@9.6.4': {} + '@cspell/cspell-pipe@9.7.0': {} - '@cspell/cspell-resolver@9.6.4': + '@cspell/cspell-resolver@9.7.0': dependencies: - global-directory: 4.0.1 + global-directory: 5.0.0 - '@cspell/cspell-service-bus@9.6.4': {} + '@cspell/cspell-service-bus@9.7.0': {} - '@cspell/cspell-types@9.6.4': {} + '@cspell/cspell-types@9.7.0': {} - '@cspell/cspell-worker@9.6.4': + '@cspell/cspell-worker@9.7.0': dependencies: - cspell-lib: 9.6.4 + cspell-lib: 9.7.0 '@cspell/dict-ada@4.1.1': {} @@ -16099,7 +16570,7 @@ snapshots: '@cspell/dict-docker@1.1.17': {} - '@cspell/dict-dotnet@5.0.11': {} + '@cspell/dict-dotnet@5.0.12': {} '@cspell/dict-elixir@4.0.8': {} @@ -16141,7 +16612,7 @@ snapshots: '@cspell/dict-kotlin@1.1.1': {} - '@cspell/dict-latex@5.0.0': {} + '@cspell/dict-latex@5.1.0': {} '@cspell/dict-lorem-ipsum@4.0.5': {} @@ -16160,7 +16631,7 @@ snapshots: '@cspell/dict-node@5.0.9': {} - '@cspell/dict-npm@5.2.34': {} + '@cspell/dict-npm@5.2.35': {} '@cspell/dict-php@4.1.1': {} @@ -16182,7 +16653,7 @@ snapshots: '@cspell/dict-shell@1.1.2': {} - '@cspell/dict-software-terms@5.1.20': {} + '@cspell/dict-software-terms@5.1.22': {} '@cspell/dict-sql@2.2.1': {} @@ -16198,18 +16669,18 @@ snapshots: '@cspell/dict-zig@1.0.0': {} - '@cspell/dynamic-import@9.6.4': + '@cspell/dynamic-import@9.7.0': dependencies: - '@cspell/url': 9.6.4 + '@cspell/url': 9.7.0 import-meta-resolve: 4.2.0 - '@cspell/filetypes@9.6.4': {} + '@cspell/filetypes@9.7.0': {} - '@cspell/rpc@9.6.4': {} + '@cspell/rpc@9.7.0': {} - '@cspell/strong-weak-map@9.6.4': {} + '@cspell/strong-weak-map@9.7.0': {} - '@cspell/url@9.6.4': {} + '@cspell/url@9.7.0': {} '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': dependencies: @@ -16341,7 +16812,7 @@ snapshots: dependencies: '@eslint/object-schema': 2.1.7 debug: 4.4.3 - minimatch: 10.2.1 + minimatch: 10.2.2 transitivePeerDependencies: - supports-color @@ -16362,7 +16833,7 @@ snapshots: ignore: 5.3.2 import-fresh: 3.3.1 js-yaml: 4.1.1 - minimatch: 10.2.1 + minimatch: 10.2.2 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color @@ -16436,7 +16907,16 @@ snapshots: '@fern-api/logger@0.4.24-rc1': dependencies: '@fern-api/core-utils': 0.4.24-rc1 - chalk: 5.6.2 + chalk: 5.4.1 + + '@fern-api/replay@0.6.0': + dependencies: + minimatch: 10.2.2 + node-diff3: 3.2.0 + simple-git: 3.30.0 + yaml: 2.3.3 + transitivePeerDependencies: + - supports-color '@fern-api/ui-core-utils@0.145.12-b50d999d1': dependencies: @@ -16511,8 +16991,20 @@ snapshots: transitivePeerDependencies: - encoding + '@fern-fern/ir-sdk@39.0.0': {} + '@fern-fern/ir-sdk@53.9.0': {} + '@fern-fern/ir-sdk@58.3.0': + dependencies: + form-data: 4.0.5 + formdata-node: 6.0.3 + node-fetch: 2.7.0 + qs: 6.14.2 + readable-stream: 4.7.0 + transitivePeerDependencies: + - encoding + '@fern-fern/ir-sdk@61.7.0': dependencies: form-data: 4.0.5 @@ -16845,7 +17337,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.2.1 + '@types/node': 22.19.11 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -17310,66 +17802,66 @@ snapshots: '@oxc-project/types@0.110.0': {} - '@oxc-resolver/binding-android-arm-eabi@11.17.1': + '@oxc-resolver/binding-android-arm-eabi@11.18.0': optional: true - '@oxc-resolver/binding-android-arm64@11.17.1': + '@oxc-resolver/binding-android-arm64@11.18.0': optional: true - '@oxc-resolver/binding-darwin-arm64@11.17.1': + '@oxc-resolver/binding-darwin-arm64@11.18.0': optional: true - '@oxc-resolver/binding-darwin-x64@11.17.1': + '@oxc-resolver/binding-darwin-x64@11.18.0': optional: true - '@oxc-resolver/binding-freebsd-x64@11.17.1': + '@oxc-resolver/binding-freebsd-x64@11.18.0': optional: true - '@oxc-resolver/binding-linux-arm-gnueabihf@11.17.1': + '@oxc-resolver/binding-linux-arm-gnueabihf@11.18.0': optional: true - '@oxc-resolver/binding-linux-arm-musleabihf@11.17.1': + '@oxc-resolver/binding-linux-arm-musleabihf@11.18.0': optional: true - '@oxc-resolver/binding-linux-arm64-gnu@11.17.1': + '@oxc-resolver/binding-linux-arm64-gnu@11.18.0': optional: true - '@oxc-resolver/binding-linux-arm64-musl@11.17.1': + '@oxc-resolver/binding-linux-arm64-musl@11.18.0': optional: true - '@oxc-resolver/binding-linux-ppc64-gnu@11.17.1': + '@oxc-resolver/binding-linux-ppc64-gnu@11.18.0': optional: true - '@oxc-resolver/binding-linux-riscv64-gnu@11.17.1': + '@oxc-resolver/binding-linux-riscv64-gnu@11.18.0': optional: true - '@oxc-resolver/binding-linux-riscv64-musl@11.17.1': + '@oxc-resolver/binding-linux-riscv64-musl@11.18.0': optional: true - '@oxc-resolver/binding-linux-s390x-gnu@11.17.1': + '@oxc-resolver/binding-linux-s390x-gnu@11.18.0': optional: true - '@oxc-resolver/binding-linux-x64-gnu@11.17.1': + '@oxc-resolver/binding-linux-x64-gnu@11.18.0': optional: true - '@oxc-resolver/binding-linux-x64-musl@11.17.1': + '@oxc-resolver/binding-linux-x64-musl@11.18.0': optional: true - '@oxc-resolver/binding-openharmony-arm64@11.17.1': + '@oxc-resolver/binding-openharmony-arm64@11.18.0': optional: true - '@oxc-resolver/binding-wasm32-wasi@11.17.1': + '@oxc-resolver/binding-wasm32-wasi@11.18.0': dependencies: '@napi-rs/wasm-runtime': 1.1.1 optional: true - '@oxc-resolver/binding-win32-arm64-msvc@11.17.1': + '@oxc-resolver/binding-win32-arm64-msvc@11.18.0': optional: true - '@oxc-resolver/binding-win32-ia32-msvc@11.17.1': + '@oxc-resolver/binding-win32-ia32-msvc@11.18.0': optional: true - '@oxc-resolver/binding-win32-x64-msvc@11.17.1': + '@oxc-resolver/binding-win32-x64-msvc@11.18.0': optional: true '@pnpm/config.env-replace@1.1.0': {} @@ -17424,7 +17916,7 @@ snapshots: https-proxy-agent: 7.0.6 js-levenshtein: 1.1.6 js-yaml: 4.1.1 - minimatch: 10.2.1 + minimatch: 10.2.2 pluralize: 8.0.0 yaml-ast-parser: 0.0.43 transitivePeerDependencies: @@ -17566,7 +18058,7 @@ snapshots: '@ts-morph/common@0.28.1': dependencies: - minimatch: 10.2.1 + minimatch: 10.2.2 path-browserify: 1.0.1 tinyglobby: 0.2.15 @@ -17590,7 +18082,7 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 25.2.1 + '@types/node': 22.19.11 '@types/boxen@3.0.5': dependencies: @@ -17603,15 +18095,15 @@ snapshots: '@types/cli-progress@3.11.6': dependencies: - '@types/node': 25.2.1 + '@types/node': 18.15.3 '@types/connect@3.4.38': dependencies: - '@types/node': 25.2.1 + '@types/node': 22.19.11 '@types/cors@2.8.19': dependencies: - '@types/node': 25.2.1 + '@types/node': 18.15.3 '@types/debug@4.1.12': dependencies: @@ -17619,7 +18111,7 @@ snapshots: '@types/decompress@4.2.7': dependencies: - '@types/node': 25.2.1 + '@types/node': 18.15.3 '@types/deep-eql@4.0.2': {} @@ -17635,7 +18127,7 @@ snapshots: '@types/express-serve-static-core@4.19.8': dependencies: - '@types/node': 25.2.1 + '@types/node': 22.19.11 '@types/qs': 6.9.15 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -17656,7 +18148,7 @@ snapshots: '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 - '@types/node': 25.2.1 + '@types/node': 18.15.3 '@types/get-port@4.2.0': dependencies: @@ -17702,12 +18194,12 @@ snapshots: '@types/jsonfile@6.1.4': dependencies: - '@types/node': 25.2.1 + '@types/node': 22.19.11 '@types/jsonwebtoken@9.0.10': dependencies: '@types/ms': 2.1.0 - '@types/node': 25.2.1 + '@types/node': 18.15.3 '@types/katex@0.16.8': {} @@ -17731,9 +18223,11 @@ snapshots: '@types/mime@1.3.5': {} + '@types/minimatch@3.0.5': {} + '@types/minimatch@6.0.0': dependencies: - minimatch: 10.2.1 + minimatch: 10.2.2 '@types/minimist@1.2.5': {} @@ -17741,11 +18235,15 @@ snapshots: '@types/node-fetch@2.6.9': dependencies: - '@types/node': 25.2.1 + '@types/node': 18.15.3 form-data: 4.0.5 '@types/node@18.15.3': {} + '@types/node@22.19.11': + dependencies: + undici-types: 6.21.0 + '@types/node@25.2.1': dependencies: undici-types: 7.16.0 @@ -17760,7 +18258,7 @@ snapshots: '@types/pngjs@6.0.5': dependencies: - '@types/node': 25.2.1 + '@types/node': 18.15.3 '@types/prettier@2.7.3': {} @@ -17778,44 +18276,44 @@ snapshots: '@types/readable-stream@4.0.23': dependencies: - '@types/node': 25.2.1 + '@types/node': 22.19.11 '@types/semver@7.7.1': {} '@types/send@0.17.6': dependencies: '@types/mime': 1.3.5 - '@types/node': 25.2.1 + '@types/node': 22.19.11 '@types/send@1.2.1': dependencies: - '@types/node': 25.2.1 + '@types/node': 22.19.11 '@types/serve-static@1.15.10': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 25.2.1 + '@types/node': 22.19.11 '@types/send': 0.17.6 '@types/stack-utils@2.0.3': {} '@types/stream-chain@2.1.0': dependencies: - '@types/node': 25.2.1 + '@types/node': 22.19.11 '@types/stream-json@1.7.8': dependencies: - '@types/node': 25.2.1 + '@types/node': 18.15.3 '@types/stream-chain': 2.1.0 '@types/swagger2openapi@7.0.4': dependencies: - '@types/node': 25.2.1 + '@types/node': 18.15.3 openapi-types: 12.1.3 '@types/tar@6.1.13': dependencies: - '@types/node': 25.2.1 + '@types/node': 18.15.3 minipass: 4.2.8 '@types/terminal-link@1.2.0': @@ -17824,7 +18322,7 @@ snapshots: '@types/through@0.0.33': dependencies: - '@types/node': 25.2.1 + '@types/node': 22.19.11 '@types/tinycolor2@1.4.6': {} @@ -17848,11 +18346,11 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.2.1 + '@types/node': 18.15.3 '@types/xml2js@0.4.14': dependencies: - '@types/node': 25.2.1 + '@types/node': 18.15.3 '@types/yargs-parser@21.0.3': {} @@ -17862,11 +18360,11 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 25.2.1 + '@types/node': 18.15.3 '@types/yazl@3.3.0': dependencies: - '@types/node': 25.2.1 + '@types/node': 18.15.3 '@typescript-eslint/project-service@8.54.0(typescript@5.9.3)': dependencies: @@ -17895,7 +18393,7 @@ snapshots: '@typescript-eslint/types': 8.54.0 '@typescript-eslint/visitor-keys': 8.54.0 debug: 4.4.3 - minimatch: 10.2.1 + minimatch: 10.2.2 semver: 7.7.3 tinyglobby: 0.2.15 ts-api-utils: 2.4.0(typescript@5.9.3) @@ -17945,6 +18443,14 @@ snapshots: optionalDependencies: vite: 7.3.1(@types/node@18.15.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3) + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3))': + dependencies: + '@vitest/spy': 4.0.18 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.1(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3) + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3))': dependencies: '@vitest/spy': 4.0.18 @@ -17975,6 +18481,38 @@ snapshots: '@vitest/pretty-format': 4.0.18 tinyrainbow: 3.0.3 + '@vue/compiler-core@3.5.27': + dependencies: + '@babel/parser': 7.28.6 + '@vue/shared': 3.5.27 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.27': + dependencies: + '@vue/compiler-core': 3.5.27 + '@vue/shared': 3.5.27 + + '@vue/compiler-sfc@3.5.27': + dependencies: + '@babel/parser': 7.28.6 + '@vue/compiler-core': 3.5.27 + '@vue/compiler-dom': 3.5.27 + '@vue/compiler-ssr': 3.5.27 + '@vue/shared': 3.5.27 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.6 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.27': + dependencies: + '@vue/compiler-dom': 3.5.27 + '@vue/shared': 3.5.27 + + '@vue/shared@3.5.27': {} + '@wasm-fmt/gofmt@0.4.9': {} '@wasm-fmt/ruff_fmt@0.6.1': {} @@ -18049,6 +18587,8 @@ snapshots: argparse@2.0.1: {} + array-differ@3.0.0: {} + array-flatten@1.1.1: {} array-timsort@1.0.3: {} @@ -18057,6 +18597,8 @@ snapshots: arrify@1.0.1: {} + arrify@2.0.1: {} + assertion-error@2.0.1: {} ast-kit@3.0.0-beta.1: @@ -18124,7 +18666,7 @@ snapshots: balanced-match@2.0.0: {} - balanced-match@4.0.3: {} + balanced-match@4.0.4: {} bare-events@2.8.2: {} @@ -18217,16 +18759,16 @@ snapshots: dependencies: ansi-align: 3.0.1 camelcase: 7.0.1 - chalk: 5.6.2 + chalk: 5.4.1 cli-boxes: 3.0.0 string-width: 5.1.2 type-fest: 2.19.0 widest-line: 4.0.1 wrap-ansi: 8.1.0 - brace-expansion@5.0.2: + brace-expansion@5.0.3: dependencies: - balanced-match: 4.0.3 + balanced-match: 4.0.4 braces@3.0.3: dependencies: @@ -18301,6 +18843,8 @@ snapshots: call-me-maybe@1.0.2: {} + callsite@1.0.0: {} + callsites@3.1.0: {} camelcase-keys@6.2.2: @@ -18311,6 +18855,8 @@ snapshots: camelcase@5.3.1: {} + camelcase@6.3.0: {} + camelcase@7.0.1: {} caniuse-lite@1.0.30001766: {} @@ -18334,6 +18880,8 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chalk@5.4.1: {} + chalk@5.6.2: {} character-entities-html4@2.1.0: {} @@ -18388,6 +18936,12 @@ snapshots: arch: 2.2.0 execa: 0.8.0 + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -18500,61 +19054,61 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - cspell-config-lib@9.6.4: + cspell-config-lib@9.7.0: dependencies: - '@cspell/cspell-types': 9.6.4 + '@cspell/cspell-types': 9.7.0 comment-json: 4.5.1 smol-toml: 1.6.0 yaml: 2.3.3 - cspell-dictionary@9.6.4: + cspell-dictionary@9.7.0: dependencies: - '@cspell/cspell-performance-monitor': 9.6.4 - '@cspell/cspell-pipe': 9.6.4 - '@cspell/cspell-types': 9.6.4 - cspell-trie-lib: 9.6.4(@cspell/cspell-types@9.6.4) + '@cspell/cspell-performance-monitor': 9.7.0 + '@cspell/cspell-pipe': 9.7.0 + '@cspell/cspell-types': 9.7.0 + cspell-trie-lib: 9.7.0(@cspell/cspell-types@9.7.0) fast-equals: 6.0.0 - cspell-gitignore@9.6.4: + cspell-gitignore@9.7.0: dependencies: - '@cspell/url': 9.6.4 - cspell-glob: 9.6.4 - cspell-io: 9.6.4 + '@cspell/url': 9.7.0 + cspell-glob: 9.7.0 + cspell-io: 9.7.0 - cspell-glob@9.6.4: + cspell-glob@9.7.0: dependencies: - '@cspell/url': 9.6.4 + '@cspell/url': 9.7.0 picomatch: 4.0.3 - cspell-grammar@9.6.4: + cspell-grammar@9.7.0: dependencies: - '@cspell/cspell-pipe': 9.6.4 - '@cspell/cspell-types': 9.6.4 + '@cspell/cspell-pipe': 9.7.0 + '@cspell/cspell-types': 9.7.0 - cspell-io@9.6.4: + cspell-io@9.7.0: dependencies: - '@cspell/cspell-service-bus': 9.6.4 - '@cspell/url': 9.6.4 + '@cspell/cspell-service-bus': 9.7.0 + '@cspell/url': 9.7.0 - cspell-lib@9.6.4: + cspell-lib@9.7.0: dependencies: - '@cspell/cspell-bundled-dicts': 9.6.4 - '@cspell/cspell-performance-monitor': 9.6.4 - '@cspell/cspell-pipe': 9.6.4 - '@cspell/cspell-resolver': 9.6.4 - '@cspell/cspell-types': 9.6.4 - '@cspell/dynamic-import': 9.6.4 - '@cspell/filetypes': 9.6.4 - '@cspell/rpc': 9.6.4 - '@cspell/strong-weak-map': 9.6.4 - '@cspell/url': 9.6.4 + '@cspell/cspell-bundled-dicts': 9.7.0 + '@cspell/cspell-performance-monitor': 9.7.0 + '@cspell/cspell-pipe': 9.7.0 + '@cspell/cspell-resolver': 9.7.0 + '@cspell/cspell-types': 9.7.0 + '@cspell/dynamic-import': 9.7.0 + '@cspell/filetypes': 9.7.0 + '@cspell/rpc': 9.7.0 + '@cspell/strong-weak-map': 9.7.0 + '@cspell/url': 9.7.0 clear-module: 4.1.2 - cspell-config-lib: 9.6.4 - cspell-dictionary: 9.6.4 - cspell-glob: 9.6.4 - cspell-grammar: 9.6.4 - cspell-io: 9.6.4 - cspell-trie-lib: 9.6.4(@cspell/cspell-types@9.6.4) + cspell-config-lib: 9.7.0 + cspell-dictionary: 9.7.0 + cspell-glob: 9.7.0 + cspell-grammar: 9.7.0 + cspell-io: 9.7.0 + cspell-trie-lib: 9.7.0(@cspell/cspell-types@9.7.0) env-paths: 4.0.0 gensequence: 8.0.8 import-fresh: 3.3.1 @@ -18563,32 +19117,32 @@ snapshots: vscode-uri: 3.1.0 xdg-basedir: 5.1.0 - cspell-trie-lib@9.6.4(@cspell/cspell-types@9.6.4): + cspell-trie-lib@9.7.0(@cspell/cspell-types@9.7.0): dependencies: - '@cspell/cspell-types': 9.6.4 + '@cspell/cspell-types': 9.7.0 - cspell@9.6.4: + cspell@9.7.0: dependencies: - '@cspell/cspell-json-reporter': 9.6.4 - '@cspell/cspell-performance-monitor': 9.6.4 - '@cspell/cspell-pipe': 9.6.4 - '@cspell/cspell-types': 9.6.4 - '@cspell/cspell-worker': 9.6.4 - '@cspell/dynamic-import': 9.6.4 - '@cspell/url': 9.6.4 + '@cspell/cspell-json-reporter': 9.7.0 + '@cspell/cspell-performance-monitor': 9.7.0 + '@cspell/cspell-pipe': 9.7.0 + '@cspell/cspell-types': 9.7.0 + '@cspell/cspell-worker': 9.7.0 + '@cspell/dynamic-import': 9.7.0 + '@cspell/url': 9.7.0 ansi-regex: 6.2.2 chalk: 5.6.2 chalk-template: 1.1.2 commander: 14.0.3 - cspell-config-lib: 9.6.4 - cspell-dictionary: 9.6.4 - cspell-gitignore: 9.6.4 - cspell-glob: 9.6.4 - cspell-io: 9.6.4 - cspell-lib: 9.6.4 + cspell-config-lib: 9.7.0 + cspell-dictionary: 9.7.0 + cspell-gitignore: 9.7.0 + cspell-glob: 9.7.0 + cspell-io: 9.7.0 + cspell-lib: 9.7.0 fast-json-stable-stringify: 2.1.0 flatted: 3.3.3 - semver: 7.7.3 + semver: 7.7.4 tinyglobby: 0.2.15 css-functions-list@3.2.3: {} @@ -18701,14 +19255,46 @@ snapshots: delayed-stream@1.0.0: {} + depcheck@1.4.7: + dependencies: + '@babel/parser': 7.28.6 + '@babel/traverse': 7.23.2 + '@vue/compiler-sfc': 3.5.27 + callsite: 1.0.0 + camelcase: 6.3.0 + cosmiconfig: 7.1.0 + debug: 4.4.3 + deps-regex: 0.2.0 + findup-sync: 5.0.0 + ignore: 5.3.2 + is-core-module: 2.16.1 + js-yaml: 3.14.2 + json5: 2.2.2 + lodash: 4.17.23 + minimatch: 10.2.2 + multimatch: 5.0.0 + please-upgrade-node: 3.2.0 + readdirp: 3.6.0 + require-package-name: 2.0.1 + resolve: 1.22.11 + resolve-from: 5.0.0 + semver: 7.7.3 + yargs: 16.2.0 + transitivePeerDependencies: + - supports-color + depd@2.0.0: {} deprecation@2.3.1: {} + deps-regex@0.2.0: {} + dequal@2.0.3: {} destroy@1.2.0: {} + detect-file@1.0.0: {} + detect-indent@7.0.2: {} detect-libc@1.0.3: {} @@ -18753,9 +19339,9 @@ snapshots: dependencies: detect-libc: 1.0.3 - dts-resolver@2.1.3(oxc-resolver@11.17.1): + dts-resolver@2.1.3(oxc-resolver@11.18.0): optionalDependencies: - oxc-resolver: 11.17.1 + oxc-resolver: 11.18.0 dunder-proto@1.0.1: dependencies: @@ -18797,6 +19383,8 @@ snapshots: entities@6.0.1: {} + entities@7.0.1: {} + env-paths@2.2.1: {} env-paths@4.0.0: @@ -18939,7 +19527,7 @@ snapshots: is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 lodash.merge: 4.6.2 - minimatch: 10.2.1 + minimatch: 10.2.2 natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: @@ -18994,6 +19582,8 @@ snapshots: '@types/estree-jsx': 1.0.5 '@types/unist': 3.0.3 + estree-walker@2.0.2: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 @@ -19051,6 +19641,10 @@ snapshots: strip-final-newline: 4.0.0 yoctocolors: 2.1.2 + expand-tilde@2.0.2: + dependencies: + homedir-polyfill: 1.0.3 + expect-type@1.3.0: {} expect@29.7.0: @@ -19223,6 +19817,13 @@ snapshots: locate-path: 7.2.0 path-exists: 5.0.0 + findup-sync@5.0.0: + dependencies: + detect-file: 1.0.0 + is-glob: 4.0.3 + micromatch: 4.0.8 + resolve-dir: 1.0.1 + fix-dts-default-cjs-exports@1.0.1: dependencies: magic-string: 0.30.21 @@ -19371,7 +19972,7 @@ snapshots: dependencies: foreground-child: 3.3.1 jackspeak: 4.1.1 - minimatch: 10.2.1 + minimatch: 10.2.2 minipass: 7.1.2 package-json-from-dist: 1.0.1 path-scurry: 2.0.1 @@ -19381,18 +19982,32 @@ snapshots: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 - minimatch: 10.2.1 + minimatch: 10.2.2 once: 1.4.0 path-is-absolute: 1.0.1 - global-directory@4.0.1: + global-directory@5.0.0: dependencies: - ini: 4.1.1 + ini: 6.0.0 + + global-modules@1.0.0: + dependencies: + global-prefix: 1.0.2 + is-windows: 1.0.2 + resolve-dir: 1.0.1 global-modules@2.0.0: dependencies: global-prefix: 3.0.0 + global-prefix@1.0.2: + dependencies: + expand-tilde: 2.0.2 + homedir-polyfill: 1.0.3 + ini: 1.3.8 + is-windows: 1.0.2 + which: 1.3.1 + global-prefix@3.0.0: dependencies: ini: 1.3.8 @@ -19616,6 +20231,10 @@ snapshots: heap-js@2.7.1: {} + homedir-polyfill@1.0.3: + dependencies: + parse-passwd: 1.0.0 + hookable@6.0.1: {} hookified@1.15.0: {} @@ -19719,7 +20338,7 @@ snapshots: ini@1.3.8: {} - ini@4.1.1: {} + ini@6.0.0: {} inline-style-parser@0.2.7: {} @@ -19815,6 +20434,8 @@ snapshots: is-unicode-supported@2.1.0: {} + is-windows@1.0.2: {} + is-wsl@2.2.0: dependencies: is-docker: 2.2.1 @@ -19860,7 +20481,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 25.2.1 + '@types/node': 22.19.11 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -19971,7 +20592,7 @@ snapshots: kind-of@6.0.3: {} - knip@5.84.1(@types/node@25.2.1)(typescript@5.9.3): + knip@5.85.0(@types/node@25.2.1)(typescript@5.9.3): dependencies: '@nodelib/fs.walk': 1.2.8 '@types/node': 25.2.1 @@ -19980,7 +20601,7 @@ snapshots: jiti: 2.6.1 js-yaml: 4.1.1 minimist: 1.2.8 - oxc-resolver: 11.17.1 + oxc-resolver: 11.18.0 picocolors: 1.1.1 picomatch: 4.0.3 smol-toml: 1.6.0 @@ -20056,7 +20677,7 @@ snapshots: log-symbols@5.1.0: dependencies: - chalk: 5.6.2 + chalk: 5.4.1 is-unicode-supported: 1.3.0 longest-streak@3.1.0: {} @@ -20597,9 +21218,9 @@ snapshots: min-indent@1.0.1: {} - minimatch@10.2.1: + minimatch@10.2.2: dependencies: - brace-expansion: 5.0.2 + brace-expansion: 5.0.3 minimist-options@4.1.0: dependencies: @@ -20632,6 +21253,14 @@ snapshots: ms@2.1.3: {} + multimatch@5.0.0: + dependencies: + '@types/minimatch': 3.0.5 + array-differ: 3.0.0 + array-union: 2.1.0 + arrify: 2.0.1 + minimatch: 10.2.2 + mute-stream@1.0.0: {} mute-stream@2.0.0: {} @@ -20652,7 +21281,7 @@ snapshots: next-mdx-remote@5.0.0(@types/react@19.2.13)(react@19.2.4): dependencies: - '@babel/code-frame': 7.29.0 + '@babel/code-frame': 7.28.6 '@mdx-js/mdx': 3.1.1 '@mdx-js/react': 3.1.1(@types/react@19.2.13)(react@19.2.4) react: 19.2.4 @@ -20663,6 +21292,8 @@ snapshots: - '@types/react' - supports-color + node-diff3@3.2.0: {} + node-domexception@1.0.0: {} node-fetch-h2@2.3.0: @@ -20822,7 +21453,7 @@ snapshots: ora@7.0.1: dependencies: - chalk: 5.6.2 + chalk: 5.4.1 cli-cursor: 4.0.0 cli-spinners: 2.9.2 is-interactive: 2.0.0 @@ -20832,28 +21463,28 @@ snapshots: string-width: 6.1.0 strip-ansi: 7.1.2 - oxc-resolver@11.17.1: + oxc-resolver@11.18.0: optionalDependencies: - '@oxc-resolver/binding-android-arm-eabi': 11.17.1 - '@oxc-resolver/binding-android-arm64': 11.17.1 - '@oxc-resolver/binding-darwin-arm64': 11.17.1 - '@oxc-resolver/binding-darwin-x64': 11.17.1 - '@oxc-resolver/binding-freebsd-x64': 11.17.1 - '@oxc-resolver/binding-linux-arm-gnueabihf': 11.17.1 - '@oxc-resolver/binding-linux-arm-musleabihf': 11.17.1 - '@oxc-resolver/binding-linux-arm64-gnu': 11.17.1 - '@oxc-resolver/binding-linux-arm64-musl': 11.17.1 - '@oxc-resolver/binding-linux-ppc64-gnu': 11.17.1 - '@oxc-resolver/binding-linux-riscv64-gnu': 11.17.1 - '@oxc-resolver/binding-linux-riscv64-musl': 11.17.1 - '@oxc-resolver/binding-linux-s390x-gnu': 11.17.1 - '@oxc-resolver/binding-linux-x64-gnu': 11.17.1 - '@oxc-resolver/binding-linux-x64-musl': 11.17.1 - '@oxc-resolver/binding-openharmony-arm64': 11.17.1 - '@oxc-resolver/binding-wasm32-wasi': 11.17.1 - '@oxc-resolver/binding-win32-arm64-msvc': 11.17.1 - '@oxc-resolver/binding-win32-ia32-msvc': 11.17.1 - '@oxc-resolver/binding-win32-x64-msvc': 11.17.1 + '@oxc-resolver/binding-android-arm-eabi': 11.18.0 + '@oxc-resolver/binding-android-arm64': 11.18.0 + '@oxc-resolver/binding-darwin-arm64': 11.18.0 + '@oxc-resolver/binding-darwin-x64': 11.18.0 + '@oxc-resolver/binding-freebsd-x64': 11.18.0 + '@oxc-resolver/binding-linux-arm-gnueabihf': 11.18.0 + '@oxc-resolver/binding-linux-arm-musleabihf': 11.18.0 + '@oxc-resolver/binding-linux-arm64-gnu': 11.18.0 + '@oxc-resolver/binding-linux-arm64-musl': 11.18.0 + '@oxc-resolver/binding-linux-ppc64-gnu': 11.18.0 + '@oxc-resolver/binding-linux-riscv64-gnu': 11.18.0 + '@oxc-resolver/binding-linux-riscv64-musl': 11.18.0 + '@oxc-resolver/binding-linux-s390x-gnu': 11.18.0 + '@oxc-resolver/binding-linux-x64-gnu': 11.18.0 + '@oxc-resolver/binding-linux-x64-musl': 11.18.0 + '@oxc-resolver/binding-openharmony-arm64': 11.18.0 + '@oxc-resolver/binding-wasm32-wasi': 11.18.0 + '@oxc-resolver/binding-win32-arm64-msvc': 11.18.0 + '@oxc-resolver/binding-win32-ia32-msvc': 11.18.0 + '@oxc-resolver/binding-win32-x64-msvc': 11.18.0 p-finally@1.0.0: {} @@ -20932,13 +21563,15 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.29.0 + '@babel/code-frame': 7.28.6 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 parse-ms@4.0.0: {} + parse-passwd@1.0.0: {} + parse5@7.3.0: dependencies: entities: 6.0.1 @@ -21006,6 +21639,10 @@ snapshots: mlly: 1.8.0 pathe: 2.0.3 + please-upgrade-node@3.2.0: + dependencies: + semver-compare: 1.0.0 + pluralize@8.0.0: {} pngjs@7.0.0: {} @@ -21227,6 +21864,10 @@ snapshots: process: 0.11.10 string_decoder: 1.3.0 + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + readdirp@4.1.2: {} recma-build-jsx@1.0.0: @@ -21377,6 +22018,13 @@ snapshots: require-from-string@2.0.2: {} + require-package-name@2.0.1: {} + + resolve-dir@1.0.1: + dependencies: + expand-tilde: 2.0.2 + global-modules: 1.0.0 + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -21405,14 +22053,14 @@ snapshots: dependencies: glob: 7.2.3 - rolldown-plugin-dts@0.21.8(oxc-resolver@11.17.1)(rolldown@1.0.0-rc.1)(typescript@5.9.3): + rolldown-plugin-dts@0.21.8(oxc-resolver@11.18.0)(rolldown@1.0.0-rc.1)(typescript@5.9.3): dependencies: '@babel/generator': 8.0.0-beta.4 '@babel/parser': 8.0.0-beta.4 '@babel/types': 8.0.0-beta.4 ast-kit: 3.0.0-beta.1 birpc: 4.0.0 - dts-resolver: 2.1.3(oxc-resolver@11.17.1) + dts-resolver: 2.1.3(oxc-resolver@11.18.0) get-tsconfig: 4.13.1 obug: 2.1.1 rolldown: 1.0.0-rc.1 @@ -21498,6 +22146,8 @@ snapshots: dependencies: commander: 2.20.3 + semver-compare@1.0.0: {} + semver-diff@4.0.0: dependencies: semver: 7.7.3 @@ -21508,6 +22158,8 @@ snapshots: semver@7.7.3: {} + semver@7.7.4: {} + send@0.19.2: dependencies: debug: 2.6.9 @@ -22131,7 +22783,7 @@ snapshots: dependencies: dprint-node: 1.0.8 - tsdown@0.20.1(oxc-resolver@11.17.1)(typescript@5.9.3): + tsdown@0.20.1(oxc-resolver@11.18.0)(typescript@5.9.3): dependencies: ansis: 4.2.0 cac: 6.7.14 @@ -22142,7 +22794,7 @@ snapshots: obug: 2.1.1 picomatch: 4.0.3 rolldown: 1.0.0-rc.1 - rolldown-plugin-dts: 0.21.8(oxc-resolver@11.17.1)(rolldown@1.0.0-rc.1)(typescript@5.9.3) + rolldown-plugin-dts: 0.21.8(oxc-resolver@11.18.0)(rolldown@1.0.0-rc.1)(typescript@5.9.3) semver: 7.7.3 tinyexec: 1.0.2 tinyglobby: 0.2.15 @@ -22273,6 +22925,8 @@ snapshots: '@quansync/fs': 1.0.0 quansync: 1.0.0 + undici-types@6.21.0: {} + undici-types@7.16.0: {} undici@6.23.0: {} @@ -22426,6 +23080,21 @@ snapshots: tsx: 4.21.0 yaml: 2.3.3 + vite@7.3.1(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.57.1 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.19.11 + fsevents: 2.3.3 + jiti: 2.6.1 + tsx: 4.21.0 + yaml: 2.3.3 + vite@7.3.1(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3): dependencies: esbuild: 0.27.3 @@ -22478,6 +23147,43 @@ snapshots: - tsx - yaml + vitest@4.0.18(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3): + dependencies: + '@vitest/expect': 4.0.18 + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3)) + '@vitest/pretty-format': 4.0.18 + '@vitest/runner': 4.0.18 + '@vitest/snapshot': 4.0.18 + '@vitest/spy': 4.0.18 + '@vitest/utils': 4.0.18 + es-module-lexer: 1.7.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.3.1(@types/node@22.19.11)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.19.11 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + vitest@4.0.18(@types/node@25.2.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.3.3): dependencies: '@vitest/expect': 4.0.18 @@ -22634,6 +23340,16 @@ snapshots: yargs-parser@21.1.1: {} + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + yargs@17.7.2: dependencies: cliui: 8.0.1 From 16a1752305c211d61a49ff4192b821cc281b6e6c Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Tue, 24 Feb 2026 23:13:51 -0500 Subject: [PATCH 17/20] chore(csharp): update csharp-sdk seed (#12736) Co-authored-by: fern-support --- .../explicit-namespaces/reference.md | 65 ++++++++++++ .../explicit-namespaces/snippet.json | 12 +++ .../src/SeedApi.DynamicSnippets/Example33.cs | 6 +- .../src/SeedApi.DynamicSnippets/Example34.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example35.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example36.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example37.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example38.cs | 5 +- .../src/SeedApi.DynamicSnippets/Example39.cs | 5 +- .../src/SeedApi.DynamicSnippets/Example40.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example41.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example42.cs | 7 +- .../src/SeedApi.DynamicSnippets/Example43.cs | 13 +-- .../src/SeedApi.DynamicSnippets/Example44.cs | 10 +- .../src/SeedApi.DynamicSnippets/Example45.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example46.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example47.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example48.cs | 35 +------ .../src/SeedApi.DynamicSnippets/Example50.cs | 36 ++++++- .../src/SeedApi.DynamicSnippets/Example52.cs | 7 +- .../src/SeedApi.DynamicSnippets/Example53.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example54.cs | 9 +- .../src/SeedApi.DynamicSnippets/Example55.cs | 25 +++++ .../Endpoints/Params/IParamsClient.cs | 11 +++ .../Endpoints/Params/ParamsClient.cs | 89 +++++++++++++++++ .../include-exception-handler/reference.md | 65 ++++++++++++ .../include-exception-handler/snippet.json | 12 +++ .../src/SeedApi.DynamicSnippets/Example33.cs | 6 +- .../src/SeedApi.DynamicSnippets/Example34.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example35.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example36.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example37.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example38.cs | 5 +- .../src/SeedApi.DynamicSnippets/Example39.cs | 5 +- .../src/SeedApi.DynamicSnippets/Example40.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example41.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example42.cs | 7 +- .../src/SeedApi.DynamicSnippets/Example43.cs | 13 +-- .../src/SeedApi.DynamicSnippets/Example44.cs | 10 +- .../src/SeedApi.DynamicSnippets/Example45.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example46.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example47.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example48.cs | 34 +------ .../src/SeedApi.DynamicSnippets/Example50.cs | 35 ++++++- .../src/SeedApi.DynamicSnippets/Example52.cs | 7 +- .../src/SeedApi.DynamicSnippets/Example53.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example54.cs | 8 +- .../src/SeedApi.DynamicSnippets/Example55.cs | 24 +++++ .../Endpoints/Params/IParamsClient.cs | 11 +++ .../Endpoints/Params/ParamsClient.cs | 98 +++++++++++++++++++ .../no-generate-error-types/reference.md | 65 ++++++++++++ .../no-generate-error-types/snippet.json | 12 +++ .../src/SeedApi.DynamicSnippets/Example33.cs | 6 +- .../src/SeedApi.DynamicSnippets/Example34.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example35.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example36.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example37.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example38.cs | 5 +- .../src/SeedApi.DynamicSnippets/Example39.cs | 5 +- .../src/SeedApi.DynamicSnippets/Example40.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example41.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example42.cs | 7 +- .../src/SeedApi.DynamicSnippets/Example43.cs | 13 +-- .../src/SeedApi.DynamicSnippets/Example44.cs | 10 +- .../src/SeedApi.DynamicSnippets/Example45.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example46.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example47.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example48.cs | 34 +------ .../src/SeedApi.DynamicSnippets/Example50.cs | 35 ++++++- .../src/SeedApi.DynamicSnippets/Example52.cs | 7 +- .../src/SeedApi.DynamicSnippets/Example53.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example54.cs | 8 +- .../src/SeedApi.DynamicSnippets/Example55.cs | 24 +++++ .../Endpoints/Params/IParamsClient.cs | 11 +++ .../Endpoints/Params/ParamsClient.cs | 89 +++++++++++++++++ .../reference.md | 65 ++++++++++++ .../snippet.json | 12 +++ .../src/SeedApi.DynamicSnippets/Example33.cs | 6 +- .../src/SeedApi.DynamicSnippets/Example34.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example35.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example36.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example37.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example38.cs | 5 +- .../src/SeedApi.DynamicSnippets/Example39.cs | 5 +- .../src/SeedApi.DynamicSnippets/Example40.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example41.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example42.cs | 7 +- .../src/SeedApi.DynamicSnippets/Example43.cs | 13 +-- .../src/SeedApi.DynamicSnippets/Example44.cs | 10 +- .../src/SeedApi.DynamicSnippets/Example45.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example46.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example47.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example48.cs | 34 +------ .../src/SeedApi.DynamicSnippets/Example50.cs | 35 ++++++- .../src/SeedApi.DynamicSnippets/Example52.cs | 7 +- .../src/SeedApi.DynamicSnippets/Example53.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example54.cs | 8 +- .../src/SeedApi.DynamicSnippets/Example55.cs | 25 +++++ .../Endpoints/Params/IParamsClient.cs | 11 +++ .../Endpoints/Params/ParamsClient.cs | 89 +++++++++++++++++ .../reference.md | 65 ++++++++++++ .../snippet.json | 12 +++ .../src/SeedApi.DynamicSnippets/Example33.cs | 6 +- .../src/SeedApi.DynamicSnippets/Example34.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example35.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example36.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example37.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example38.cs | 5 +- .../src/SeedApi.DynamicSnippets/Example39.cs | 5 +- .../src/SeedApi.DynamicSnippets/Example40.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example41.cs | 4 +- .../src/SeedApi.DynamicSnippets/Example42.cs | 7 +- .../src/SeedApi.DynamicSnippets/Example43.cs | 13 +-- .../src/SeedApi.DynamicSnippets/Example44.cs | 10 +- .../src/SeedApi.DynamicSnippets/Example45.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example46.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example47.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example48.cs | 34 +------ .../src/SeedApi.DynamicSnippets/Example50.cs | 35 ++++++- .../src/SeedApi.DynamicSnippets/Example52.cs | 7 +- .../src/SeedApi.DynamicSnippets/Example53.cs | 2 +- .../src/SeedApi.DynamicSnippets/Example54.cs | 8 +- .../src/SeedApi.DynamicSnippets/Example55.cs | 24 +++++ .../Endpoints/Params/IParamsClient.cs | 11 +++ .../Endpoints/Params/ParamsClient.cs | 89 +++++++++++++++++ 125 files changed, 1417 insertions(+), 412 deletions(-) create mode 100644 seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example55.cs create mode 100644 seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example55.cs create mode 100644 seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example55.cs create mode 100644 seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example55.cs create mode 100644 seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example55.cs diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/reference.md b/seed/csharp-sdk/exhaustive/explicit-namespaces/reference.md index 510c2aa73f7a..66d9fda01e9e 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/reference.md +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/reference.md @@ -1679,6 +1679,71 @@ await client.Endpoints.Params.ModifyWithInlinePathAsync( + + + + +
client.Endpoints.Params.UploadWithPathAsync(param, Stream { ... }) -> WithRawResponseTask<ObjectWithRequiredField> +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```csharp +await client.Endpoints.Params.UploadWithPathAsync( + "upload-path", + new MemoryStream(Encoding.UTF8.GetBytes("[bytes]")) +); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**request:** `Stream` + +
+
+
+
+ +
diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/snippet.json b/seed/csharp-sdk/exhaustive/explicit-namespaces/snippet.json index a6da386649cb..ec7665d1e55f 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/snippet.json +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/snippet.json @@ -385,6 +385,18 @@ "client": "using SeedExhaustive.Endpoints.Params;\nusing SeedExhaustive;\n\nvar client = new SeedExhaustiveClient(\"TOKEN\");\nawait client.Endpoints.Params.ModifyWithInlinePathAsync(\n new ModifyResourceAtInlinedPath { Param = \"param\", Body = \"string\" }\n);\n" } }, + { + "example_identifier": null, + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "csharp", + "client": "using SeedExhaustive;\nusing System.Text;\n\nvar client = new SeedExhaustiveClient(\"TOKEN\");\nawait client.Endpoints.Params.UploadWithPathAsync(\n \"upload-path\",\n new MemoryStream(Encoding.UTF8.GetBytes(\"[bytes]\"))\n);\n" + } + }, { "example_identifier": null, "id": { diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example33.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example33.cs index 327691595837..3046b0f12cfd 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example33.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example33.cs @@ -1,4 +1,5 @@ using SeedExhaustive; +using System.Text; namespace Usage; @@ -12,8 +13,9 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnStringAsync( - "string" + await client.Endpoints.Params.UploadWithPathAsync( + "upload-path", + new MemoryStream(Encoding.UTF8.GetBytes("[bytes]")) ); } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example34.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example34.cs index 765fd1ce4de0..1350c471edb5 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example34.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example34.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnIntAsync( - 1 + await client.Endpoints.Primitive.GetAndReturnStringAsync( + "string" ); } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example35.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example35.cs index e25cb20b8c24..af04231fa3f9 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example35.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example35.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnLongAsync( - 1000000L + await client.Endpoints.Primitive.GetAndReturnIntAsync( + 1 ); } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example36.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example36.cs index 0ef878c15c9b..c7148a896c7e 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example36.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example36.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnDoubleAsync( - 1.1 + await client.Endpoints.Primitive.GetAndReturnLongAsync( + 1000000L ); } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example37.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example37.cs index 59be43b67106..7cc3fed5a3b7 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example37.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example37.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnBoolAsync( - true + await client.Endpoints.Primitive.GetAndReturnDoubleAsync( + 1.1 ); } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example38.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example38.cs index b0b7df7de46a..b2e0d31b8dfa 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example38.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example38.cs @@ -1,5 +1,4 @@ using SeedExhaustive; -using System.Globalization; namespace Usage; @@ -13,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnDatetimeAsync( - DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal) + await client.Endpoints.Primitive.GetAndReturnBoolAsync( + true ); } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example39.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example39.cs index 57eaba1ee666..4fd055c304aa 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example39.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example39.cs @@ -1,4 +1,5 @@ using SeedExhaustive; +using System.Globalization; namespace Usage; @@ -12,8 +13,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnDateAsync( - DateOnly.Parse("2023-01-15") + await client.Endpoints.Primitive.GetAndReturnDatetimeAsync( + DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal) ); } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example40.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example40.cs index eb19b0295541..37635770ad70 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example40.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example40.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnUuidAsync( - "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32" + await client.Endpoints.Primitive.GetAndReturnDateAsync( + DateOnly.Parse("2023-01-15") ); } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example41.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example41.cs index 1640dc532124..11c5e2f566ef 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example41.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example41.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnBase64Async( - "SGVsbG8gd29ybGQh" + await client.Endpoints.Primitive.GetAndReturnUuidAsync( + "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32" ); } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example42.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example42.cs index 9d2d40ab0eeb..fd382daaae2f 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example42.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example42.cs @@ -1,5 +1,4 @@ using SeedExhaustive; -using SeedExhaustive.Endpoints.Put; namespace Usage; @@ -13,10 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Put.AddAsync( - new PutRequest { - Id = "id" - } + await client.Endpoints.Primitive.GetAndReturnBase64Async( + "SGVsbG8gd29ybGQh" ); } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example43.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example43.cs index ef9215f3363e..07db42d70535 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example43.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example43.cs @@ -1,5 +1,5 @@ using SeedExhaustive; -using SeedExhaustive.Types.Union; +using SeedExhaustive.Endpoints.Put; namespace Usage; @@ -13,13 +13,10 @@ public async Task Do() { } ); - await client.Endpoints.Union.GetAndReturnUnionAsync( - new Animal( - new Dog { - Name = "name", - LikesToWoof = true - } - ) + await client.Endpoints.Put.AddAsync( + new PutRequest { + Id = "id" + } ); } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example44.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example44.cs index e206c098c255..b84ff2325630 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example44.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example44.cs @@ -1,4 +1,5 @@ using SeedExhaustive; +using SeedExhaustive.Types.Union; namespace Usage; @@ -12,7 +13,14 @@ public async Task Do() { } ); - await client.Endpoints.Urls.WithMixedCaseAsync(); + await client.Endpoints.Union.GetAndReturnUnionAsync( + new Animal( + new Dog { + Name = "name", + LikesToWoof = true + } + ) + ); } } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example45.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example45.cs index 5b1b8b6cb561..706c62f64383 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example45.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example45.cs @@ -12,7 +12,7 @@ public async Task Do() { } ); - await client.Endpoints.Urls.NoEndingSlashAsync(); + await client.Endpoints.Urls.WithMixedCaseAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example46.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example46.cs index 85320d1de0a7..96a18948446b 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example46.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example46.cs @@ -12,7 +12,7 @@ public async Task Do() { } ); - await client.Endpoints.Urls.WithEndingSlashAsync(); + await client.Endpoints.Urls.NoEndingSlashAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example47.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example47.cs index 240cca76f1f6..789ccf194043 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example47.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example47.cs @@ -12,7 +12,7 @@ public async Task Do() { } ); - await client.Endpoints.Urls.WithUnderscoresAsync(); + await client.Endpoints.Urls.WithEndingSlashAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example48.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example48.cs index edc0ee545a9e..8a0318020957 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example48.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example48.cs @@ -1,7 +1,4 @@ using SeedExhaustive; -using SeedExhaustive.InlinedRequests; -using SeedExhaustive.Types.Object; -using System.Globalization; namespace Usage; @@ -15,37 +12,7 @@ public async Task Do() { } ); - await client.InlinedRequests.PostWithObjectBodyandResponseAsync( - new PostWithObjectBody { - String = "string", - Integer = 1, - NestedObject = new ObjectWithOptionalField { - String = "string", - Integer = 1, - Long = 1000000L, - Double = 1.1, - Bool = true, - Datetime = DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal), - Date = DateOnly.Parse("2023-01-15"), - Uuid = "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", - Base64 = "SGVsbG8gd29ybGQh", - List = new List(){ - "list", - "list", - } - , - Set = new HashSet(){ - "set", - } - , - Map = new Dictionary(){ - [1] = "map", - } - , - Bigint = "1000000" - } - } - ); + await client.Endpoints.Urls.WithUnderscoresAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example50.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example50.cs index c08d77a58df1..07a5a9154024 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example50.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example50.cs @@ -1,4 +1,7 @@ using SeedExhaustive; +using SeedExhaustive.InlinedRequests; +using SeedExhaustive.Types.Object; +using System.Globalization; namespace Usage; @@ -12,10 +15,35 @@ public async Task Do() { } ); - await client.NoAuth.PostWithNoAuthAsync( - new Dictionary() - { - ["key"] = "value", + await client.InlinedRequests.PostWithObjectBodyandResponseAsync( + new PostWithObjectBody { + String = "string", + Integer = 1, + NestedObject = new ObjectWithOptionalField { + String = "string", + Integer = 1, + Long = 1000000L, + Double = 1.1, + Bool = true, + Datetime = DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal), + Date = DateOnly.Parse("2023-01-15"), + Uuid = "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + Base64 = "SGVsbG8gd29ybGQh", + List = new List(){ + "list", + "list", + } + , + Set = new HashSet(){ + "set", + } + , + Map = new Dictionary(){ + [1] = "map", + } + , + Bigint = "1000000" + } } ); } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example52.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example52.cs index b23f44b8725b..bdf76dcaca29 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example52.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example52.cs @@ -12,7 +12,12 @@ public async Task Do() { } ); - await client.NoReqBody.GetWithNoRequestBodyAsync(); + await client.NoAuth.PostWithNoAuthAsync( + new Dictionary() + { + ["key"] = "value", + } + ); } } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example53.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example53.cs index c40412dbe271..e9362e2b2e2b 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example53.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example53.cs @@ -12,7 +12,7 @@ public async Task Do() { } ); - await client.NoReqBody.PostWithNoRequestBodyAsync(); + await client.NoReqBody.GetWithNoRequestBodyAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example54.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example54.cs index c8de9e922f27..0cc9362a447c 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example54.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example54.cs @@ -1,5 +1,4 @@ using SeedExhaustive; -using SeedExhaustive.ReqWithHeaders; namespace Usage; @@ -13,13 +12,7 @@ public async Task Do() { } ); - await client.ReqWithHeaders.GetWithCustomHeaderAsync( - new ReqWithHeaders { - XTestServiceHeader = "X-TEST-SERVICE-HEADER", - XTestEndpointHeader = "X-TEST-ENDPOINT-HEADER", - Body = "string" - } - ); + await client.NoReqBody.PostWithNoRequestBodyAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example55.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example55.cs new file mode 100644 index 000000000000..f19bb76e6f90 --- /dev/null +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedApi.DynamicSnippets/Example55.cs @@ -0,0 +1,25 @@ +using SeedExhaustive; +using SeedExhaustive.ReqWithHeaders; + +namespace Usage; + +public class Example55 +{ + public async Task Do() { + var client = new SeedExhaustiveClient( + token: "", + clientOptions: new ClientOptions { + BaseUrl = "https://api.fern.com" + } + ); + + await client.ReqWithHeaders.GetWithCustomHeaderAsync( + new ReqWithHeaders { + XTestServiceHeader = "X-TEST-SERVICE-HEADER", + XTestEndpointHeader = "X-TEST-ENDPOINT-HEADER", + Body = "string" + } + ); + } + +} diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs index 6cc2f3fed926..4c91598fe25f 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs @@ -1,4 +1,5 @@ using SeedExhaustive; +using SeedExhaustive.Types.Object; namespace SeedExhaustive.Endpoints.Params; @@ -77,4 +78,14 @@ WithRawResponseTask ModifyWithInlinePathAsync( RequestOptions? options = null, CancellationToken cancellationToken = default ); + + /// + /// POST bytes with path param returning object + /// + WithRawResponseTask UploadWithPathAsync( + string param, + Stream request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ); } diff --git a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs index 9c5170750c71..0c863f7b36d3 100644 --- a/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs +++ b/seed/csharp-sdk/exhaustive/explicit-namespaces/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs @@ -1,6 +1,7 @@ using System.Text.Json; using SeedExhaustive; using SeedExhaustive.Core; +using SeedExhaustive.Types.Object; namespace SeedExhaustive.Endpoints.Params; @@ -276,6 +277,73 @@ private async Task> ModifyWithInlinePathAsyncCore( } } + private async Task> UploadWithPathAsyncCore( + string param, + Stream request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + var _headers = await new SeedExhaustive.Core.HeadersBuilder.Builder() + .Add(_client.Options.Headers) + .Add(_client.Options.AdditionalHeaders) + .Add(options?.AdditionalHeaders) + .BuildAsync() + .ConfigureAwait(false); + var response = await _client + .SendRequestAsync( + new StreamRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Post, + Path = string.Format( + "/params/path/{0}", + ValueConvert.ToPathParameterString(param) + ), + Body = request, + Headers = _headers, + Options = options, + }, + cancellationToken + ) + .ConfigureAwait(false); + if (response.StatusCode is >= 200 and < 400) + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + try + { + var responseData = JsonUtils.Deserialize(responseBody)!; + return new WithRawResponse() + { + Data = responseData, + RawResponse = new RawResponse() + { + StatusCode = response.Raw.StatusCode, + Url = response.Raw.RequestMessage?.RequestUri ?? new Uri("about:blank"), + Headers = ResponseHeaders.FromHttpResponseMessage(response.Raw), + }, + }; + } + catch (JsonException e) + { + throw new SeedExhaustiveApiException( + "Failed to deserialize response", + response.StatusCode, + responseBody, + e + ); + } + } + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + throw new SeedExhaustiveApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); + } + } + /// /// GET with path param /// @@ -562,4 +630,25 @@ public WithRawResponseTask ModifyWithInlinePathAsync( ModifyWithInlinePathAsyncCore(request, options, cancellationToken) ); } + + /// + /// POST bytes with path param returning object + /// + /// + /// await client.Endpoints.Params.UploadWithPathAsync( + /// "upload-path", + /// new MemoryStream(Encoding.UTF8.GetBytes("[bytes]")) + /// ); + /// + public WithRawResponseTask UploadWithPathAsync( + string param, + Stream request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + return new WithRawResponseTask( + UploadWithPathAsyncCore(param, request, options, cancellationToken) + ); + } } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/reference.md b/seed/csharp-sdk/exhaustive/include-exception-handler/reference.md index 510c2aa73f7a..66d9fda01e9e 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/reference.md +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/reference.md @@ -1679,6 +1679,71 @@ await client.Endpoints.Params.ModifyWithInlinePathAsync( + + + + +
client.Endpoints.Params.UploadWithPathAsync(param, Stream { ... }) -> WithRawResponseTask<ObjectWithRequiredField> +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```csharp +await client.Endpoints.Params.UploadWithPathAsync( + "upload-path", + new MemoryStream(Encoding.UTF8.GetBytes("[bytes]")) +); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**request:** `Stream` + +
+
+
+
+ +
diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/snippet.json b/seed/csharp-sdk/exhaustive/include-exception-handler/snippet.json index ed9a39cb697d..355bc3b6607a 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/snippet.json +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/snippet.json @@ -385,6 +385,18 @@ "client": "using SeedExhaustive.Endpoints;\nusing SeedExhaustive;\n\nvar client = new SeedExhaustiveClient(\"TOKEN\");\nawait client.Endpoints.Params.ModifyWithInlinePathAsync(\n new ModifyResourceAtInlinedPath { Param = \"param\", Body = \"string\" }\n);\n" } }, + { + "example_identifier": null, + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "csharp", + "client": "using SeedExhaustive;\nusing System.Text;\n\nvar client = new SeedExhaustiveClient(\"TOKEN\");\nawait client.Endpoints.Params.UploadWithPathAsync(\n \"upload-path\",\n new MemoryStream(Encoding.UTF8.GetBytes(\"[bytes]\"))\n);\n" + } + }, { "example_identifier": null, "id": { diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example33.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example33.cs index 327691595837..3046b0f12cfd 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example33.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example33.cs @@ -1,4 +1,5 @@ using SeedExhaustive; +using System.Text; namespace Usage; @@ -12,8 +13,9 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnStringAsync( - "string" + await client.Endpoints.Params.UploadWithPathAsync( + "upload-path", + new MemoryStream(Encoding.UTF8.GetBytes("[bytes]")) ); } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example34.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example34.cs index 765fd1ce4de0..1350c471edb5 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example34.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example34.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnIntAsync( - 1 + await client.Endpoints.Primitive.GetAndReturnStringAsync( + "string" ); } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example35.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example35.cs index e25cb20b8c24..af04231fa3f9 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example35.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example35.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnLongAsync( - 1000000L + await client.Endpoints.Primitive.GetAndReturnIntAsync( + 1 ); } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example36.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example36.cs index 0ef878c15c9b..c7148a896c7e 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example36.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example36.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnDoubleAsync( - 1.1 + await client.Endpoints.Primitive.GetAndReturnLongAsync( + 1000000L ); } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example37.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example37.cs index 59be43b67106..7cc3fed5a3b7 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example37.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example37.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnBoolAsync( - true + await client.Endpoints.Primitive.GetAndReturnDoubleAsync( + 1.1 ); } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example38.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example38.cs index b0b7df7de46a..b2e0d31b8dfa 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example38.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example38.cs @@ -1,5 +1,4 @@ using SeedExhaustive; -using System.Globalization; namespace Usage; @@ -13,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnDatetimeAsync( - DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal) + await client.Endpoints.Primitive.GetAndReturnBoolAsync( + true ); } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example39.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example39.cs index 57eaba1ee666..4fd055c304aa 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example39.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example39.cs @@ -1,4 +1,5 @@ using SeedExhaustive; +using System.Globalization; namespace Usage; @@ -12,8 +13,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnDateAsync( - DateOnly.Parse("2023-01-15") + await client.Endpoints.Primitive.GetAndReturnDatetimeAsync( + DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal) ); } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example40.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example40.cs index eb19b0295541..37635770ad70 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example40.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example40.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnUuidAsync( - "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32" + await client.Endpoints.Primitive.GetAndReturnDateAsync( + DateOnly.Parse("2023-01-15") ); } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example41.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example41.cs index 1640dc532124..11c5e2f566ef 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example41.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example41.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnBase64Async( - "SGVsbG8gd29ybGQh" + await client.Endpoints.Primitive.GetAndReturnUuidAsync( + "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32" ); } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example42.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example42.cs index a8b523436160..fd382daaae2f 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example42.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example42.cs @@ -1,5 +1,4 @@ using SeedExhaustive; -using SeedExhaustive.Endpoints; namespace Usage; @@ -13,10 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Put.AddAsync( - new PutRequest { - Id = "id" - } + await client.Endpoints.Primitive.GetAndReturnBase64Async( + "SGVsbG8gd29ybGQh" ); } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example43.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example43.cs index ba24e6359f7b..ec11c4efee7d 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example43.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example43.cs @@ -1,5 +1,5 @@ using SeedExhaustive; -using SeedExhaustive.Types; +using SeedExhaustive.Endpoints; namespace Usage; @@ -13,13 +13,10 @@ public async Task Do() { } ); - await client.Endpoints.Union.GetAndReturnUnionAsync( - new Animal( - new Dog { - Name = "name", - LikesToWoof = true - } - ) + await client.Endpoints.Put.AddAsync( + new PutRequest { + Id = "id" + } ); } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example44.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example44.cs index e206c098c255..528bbc9c5baf 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example44.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example44.cs @@ -1,4 +1,5 @@ using SeedExhaustive; +using SeedExhaustive.Types; namespace Usage; @@ -12,7 +13,14 @@ public async Task Do() { } ); - await client.Endpoints.Urls.WithMixedCaseAsync(); + await client.Endpoints.Union.GetAndReturnUnionAsync( + new Animal( + new Dog { + Name = "name", + LikesToWoof = true + } + ) + ); } } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example45.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example45.cs index 5b1b8b6cb561..706c62f64383 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example45.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example45.cs @@ -12,7 +12,7 @@ public async Task Do() { } ); - await client.Endpoints.Urls.NoEndingSlashAsync(); + await client.Endpoints.Urls.WithMixedCaseAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example46.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example46.cs index 85320d1de0a7..96a18948446b 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example46.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example46.cs @@ -12,7 +12,7 @@ public async Task Do() { } ); - await client.Endpoints.Urls.WithEndingSlashAsync(); + await client.Endpoints.Urls.NoEndingSlashAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example47.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example47.cs index 240cca76f1f6..789ccf194043 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example47.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example47.cs @@ -12,7 +12,7 @@ public async Task Do() { } ); - await client.Endpoints.Urls.WithUnderscoresAsync(); + await client.Endpoints.Urls.WithEndingSlashAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example48.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example48.cs index 2240349d6b72..8a0318020957 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example48.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example48.cs @@ -1,6 +1,4 @@ using SeedExhaustive; -using SeedExhaustive.Types; -using System.Globalization; namespace Usage; @@ -14,37 +12,7 @@ public async Task Do() { } ); - await client.InlinedRequests.PostWithObjectBodyandResponseAsync( - new PostWithObjectBody { - String = "string", - Integer = 1, - NestedObject = new ObjectWithOptionalField { - String = "string", - Integer = 1, - Long = 1000000L, - Double = 1.1, - Bool = true, - Datetime = DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal), - Date = DateOnly.Parse("2023-01-15"), - Uuid = "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", - Base64 = "SGVsbG8gd29ybGQh", - List = new List(){ - "list", - "list", - } - , - Set = new HashSet(){ - "set", - } - , - Map = new Dictionary(){ - [1] = "map", - } - , - Bigint = "1000000" - } - } - ); + await client.Endpoints.Urls.WithUnderscoresAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example50.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example50.cs index c08d77a58df1..08940108510d 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example50.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example50.cs @@ -1,4 +1,6 @@ using SeedExhaustive; +using SeedExhaustive.Types; +using System.Globalization; namespace Usage; @@ -12,10 +14,35 @@ public async Task Do() { } ); - await client.NoAuth.PostWithNoAuthAsync( - new Dictionary() - { - ["key"] = "value", + await client.InlinedRequests.PostWithObjectBodyandResponseAsync( + new PostWithObjectBody { + String = "string", + Integer = 1, + NestedObject = new ObjectWithOptionalField { + String = "string", + Integer = 1, + Long = 1000000L, + Double = 1.1, + Bool = true, + Datetime = DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal), + Date = DateOnly.Parse("2023-01-15"), + Uuid = "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + Base64 = "SGVsbG8gd29ybGQh", + List = new List(){ + "list", + "list", + } + , + Set = new HashSet(){ + "set", + } + , + Map = new Dictionary(){ + [1] = "map", + } + , + Bigint = "1000000" + } } ); } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example52.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example52.cs index b23f44b8725b..bdf76dcaca29 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example52.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example52.cs @@ -12,7 +12,12 @@ public async Task Do() { } ); - await client.NoReqBody.GetWithNoRequestBodyAsync(); + await client.NoAuth.PostWithNoAuthAsync( + new Dictionary() + { + ["key"] = "value", + } + ); } } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example53.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example53.cs index c40412dbe271..e9362e2b2e2b 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example53.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example53.cs @@ -12,7 +12,7 @@ public async Task Do() { } ); - await client.NoReqBody.PostWithNoRequestBodyAsync(); + await client.NoReqBody.GetWithNoRequestBodyAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example54.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example54.cs index 340481f12c1c..0cc9362a447c 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example54.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example54.cs @@ -12,13 +12,7 @@ public async Task Do() { } ); - await client.ReqWithHeaders.GetWithCustomHeaderAsync( - new ReqWithHeaders { - XTestServiceHeader = "X-TEST-SERVICE-HEADER", - XTestEndpointHeader = "X-TEST-ENDPOINT-HEADER", - Body = "string" - } - ); + await client.NoReqBody.PostWithNoRequestBodyAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example55.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example55.cs new file mode 100644 index 000000000000..75e43cd89099 --- /dev/null +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedApi.DynamicSnippets/Example55.cs @@ -0,0 +1,24 @@ +using SeedExhaustive; + +namespace Usage; + +public class Example55 +{ + public async Task Do() { + var client = new SeedExhaustiveClient( + token: "", + clientOptions: new ClientOptions { + BaseUrl = "https://api.fern.com" + } + ); + + await client.ReqWithHeaders.GetWithCustomHeaderAsync( + new ReqWithHeaders { + XTestServiceHeader = "X-TEST-SERVICE-HEADER", + XTestEndpointHeader = "X-TEST-ENDPOINT-HEADER", + Body = "string" + } + ); + } + +} diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs index cf764e37068e..d43334a0aa9b 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs @@ -1,4 +1,5 @@ using SeedExhaustive; +using SeedExhaustive.Types; namespace SeedExhaustive.Endpoints; @@ -77,4 +78,14 @@ WithRawResponseTask ModifyWithInlinePathAsync( RequestOptions? options = null, CancellationToken cancellationToken = default ); + + /// + /// POST bytes with path param returning object + /// + WithRawResponseTask UploadWithPathAsync( + string param, + Stream request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ); } diff --git a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs index 2da6dacc0844..35c76a700b92 100644 --- a/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs +++ b/seed/csharp-sdk/exhaustive/include-exception-handler/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs @@ -1,6 +1,7 @@ using System.Text.Json; using SeedExhaustive; using SeedExhaustive.Core; +using SeedExhaustive.Types; namespace SeedExhaustive.Endpoints; @@ -312,6 +313,82 @@ private async Task> ModifyWithInlinePathAsyncCore( .ConfigureAwait(false); } + private async Task> UploadWithPathAsyncCore( + string param, + Stream request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + return await _client + .Options.ExceptionHandler.TryCatchAsync(async () => + { + var _headers = await new SeedExhaustive.Core.HeadersBuilder.Builder() + .Add(_client.Options.Headers) + .Add(_client.Options.AdditionalHeaders) + .Add(options?.AdditionalHeaders) + .BuildAsync() + .ConfigureAwait(false); + var response = await _client + .SendRequestAsync( + new StreamRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Post, + Path = string.Format( + "/params/path/{0}", + ValueConvert.ToPathParameterString(param) + ), + Body = request, + Headers = _headers, + Options = options, + }, + cancellationToken + ) + .ConfigureAwait(false); + if (response.StatusCode is >= 200 and < 400) + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + try + { + var responseData = JsonUtils.Deserialize( + responseBody + )!; + return new WithRawResponse() + { + Data = responseData, + RawResponse = new RawResponse() + { + StatusCode = response.Raw.StatusCode, + Url = + response.Raw.RequestMessage?.RequestUri + ?? new Uri("about:blank"), + Headers = ResponseHeaders.FromHttpResponseMessage(response.Raw), + }, + }; + } + catch (JsonException e) + { + throw new SeedExhaustiveApiException( + "Failed to deserialize response", + response.StatusCode, + responseBody, + e + ); + } + } + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + throw new SeedExhaustiveApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); + } + }) + .ConfigureAwait(false); + } + /// /// GET with path param /// @@ -618,4 +695,25 @@ public WithRawResponseTask ModifyWithInlinePathAsync( ModifyWithInlinePathAsyncCore(request, options, cancellationToken) ); } + + /// + /// POST bytes with path param returning object + /// + /// + /// await client.Endpoints.Params.UploadWithPathAsync( + /// "upload-path", + /// new MemoryStream(Encoding.UTF8.GetBytes("[bytes]")) + /// ); + /// + public WithRawResponseTask UploadWithPathAsync( + string param, + Stream request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + return new WithRawResponseTask( + UploadWithPathAsyncCore(param, request, options, cancellationToken) + ); + } } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/reference.md b/seed/csharp-sdk/exhaustive/no-generate-error-types/reference.md index 510c2aa73f7a..66d9fda01e9e 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/reference.md +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/reference.md @@ -1679,6 +1679,71 @@ await client.Endpoints.Params.ModifyWithInlinePathAsync( + + + + +
client.Endpoints.Params.UploadWithPathAsync(param, Stream { ... }) -> WithRawResponseTask<ObjectWithRequiredField> +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```csharp +await client.Endpoints.Params.UploadWithPathAsync( + "upload-path", + new MemoryStream(Encoding.UTF8.GetBytes("[bytes]")) +); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**request:** `Stream` + +
+
+
+
+ +
diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/snippet.json b/seed/csharp-sdk/exhaustive/no-generate-error-types/snippet.json index ed9a39cb697d..355bc3b6607a 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/snippet.json +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/snippet.json @@ -385,6 +385,18 @@ "client": "using SeedExhaustive.Endpoints;\nusing SeedExhaustive;\n\nvar client = new SeedExhaustiveClient(\"TOKEN\");\nawait client.Endpoints.Params.ModifyWithInlinePathAsync(\n new ModifyResourceAtInlinedPath { Param = \"param\", Body = \"string\" }\n);\n" } }, + { + "example_identifier": null, + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "csharp", + "client": "using SeedExhaustive;\nusing System.Text;\n\nvar client = new SeedExhaustiveClient(\"TOKEN\");\nawait client.Endpoints.Params.UploadWithPathAsync(\n \"upload-path\",\n new MemoryStream(Encoding.UTF8.GetBytes(\"[bytes]\"))\n);\n" + } + }, { "example_identifier": null, "id": { diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example33.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example33.cs index 327691595837..3046b0f12cfd 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example33.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example33.cs @@ -1,4 +1,5 @@ using SeedExhaustive; +using System.Text; namespace Usage; @@ -12,8 +13,9 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnStringAsync( - "string" + await client.Endpoints.Params.UploadWithPathAsync( + "upload-path", + new MemoryStream(Encoding.UTF8.GetBytes("[bytes]")) ); } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example34.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example34.cs index 765fd1ce4de0..1350c471edb5 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example34.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example34.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnIntAsync( - 1 + await client.Endpoints.Primitive.GetAndReturnStringAsync( + "string" ); } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example35.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example35.cs index e25cb20b8c24..af04231fa3f9 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example35.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example35.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnLongAsync( - 1000000L + await client.Endpoints.Primitive.GetAndReturnIntAsync( + 1 ); } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example36.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example36.cs index 0ef878c15c9b..c7148a896c7e 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example36.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example36.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnDoubleAsync( - 1.1 + await client.Endpoints.Primitive.GetAndReturnLongAsync( + 1000000L ); } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example37.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example37.cs index 59be43b67106..7cc3fed5a3b7 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example37.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example37.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnBoolAsync( - true + await client.Endpoints.Primitive.GetAndReturnDoubleAsync( + 1.1 ); } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example38.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example38.cs index b0b7df7de46a..b2e0d31b8dfa 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example38.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example38.cs @@ -1,5 +1,4 @@ using SeedExhaustive; -using System.Globalization; namespace Usage; @@ -13,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnDatetimeAsync( - DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal) + await client.Endpoints.Primitive.GetAndReturnBoolAsync( + true ); } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example39.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example39.cs index 57eaba1ee666..4fd055c304aa 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example39.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example39.cs @@ -1,4 +1,5 @@ using SeedExhaustive; +using System.Globalization; namespace Usage; @@ -12,8 +13,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnDateAsync( - DateOnly.Parse("2023-01-15") + await client.Endpoints.Primitive.GetAndReturnDatetimeAsync( + DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal) ); } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example40.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example40.cs index eb19b0295541..37635770ad70 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example40.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example40.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnUuidAsync( - "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32" + await client.Endpoints.Primitive.GetAndReturnDateAsync( + DateOnly.Parse("2023-01-15") ); } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example41.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example41.cs index 1640dc532124..11c5e2f566ef 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example41.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example41.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnBase64Async( - "SGVsbG8gd29ybGQh" + await client.Endpoints.Primitive.GetAndReturnUuidAsync( + "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32" ); } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example42.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example42.cs index a8b523436160..fd382daaae2f 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example42.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example42.cs @@ -1,5 +1,4 @@ using SeedExhaustive; -using SeedExhaustive.Endpoints; namespace Usage; @@ -13,10 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Put.AddAsync( - new PutRequest { - Id = "id" - } + await client.Endpoints.Primitive.GetAndReturnBase64Async( + "SGVsbG8gd29ybGQh" ); } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example43.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example43.cs index ba24e6359f7b..ec11c4efee7d 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example43.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example43.cs @@ -1,5 +1,5 @@ using SeedExhaustive; -using SeedExhaustive.Types; +using SeedExhaustive.Endpoints; namespace Usage; @@ -13,13 +13,10 @@ public async Task Do() { } ); - await client.Endpoints.Union.GetAndReturnUnionAsync( - new Animal( - new Dog { - Name = "name", - LikesToWoof = true - } - ) + await client.Endpoints.Put.AddAsync( + new PutRequest { + Id = "id" + } ); } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example44.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example44.cs index e206c098c255..528bbc9c5baf 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example44.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example44.cs @@ -1,4 +1,5 @@ using SeedExhaustive; +using SeedExhaustive.Types; namespace Usage; @@ -12,7 +13,14 @@ public async Task Do() { } ); - await client.Endpoints.Urls.WithMixedCaseAsync(); + await client.Endpoints.Union.GetAndReturnUnionAsync( + new Animal( + new Dog { + Name = "name", + LikesToWoof = true + } + ) + ); } } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example45.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example45.cs index 5b1b8b6cb561..706c62f64383 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example45.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example45.cs @@ -12,7 +12,7 @@ public async Task Do() { } ); - await client.Endpoints.Urls.NoEndingSlashAsync(); + await client.Endpoints.Urls.WithMixedCaseAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example46.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example46.cs index 85320d1de0a7..96a18948446b 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example46.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example46.cs @@ -12,7 +12,7 @@ public async Task Do() { } ); - await client.Endpoints.Urls.WithEndingSlashAsync(); + await client.Endpoints.Urls.NoEndingSlashAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example47.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example47.cs index 240cca76f1f6..789ccf194043 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example47.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example47.cs @@ -12,7 +12,7 @@ public async Task Do() { } ); - await client.Endpoints.Urls.WithUnderscoresAsync(); + await client.Endpoints.Urls.WithEndingSlashAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example48.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example48.cs index 2240349d6b72..8a0318020957 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example48.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example48.cs @@ -1,6 +1,4 @@ using SeedExhaustive; -using SeedExhaustive.Types; -using System.Globalization; namespace Usage; @@ -14,37 +12,7 @@ public async Task Do() { } ); - await client.InlinedRequests.PostWithObjectBodyandResponseAsync( - new PostWithObjectBody { - String = "string", - Integer = 1, - NestedObject = new ObjectWithOptionalField { - String = "string", - Integer = 1, - Long = 1000000L, - Double = 1.1, - Bool = true, - Datetime = DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal), - Date = DateOnly.Parse("2023-01-15"), - Uuid = "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", - Base64 = "SGVsbG8gd29ybGQh", - List = new List(){ - "list", - "list", - } - , - Set = new HashSet(){ - "set", - } - , - Map = new Dictionary(){ - [1] = "map", - } - , - Bigint = "1000000" - } - } - ); + await client.Endpoints.Urls.WithUnderscoresAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example50.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example50.cs index c08d77a58df1..08940108510d 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example50.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example50.cs @@ -1,4 +1,6 @@ using SeedExhaustive; +using SeedExhaustive.Types; +using System.Globalization; namespace Usage; @@ -12,10 +14,35 @@ public async Task Do() { } ); - await client.NoAuth.PostWithNoAuthAsync( - new Dictionary() - { - ["key"] = "value", + await client.InlinedRequests.PostWithObjectBodyandResponseAsync( + new PostWithObjectBody { + String = "string", + Integer = 1, + NestedObject = new ObjectWithOptionalField { + String = "string", + Integer = 1, + Long = 1000000L, + Double = 1.1, + Bool = true, + Datetime = DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal), + Date = DateOnly.Parse("2023-01-15"), + Uuid = "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + Base64 = "SGVsbG8gd29ybGQh", + List = new List(){ + "list", + "list", + } + , + Set = new HashSet(){ + "set", + } + , + Map = new Dictionary(){ + [1] = "map", + } + , + Bigint = "1000000" + } } ); } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example52.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example52.cs index b23f44b8725b..bdf76dcaca29 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example52.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example52.cs @@ -12,7 +12,12 @@ public async Task Do() { } ); - await client.NoReqBody.GetWithNoRequestBodyAsync(); + await client.NoAuth.PostWithNoAuthAsync( + new Dictionary() + { + ["key"] = "value", + } + ); } } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example53.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example53.cs index c40412dbe271..e9362e2b2e2b 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example53.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example53.cs @@ -12,7 +12,7 @@ public async Task Do() { } ); - await client.NoReqBody.PostWithNoRequestBodyAsync(); + await client.NoReqBody.GetWithNoRequestBodyAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example54.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example54.cs index 340481f12c1c..0cc9362a447c 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example54.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example54.cs @@ -12,13 +12,7 @@ public async Task Do() { } ); - await client.ReqWithHeaders.GetWithCustomHeaderAsync( - new ReqWithHeaders { - XTestServiceHeader = "X-TEST-SERVICE-HEADER", - XTestEndpointHeader = "X-TEST-ENDPOINT-HEADER", - Body = "string" - } - ); + await client.NoReqBody.PostWithNoRequestBodyAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example55.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example55.cs new file mode 100644 index 000000000000..75e43cd89099 --- /dev/null +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedApi.DynamicSnippets/Example55.cs @@ -0,0 +1,24 @@ +using SeedExhaustive; + +namespace Usage; + +public class Example55 +{ + public async Task Do() { + var client = new SeedExhaustiveClient( + token: "", + clientOptions: new ClientOptions { + BaseUrl = "https://api.fern.com" + } + ); + + await client.ReqWithHeaders.GetWithCustomHeaderAsync( + new ReqWithHeaders { + XTestServiceHeader = "X-TEST-SERVICE-HEADER", + XTestEndpointHeader = "X-TEST-ENDPOINT-HEADER", + Body = "string" + } + ); + } + +} diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs index cf764e37068e..d43334a0aa9b 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs @@ -1,4 +1,5 @@ using SeedExhaustive; +using SeedExhaustive.Types; namespace SeedExhaustive.Endpoints; @@ -77,4 +78,14 @@ WithRawResponseTask ModifyWithInlinePathAsync( RequestOptions? options = null, CancellationToken cancellationToken = default ); + + /// + /// POST bytes with path param returning object + /// + WithRawResponseTask UploadWithPathAsync( + string param, + Stream request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ); } diff --git a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs index 3275a7bfcf50..b96ea6edc201 100644 --- a/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs +++ b/seed/csharp-sdk/exhaustive/no-generate-error-types/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs @@ -1,6 +1,7 @@ using System.Text.Json; using SeedExhaustive; using SeedExhaustive.Core; +using SeedExhaustive.Types; namespace SeedExhaustive.Endpoints; @@ -276,6 +277,73 @@ private async Task> ModifyWithInlinePathAsyncCore( } } + private async Task> UploadWithPathAsyncCore( + string param, + Stream request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + var _headers = await new SeedExhaustive.Core.HeadersBuilder.Builder() + .Add(_client.Options.Headers) + .Add(_client.Options.AdditionalHeaders) + .Add(options?.AdditionalHeaders) + .BuildAsync() + .ConfigureAwait(false); + var response = await _client + .SendRequestAsync( + new StreamRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Post, + Path = string.Format( + "/params/path/{0}", + ValueConvert.ToPathParameterString(param) + ), + Body = request, + Headers = _headers, + Options = options, + }, + cancellationToken + ) + .ConfigureAwait(false); + if (response.StatusCode is >= 200 and < 400) + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + try + { + var responseData = JsonUtils.Deserialize(responseBody)!; + return new WithRawResponse() + { + Data = responseData, + RawResponse = new RawResponse() + { + StatusCode = response.Raw.StatusCode, + Url = response.Raw.RequestMessage?.RequestUri ?? new Uri("about:blank"), + Headers = ResponseHeaders.FromHttpResponseMessage(response.Raw), + }, + }; + } + catch (JsonException e) + { + throw new SeedExhaustiveApiException( + "Failed to deserialize response", + response.StatusCode, + responseBody, + e + ); + } + } + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + throw new SeedExhaustiveApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); + } + } + /// /// GET with path param /// @@ -562,4 +630,25 @@ public WithRawResponseTask ModifyWithInlinePathAsync( ModifyWithInlinePathAsyncCore(request, options, cancellationToken) ); } + + /// + /// POST bytes with path param returning object + /// + /// + /// await client.Endpoints.Params.UploadWithPathAsync( + /// "upload-path", + /// new MemoryStream(Encoding.UTF8.GetBytes("[bytes]")) + /// ); + /// + public WithRawResponseTask UploadWithPathAsync( + string param, + Stream request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + return new WithRawResponseTask( + UploadWithPathAsyncCore(param, request, options, cancellationToken) + ); + } } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/reference.md b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/reference.md index 510c2aa73f7a..66d9fda01e9e 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/reference.md +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/reference.md @@ -1679,6 +1679,71 @@ await client.Endpoints.Params.ModifyWithInlinePathAsync( + + + + +
client.Endpoints.Params.UploadWithPathAsync(param, Stream { ... }) -> WithRawResponseTask<ObjectWithRequiredField> +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```csharp +await client.Endpoints.Params.UploadWithPathAsync( + "upload-path", + new MemoryStream(Encoding.UTF8.GetBytes("[bytes]")) +); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**request:** `Stream` + +
+
+
+
+ +
diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/snippet.json b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/snippet.json index ed9a39cb697d..355bc3b6607a 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/snippet.json +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/snippet.json @@ -385,6 +385,18 @@ "client": "using SeedExhaustive.Endpoints;\nusing SeedExhaustive;\n\nvar client = new SeedExhaustiveClient(\"TOKEN\");\nawait client.Endpoints.Params.ModifyWithInlinePathAsync(\n new ModifyResourceAtInlinedPath { Param = \"param\", Body = \"string\" }\n);\n" } }, + { + "example_identifier": null, + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "csharp", + "client": "using SeedExhaustive;\nusing System.Text;\n\nvar client = new SeedExhaustiveClient(\"TOKEN\");\nawait client.Endpoints.Params.UploadWithPathAsync(\n \"upload-path\",\n new MemoryStream(Encoding.UTF8.GetBytes(\"[bytes]\"))\n);\n" + } + }, { "example_identifier": null, "id": { diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example33.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example33.cs index 8ca67aafe03a..d8ebb950f8d4 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example33.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example33.cs @@ -1,5 +1,6 @@ using SeedExhaustive; using SeedExhaustive.Core; +using System.Text; namespace Usage; @@ -13,8 +14,9 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnStringAsync( - "string" + await client.Endpoints.Params.UploadWithPathAsync( + "upload-path", + new MemoryStream(Encoding.UTF8.GetBytes("[bytes]")) ); } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example34.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example34.cs index bfe27ab95842..0a5248723e5d 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example34.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example34.cs @@ -13,8 +13,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnIntAsync( - 1 + await client.Endpoints.Primitive.GetAndReturnStringAsync( + "string" ); } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example35.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example35.cs index c5541e27c7d8..a00406d05d3e 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example35.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example35.cs @@ -13,8 +13,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnLongAsync( - 1000000L + await client.Endpoints.Primitive.GetAndReturnIntAsync( + 1 ); } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example36.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example36.cs index f0721de44c3e..20668286d208 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example36.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example36.cs @@ -13,8 +13,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnDoubleAsync( - 1.1 + await client.Endpoints.Primitive.GetAndReturnLongAsync( + 1000000L ); } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example37.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example37.cs index 7f8faf2229d7..6909daf52fd4 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example37.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example37.cs @@ -13,8 +13,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnBoolAsync( - true + await client.Endpoints.Primitive.GetAndReturnDoubleAsync( + 1.1 ); } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example38.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example38.cs index c169c2e317be..16c53962d203 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example38.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example38.cs @@ -1,6 +1,5 @@ using SeedExhaustive; using SeedExhaustive.Core; -using System.Globalization; namespace Usage; @@ -14,8 +13,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnDatetimeAsync( - DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal) + await client.Endpoints.Primitive.GetAndReturnBoolAsync( + true ); } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example39.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example39.cs index 27800fb4f0cf..a94a27c5bcd0 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example39.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example39.cs @@ -1,5 +1,6 @@ using SeedExhaustive; using SeedExhaustive.Core; +using System.Globalization; namespace Usage; @@ -13,8 +14,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnDateAsync( - DateOnly.Parse("2023-01-15") + await client.Endpoints.Primitive.GetAndReturnDatetimeAsync( + DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal) ); } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example40.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example40.cs index 50a051c95258..105c95adb41d 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example40.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example40.cs @@ -13,8 +13,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnUuidAsync( - "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32" + await client.Endpoints.Primitive.GetAndReturnDateAsync( + DateOnly.Parse("2023-01-15") ); } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example41.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example41.cs index b0cd60878404..4010ef1baf42 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example41.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example41.cs @@ -13,8 +13,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnBase64Async( - "SGVsbG8gd29ybGQh" + await client.Endpoints.Primitive.GetAndReturnUuidAsync( + "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32" ); } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example42.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example42.cs index d7d13236c5e3..685914550c63 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example42.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example42.cs @@ -1,6 +1,5 @@ using SeedExhaustive; using SeedExhaustive.Core; -using SeedExhaustive.Endpoints; namespace Usage; @@ -14,10 +13,8 @@ public async Task Do() { } ); - await client.Endpoints.Put.AddAsync( - new PutRequest { - Id = "id" - } + await client.Endpoints.Primitive.GetAndReturnBase64Async( + "SGVsbG8gd29ybGQh" ); } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example43.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example43.cs index b311b1f018ca..5de52b7ad986 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example43.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example43.cs @@ -1,6 +1,6 @@ using SeedExhaustive; using SeedExhaustive.Core; -using SeedExhaustive.Types; +using SeedExhaustive.Endpoints; namespace Usage; @@ -14,13 +14,10 @@ public async Task Do() { } ); - await client.Endpoints.Union.GetAndReturnUnionAsync( - new Animal( - new Dog { - Name = "name", - LikesToWoof = true - } - ) + await client.Endpoints.Put.AddAsync( + new PutRequest { + Id = "id" + } ); } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example44.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example44.cs index d84f8d54df8b..924ff8780ade 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example44.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example44.cs @@ -1,5 +1,6 @@ using SeedExhaustive; using SeedExhaustive.Core; +using SeedExhaustive.Types; namespace Usage; @@ -13,7 +14,14 @@ public async Task Do() { } ); - await client.Endpoints.Urls.WithMixedCaseAsync(); + await client.Endpoints.Union.GetAndReturnUnionAsync( + new Animal( + new Dog { + Name = "name", + LikesToWoof = true + } + ) + ); } } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example45.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example45.cs index b85c297b68e0..e2d3dd7f124d 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example45.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example45.cs @@ -13,7 +13,7 @@ public async Task Do() { } ); - await client.Endpoints.Urls.NoEndingSlashAsync(); + await client.Endpoints.Urls.WithMixedCaseAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example46.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example46.cs index 319ead309764..173e7e9f549f 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example46.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example46.cs @@ -13,7 +13,7 @@ public async Task Do() { } ); - await client.Endpoints.Urls.WithEndingSlashAsync(); + await client.Endpoints.Urls.NoEndingSlashAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example47.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example47.cs index 4bc8e883c3d6..4d57205b80cb 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example47.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example47.cs @@ -13,7 +13,7 @@ public async Task Do() { } ); - await client.Endpoints.Urls.WithUnderscoresAsync(); + await client.Endpoints.Urls.WithEndingSlashAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example48.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example48.cs index 86d67ad76222..c299a09066a0 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example48.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example48.cs @@ -1,7 +1,5 @@ using SeedExhaustive; using SeedExhaustive.Core; -using SeedExhaustive.Types; -using System.Globalization; namespace Usage; @@ -15,37 +13,7 @@ public async Task Do() { } ); - await client.InlinedRequests.PostWithObjectBodyandResponseAsync( - new PostWithObjectBody { - String = "string", - Integer = 1, - NestedObject = new ObjectWithOptionalField { - String = "string", - Integer = 1, - Long = 1000000L, - Double = 1.1, - Bool = true, - Datetime = DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal), - Date = DateOnly.Parse("2023-01-15"), - Uuid = "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", - Base64 = "SGVsbG8gd29ybGQh", - List = new List(){ - "list", - "list", - } - , - Set = new HashSet(){ - "set", - } - , - Map = new Dictionary(){ - [1] = "map", - } - , - Bigint = "1000000" - } - } - ); + await client.Endpoints.Urls.WithUnderscoresAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example50.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example50.cs index 4ee72c98ea58..d58d41183d3b 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example50.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example50.cs @@ -1,5 +1,7 @@ using SeedExhaustive; using SeedExhaustive.Core; +using SeedExhaustive.Types; +using System.Globalization; namespace Usage; @@ -13,10 +15,35 @@ public async Task Do() { } ); - await client.NoAuth.PostWithNoAuthAsync( - new Dictionary() - { - ["key"] = "value", + await client.InlinedRequests.PostWithObjectBodyandResponseAsync( + new PostWithObjectBody { + String = "string", + Integer = 1, + NestedObject = new ObjectWithOptionalField { + String = "string", + Integer = 1, + Long = 1000000L, + Double = 1.1, + Bool = true, + Datetime = DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal), + Date = DateOnly.Parse("2023-01-15"), + Uuid = "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + Base64 = "SGVsbG8gd29ybGQh", + List = new List(){ + "list", + "list", + } + , + Set = new HashSet(){ + "set", + } + , + Map = new Dictionary(){ + [1] = "map", + } + , + Bigint = "1000000" + } } ); } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example52.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example52.cs index b3af29367642..197bb984aa76 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example52.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example52.cs @@ -13,7 +13,12 @@ public async Task Do() { } ); - await client.NoReqBody.GetWithNoRequestBodyAsync(); + await client.NoAuth.PostWithNoAuthAsync( + new Dictionary() + { + ["key"] = "value", + } + ); } } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example53.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example53.cs index 47f4b4686625..80f4487a7dda 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example53.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example53.cs @@ -13,7 +13,7 @@ public async Task Do() { } ); - await client.NoReqBody.PostWithNoRequestBodyAsync(); + await client.NoReqBody.GetWithNoRequestBodyAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example54.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example54.cs index f9b04e25144c..c42455284555 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example54.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example54.cs @@ -13,13 +13,7 @@ public async Task Do() { } ); - await client.ReqWithHeaders.GetWithCustomHeaderAsync( - new ReqWithHeaders { - XTestServiceHeader = "X-TEST-SERVICE-HEADER", - XTestEndpointHeader = "X-TEST-ENDPOINT-HEADER", - Body = "string" - } - ); + await client.NoReqBody.PostWithNoRequestBodyAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example55.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example55.cs new file mode 100644 index 000000000000..731b6aeb3223 --- /dev/null +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedApi.DynamicSnippets/Example55.cs @@ -0,0 +1,25 @@ +using SeedExhaustive; +using SeedExhaustive.Core; + +namespace Usage; + +public class Example55 +{ + public async Task Do() { + var client = new SeedExhaustiveClient( + token: "", + clientOptions: new ClientOptions { + BaseUrl = "https://api.fern.com" + } + ); + + await client.ReqWithHeaders.GetWithCustomHeaderAsync( + new ReqWithHeaders { + XTestServiceHeader = "X-TEST-SERVICE-HEADER", + XTestEndpointHeader = "X-TEST-ENDPOINT-HEADER", + Body = "string" + } + ); + } + +} diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs index 848448ad3e41..b40da4a82641 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs @@ -1,5 +1,6 @@ using SeedExhaustive; using SeedExhaustive.Core; +using SeedExhaustive.Types; namespace SeedExhaustive.Endpoints; @@ -78,4 +79,14 @@ WithRawResponseTask ModifyWithInlinePathAsync( RequestOptions? options = null, CancellationToken cancellationToken = default ); + + /// + /// POST bytes with path param returning object + /// + WithRawResponseTask UploadWithPathAsync( + string param, + Stream request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ); } diff --git a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs index 3275a7bfcf50..b96ea6edc201 100644 --- a/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs +++ b/seed/csharp-sdk/exhaustive/no-root-namespace-for-core-classes/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs @@ -1,6 +1,7 @@ using System.Text.Json; using SeedExhaustive; using SeedExhaustive.Core; +using SeedExhaustive.Types; namespace SeedExhaustive.Endpoints; @@ -276,6 +277,73 @@ private async Task> ModifyWithInlinePathAsyncCore( } } + private async Task> UploadWithPathAsyncCore( + string param, + Stream request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + var _headers = await new SeedExhaustive.Core.HeadersBuilder.Builder() + .Add(_client.Options.Headers) + .Add(_client.Options.AdditionalHeaders) + .Add(options?.AdditionalHeaders) + .BuildAsync() + .ConfigureAwait(false); + var response = await _client + .SendRequestAsync( + new StreamRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Post, + Path = string.Format( + "/params/path/{0}", + ValueConvert.ToPathParameterString(param) + ), + Body = request, + Headers = _headers, + Options = options, + }, + cancellationToken + ) + .ConfigureAwait(false); + if (response.StatusCode is >= 200 and < 400) + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + try + { + var responseData = JsonUtils.Deserialize(responseBody)!; + return new WithRawResponse() + { + Data = responseData, + RawResponse = new RawResponse() + { + StatusCode = response.Raw.StatusCode, + Url = response.Raw.RequestMessage?.RequestUri ?? new Uri("about:blank"), + Headers = ResponseHeaders.FromHttpResponseMessage(response.Raw), + }, + }; + } + catch (JsonException e) + { + throw new SeedExhaustiveApiException( + "Failed to deserialize response", + response.StatusCode, + responseBody, + e + ); + } + } + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + throw new SeedExhaustiveApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); + } + } + /// /// GET with path param /// @@ -562,4 +630,25 @@ public WithRawResponseTask ModifyWithInlinePathAsync( ModifyWithInlinePathAsyncCore(request, options, cancellationToken) ); } + + /// + /// POST bytes with path param returning object + /// + /// + /// await client.Endpoints.Params.UploadWithPathAsync( + /// "upload-path", + /// new MemoryStream(Encoding.UTF8.GetBytes("[bytes]")) + /// ); + /// + public WithRawResponseTask UploadWithPathAsync( + string param, + Stream request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + return new WithRawResponseTask( + UploadWithPathAsyncCore(param, request, options, cancellationToken) + ); + } } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/reference.md b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/reference.md index 510c2aa73f7a..66d9fda01e9e 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/reference.md +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/reference.md @@ -1679,6 +1679,71 @@ await client.Endpoints.Params.ModifyWithInlinePathAsync( + + + + +
client.Endpoints.Params.UploadWithPathAsync(param, Stream { ... }) -> WithRawResponseTask<ObjectWithRequiredField> +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```csharp +await client.Endpoints.Params.UploadWithPathAsync( + "upload-path", + new MemoryStream(Encoding.UTF8.GetBytes("[bytes]")) +); +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**request:** `Stream` + +
+
+
+
+ +
diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/snippet.json b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/snippet.json index ed9a39cb697d..355bc3b6607a 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/snippet.json +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/snippet.json @@ -385,6 +385,18 @@ "client": "using SeedExhaustive.Endpoints;\nusing SeedExhaustive;\n\nvar client = new SeedExhaustiveClient(\"TOKEN\");\nawait client.Endpoints.Params.ModifyWithInlinePathAsync(\n new ModifyResourceAtInlinedPath { Param = \"param\", Body = \"string\" }\n);\n" } }, + { + "example_identifier": null, + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "csharp", + "client": "using SeedExhaustive;\nusing System.Text;\n\nvar client = new SeedExhaustiveClient(\"TOKEN\");\nawait client.Endpoints.Params.UploadWithPathAsync(\n \"upload-path\",\n new MemoryStream(Encoding.UTF8.GetBytes(\"[bytes]\"))\n);\n" + } + }, { "example_identifier": null, "id": { diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example33.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example33.cs index 327691595837..3046b0f12cfd 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example33.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example33.cs @@ -1,4 +1,5 @@ using SeedExhaustive; +using System.Text; namespace Usage; @@ -12,8 +13,9 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnStringAsync( - "string" + await client.Endpoints.Params.UploadWithPathAsync( + "upload-path", + new MemoryStream(Encoding.UTF8.GetBytes("[bytes]")) ); } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example34.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example34.cs index 765fd1ce4de0..1350c471edb5 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example34.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example34.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnIntAsync( - 1 + await client.Endpoints.Primitive.GetAndReturnStringAsync( + "string" ); } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example35.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example35.cs index e25cb20b8c24..af04231fa3f9 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example35.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example35.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnLongAsync( - 1000000L + await client.Endpoints.Primitive.GetAndReturnIntAsync( + 1 ); } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example36.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example36.cs index 0ef878c15c9b..c7148a896c7e 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example36.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example36.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnDoubleAsync( - 1.1 + await client.Endpoints.Primitive.GetAndReturnLongAsync( + 1000000L ); } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example37.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example37.cs index 59be43b67106..7cc3fed5a3b7 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example37.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example37.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnBoolAsync( - true + await client.Endpoints.Primitive.GetAndReturnDoubleAsync( + 1.1 ); } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example38.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example38.cs index b0b7df7de46a..b2e0d31b8dfa 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example38.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example38.cs @@ -1,5 +1,4 @@ using SeedExhaustive; -using System.Globalization; namespace Usage; @@ -13,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnDatetimeAsync( - DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal) + await client.Endpoints.Primitive.GetAndReturnBoolAsync( + true ); } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example39.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example39.cs index 57eaba1ee666..4fd055c304aa 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example39.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example39.cs @@ -1,4 +1,5 @@ using SeedExhaustive; +using System.Globalization; namespace Usage; @@ -12,8 +13,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnDateAsync( - DateOnly.Parse("2023-01-15") + await client.Endpoints.Primitive.GetAndReturnDatetimeAsync( + DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal) ); } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example40.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example40.cs index eb19b0295541..37635770ad70 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example40.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example40.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnUuidAsync( - "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32" + await client.Endpoints.Primitive.GetAndReturnDateAsync( + DateOnly.Parse("2023-01-15") ); } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example41.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example41.cs index 1640dc532124..11c5e2f566ef 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example41.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example41.cs @@ -12,8 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Primitive.GetAndReturnBase64Async( - "SGVsbG8gd29ybGQh" + await client.Endpoints.Primitive.GetAndReturnUuidAsync( + "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32" ); } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example42.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example42.cs index a8b523436160..fd382daaae2f 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example42.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example42.cs @@ -1,5 +1,4 @@ using SeedExhaustive; -using SeedExhaustive.Endpoints; namespace Usage; @@ -13,10 +12,8 @@ public async Task Do() { } ); - await client.Endpoints.Put.AddAsync( - new PutRequest { - Id = "id" - } + await client.Endpoints.Primitive.GetAndReturnBase64Async( + "SGVsbG8gd29ybGQh" ); } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example43.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example43.cs index ba24e6359f7b..ec11c4efee7d 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example43.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example43.cs @@ -1,5 +1,5 @@ using SeedExhaustive; -using SeedExhaustive.Types; +using SeedExhaustive.Endpoints; namespace Usage; @@ -13,13 +13,10 @@ public async Task Do() { } ); - await client.Endpoints.Union.GetAndReturnUnionAsync( - new Animal( - new Dog { - Name = "name", - LikesToWoof = true - } - ) + await client.Endpoints.Put.AddAsync( + new PutRequest { + Id = "id" + } ); } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example44.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example44.cs index e206c098c255..528bbc9c5baf 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example44.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example44.cs @@ -1,4 +1,5 @@ using SeedExhaustive; +using SeedExhaustive.Types; namespace Usage; @@ -12,7 +13,14 @@ public async Task Do() { } ); - await client.Endpoints.Urls.WithMixedCaseAsync(); + await client.Endpoints.Union.GetAndReturnUnionAsync( + new Animal( + new Dog { + Name = "name", + LikesToWoof = true + } + ) + ); } } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example45.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example45.cs index 5b1b8b6cb561..706c62f64383 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example45.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example45.cs @@ -12,7 +12,7 @@ public async Task Do() { } ); - await client.Endpoints.Urls.NoEndingSlashAsync(); + await client.Endpoints.Urls.WithMixedCaseAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example46.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example46.cs index 85320d1de0a7..96a18948446b 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example46.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example46.cs @@ -12,7 +12,7 @@ public async Task Do() { } ); - await client.Endpoints.Urls.WithEndingSlashAsync(); + await client.Endpoints.Urls.NoEndingSlashAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example47.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example47.cs index 240cca76f1f6..789ccf194043 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example47.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example47.cs @@ -12,7 +12,7 @@ public async Task Do() { } ); - await client.Endpoints.Urls.WithUnderscoresAsync(); + await client.Endpoints.Urls.WithEndingSlashAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example48.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example48.cs index 2240349d6b72..8a0318020957 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example48.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example48.cs @@ -1,6 +1,4 @@ using SeedExhaustive; -using SeedExhaustive.Types; -using System.Globalization; namespace Usage; @@ -14,37 +12,7 @@ public async Task Do() { } ); - await client.InlinedRequests.PostWithObjectBodyandResponseAsync( - new PostWithObjectBody { - String = "string", - Integer = 1, - NestedObject = new ObjectWithOptionalField { - String = "string", - Integer = 1, - Long = 1000000L, - Double = 1.1, - Bool = true, - Datetime = DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal), - Date = DateOnly.Parse("2023-01-15"), - Uuid = "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", - Base64 = "SGVsbG8gd29ybGQh", - List = new List(){ - "list", - "list", - } - , - Set = new HashSet(){ - "set", - } - , - Map = new Dictionary(){ - [1] = "map", - } - , - Bigint = "1000000" - } - } - ); + await client.Endpoints.Urls.WithUnderscoresAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example50.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example50.cs index c08d77a58df1..08940108510d 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example50.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example50.cs @@ -1,4 +1,6 @@ using SeedExhaustive; +using SeedExhaustive.Types; +using System.Globalization; namespace Usage; @@ -12,10 +14,35 @@ public async Task Do() { } ); - await client.NoAuth.PostWithNoAuthAsync( - new Dictionary() - { - ["key"] = "value", + await client.InlinedRequests.PostWithObjectBodyandResponseAsync( + new PostWithObjectBody { + String = "string", + Integer = 1, + NestedObject = new ObjectWithOptionalField { + String = "string", + Integer = 1, + Long = 1000000L, + Double = 1.1, + Bool = true, + Datetime = DateTime.Parse("2024-01-15T09:30:00Z", null, DateTimeStyles.AdjustToUniversal), + Date = DateOnly.Parse("2023-01-15"), + Uuid = "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32", + Base64 = "SGVsbG8gd29ybGQh", + List = new List(){ + "list", + "list", + } + , + Set = new HashSet(){ + "set", + } + , + Map = new Dictionary(){ + [1] = "map", + } + , + Bigint = "1000000" + } } ); } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example52.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example52.cs index b23f44b8725b..bdf76dcaca29 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example52.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example52.cs @@ -12,7 +12,12 @@ public async Task Do() { } ); - await client.NoReqBody.GetWithNoRequestBodyAsync(); + await client.NoAuth.PostWithNoAuthAsync( + new Dictionary() + { + ["key"] = "value", + } + ); } } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example53.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example53.cs index c40412dbe271..e9362e2b2e2b 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example53.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example53.cs @@ -12,7 +12,7 @@ public async Task Do() { } ); - await client.NoReqBody.PostWithNoRequestBodyAsync(); + await client.NoReqBody.GetWithNoRequestBodyAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example54.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example54.cs index 340481f12c1c..0cc9362a447c 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example54.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example54.cs @@ -12,13 +12,7 @@ public async Task Do() { } ); - await client.ReqWithHeaders.GetWithCustomHeaderAsync( - new ReqWithHeaders { - XTestServiceHeader = "X-TEST-SERVICE-HEADER", - XTestEndpointHeader = "X-TEST-ENDPOINT-HEADER", - Body = "string" - } - ); + await client.NoReqBody.PostWithNoRequestBodyAsync(); } } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example55.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example55.cs new file mode 100644 index 000000000000..75e43cd89099 --- /dev/null +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedApi.DynamicSnippets/Example55.cs @@ -0,0 +1,24 @@ +using SeedExhaustive; + +namespace Usage; + +public class Example55 +{ + public async Task Do() { + var client = new SeedExhaustiveClient( + token: "", + clientOptions: new ClientOptions { + BaseUrl = "https://api.fern.com" + } + ); + + await client.ReqWithHeaders.GetWithCustomHeaderAsync( + new ReqWithHeaders { + XTestServiceHeader = "X-TEST-SERVICE-HEADER", + XTestEndpointHeader = "X-TEST-ENDPOINT-HEADER", + Body = "string" + } + ); + } + +} diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs index cf764e37068e..d43334a0aa9b 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedExhaustive/Endpoints/Params/IParamsClient.cs @@ -1,4 +1,5 @@ using SeedExhaustive; +using SeedExhaustive.Types; namespace SeedExhaustive.Endpoints; @@ -77,4 +78,14 @@ WithRawResponseTask ModifyWithInlinePathAsync( RequestOptions? options = null, CancellationToken cancellationToken = default ); + + /// + /// POST bytes with path param returning object + /// + WithRawResponseTask UploadWithPathAsync( + string param, + Stream request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ); } diff --git a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs index 20e5b189b17d..ee327af5f9a6 100644 --- a/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs +++ b/seed/csharp-sdk/exhaustive/redact-response-body-on-error/src/SeedExhaustive/Endpoints/Params/ParamsClient.cs @@ -1,6 +1,7 @@ using System.Text.Json; using SeedExhaustive; using SeedExhaustive.Core; +using SeedExhaustive.Types; namespace SeedExhaustive.Endpoints; @@ -276,6 +277,73 @@ private async Task> ModifyWithInlinePathAsyncCore( } } + private async Task> UploadWithPathAsyncCore( + string param, + Stream request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + var _headers = await new SeedExhaustive.Core.HeadersBuilder.Builder() + .Add(_client.Options.Headers) + .Add(_client.Options.AdditionalHeaders) + .Add(options?.AdditionalHeaders) + .BuildAsync() + .ConfigureAwait(false); + var response = await _client + .SendRequestAsync( + new StreamRequest + { + BaseUrl = _client.Options.BaseUrl, + Method = HttpMethod.Post, + Path = string.Format( + "/params/path/{0}", + ValueConvert.ToPathParameterString(param) + ), + Body = request, + Headers = _headers, + Options = options, + }, + cancellationToken + ) + .ConfigureAwait(false); + if (response.StatusCode is >= 200 and < 400) + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + try + { + var responseData = JsonUtils.Deserialize(responseBody)!; + return new WithRawResponse() + { + Data = responseData, + RawResponse = new RawResponse() + { + StatusCode = response.Raw.StatusCode, + Url = response.Raw.RequestMessage?.RequestUri ?? new Uri("about:blank"), + Headers = ResponseHeaders.FromHttpResponseMessage(response.Raw), + }, + }; + } + catch (JsonException e) + { + throw new SeedExhaustiveApiException( + "Failed to deserialize response", + response.StatusCode, + null, + e + ); + } + } + { + var responseBody = await response.Raw.Content.ReadAsStringAsync(); + throw new SeedExhaustiveApiException( + $"Error with status code {response.StatusCode}", + response.StatusCode, + responseBody + ); + } + } + /// /// GET with path param /// @@ -562,4 +630,25 @@ public WithRawResponseTask ModifyWithInlinePathAsync( ModifyWithInlinePathAsyncCore(request, options, cancellationToken) ); } + + /// + /// POST bytes with path param returning object + /// + /// + /// await client.Endpoints.Params.UploadWithPathAsync( + /// "upload-path", + /// new MemoryStream(Encoding.UTF8.GetBytes("[bytes]")) + /// ); + /// + public WithRawResponseTask UploadWithPathAsync( + string param, + Stream request, + RequestOptions? options = null, + CancellationToken cancellationToken = default + ) + { + return new WithRawResponseTask( + UploadWithPathAsyncCore(param, request, options, cancellationToken) + ); + } } From 6b1d2a110cde37778556b1effcb86049503baafd Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Wed, 25 Feb 2026 00:16:29 -0500 Subject: [PATCH 18/20] chore(csharp): update csharp-sdk seed (#12755) Co-authored-by: tstanmay13 From f03a7f85a3aac953fe2772ffa95d15314001949f Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Wed, 25 Feb 2026 00:18:20 -0500 Subject: [PATCH 19/20] chore(postman): update postman seed (#12735) Co-authored-by: tstanmay13 --- seed/postman/exhaustive/collection.json | 78 +++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/seed/postman/exhaustive/collection.json b/seed/postman/exhaustive/collection.json index a18c80360ac1..6008d67f70d2 100644 --- a/seed/postman/exhaustive/collection.json +++ b/seed/postman/exhaustive/collection.json @@ -2765,6 +2765,84 @@ "_postman_previewlanguage": "json" } ] + }, + { + "_type": "endpoint", + "name": "Upload With Path", + "request": { + "description": "POST bytes with path param returning object", + "url": { + "raw": "{{baseUrl}}/params/path/:param", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "params", + "path", + ":param" + ], + "query": [], + "variable": [ + { + "key": "param", + "description": null, + "value": "upload-path" + } + ] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": null + }, + "response": [ + { + "name": "Success", + "status": "OK", + "code": 200, + "originalRequest": { + "description": "POST bytes with path param returning object", + "url": { + "raw": "{{baseUrl}}/params/path/:param", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "params", + "path", + ":param" + ], + "query": [], + "variable": [ + { + "key": "param", + "description": null, + "value": "upload-path" + } + ] + }, + "header": [ + { + "type": "text", + "key": "Content-Type", + "value": "application/json" + } + ], + "method": "POST", + "auth": null, + "body": null + }, + "description": null, + "body": "{\n \"string\": \"uploaded\"\n}", + "_postman_previewlanguage": "json" + } + ] } ] }, From 7d76cc60b51dc7728e6dd10b4ed8d009f0b1455a Mon Sep 17 00:00:00 2001 From: Fern Support <126544928+fern-support@users.noreply.github.com> Date: Wed, 25 Feb 2026 00:18:56 -0500 Subject: [PATCH 20/20] chore(typescript): update ts-sdk seed (#12741) Co-authored-by: tstanmay13 --- .../exhaustive/allow-extra-fields/README.md | 46 ++ .../allow-extra-fields/reference.md | 71 +++ .../allow-extra-fields/snippet.json | 11 + .../resources/params/client/Client.ts | 68 +++ .../allow-extra-fields/src/core/exports.ts | 1 + .../src/core/file/exports.ts | 1 + .../allow-extra-fields/src/core/file/file.ts | 217 ++++++++ .../allow-extra-fields/src/core/file/index.ts | 2 + .../allow-extra-fields/src/core/file/types.ts | 81 +++ .../allow-extra-fields/src/core/index.ts | 1 + .../tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../tests/unit/test-file.txt | 1 + .../exhaustive/bigint-serde-layer/README.md | 46 ++ .../bigint-serde-layer/reference.md | 71 +++ .../bigint-serde-layer/snippet.json | 11 + .../resources/params/client/Client.ts | 74 +++ .../bigint-serde-layer/src/core/exports.ts | 1 + .../src/core/file/exports.ts | 1 + .../bigint-serde-layer/src/core/file/file.ts | 217 ++++++++ .../bigint-serde-layer/src/core/file/index.ts | 2 + .../bigint-serde-layer/src/core/file/types.ts | 81 +++ .../bigint-serde-layer/src/core/index.ts | 1 + .../tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../tests/unit/test-file.txt | 1 + seed/ts-sdk/exhaustive/bigint/README.md | 46 ++ seed/ts-sdk/exhaustive/bigint/reference.md | 71 +++ seed/ts-sdk/exhaustive/bigint/snippet.json | 11 + .../resources/params/client/Client.ts | 68 +++ .../exhaustive/bigint/src/core/exports.ts | 1 + .../bigint/src/core/file/exports.ts | 1 + .../exhaustive/bigint/src/core/file/file.ts | 217 ++++++++ .../exhaustive/bigint/src/core/file/index.ts | 2 + .../exhaustive/bigint/src/core/file/types.ts | 81 +++ .../exhaustive/bigint/src/core/index.ts | 1 + .../bigint/tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../bigint/tests/unit/test-file.txt | 1 + .../consolidate-type-files/README.md | 46 ++ .../consolidate-type-files/reference.md | 71 +++ .../consolidate-type-files/snippet.json | 11 + .../resources/params/client/Client.ts | 68 +++ .../src/core/exports.ts | 1 + .../src/core/file/exports.ts | 1 + .../src/core/file/file.ts | 217 ++++++++ .../src/core/file/index.ts | 2 + .../src/core/file/types.ts | 81 +++ .../consolidate-type-files/src/core/index.ts | 1 + .../tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../tests/unit/test-file.txt | 1 + .../export-all-requests-at-root/README.md | 46 ++ .../export-all-requests-at-root/reference.md | 71 +++ .../export-all-requests-at-root/snippet.json | 11 + .../resources/params/client/Client.ts | 68 +++ .../src/core/exports.ts | 1 + .../src/core/file/exports.ts | 1 + .../src/core/file/file.ts | 217 ++++++++ .../src/core/file/index.ts | 2 + .../src/core/file/types.ts | 81 +++ .../src/core/index.ts | 1 + .../tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../tests/unit/test-file.txt | 1 + .../resources/params/client/Client.d.ts | 13 + .../resources/params/client/Client.js | 50 ++ .../cjs/core/exports.d.ts | 1 + .../local-files-no-source/cjs/core/exports.js | 1 + .../cjs/core/file/exports.d.ts | 1 + .../cjs/core/file/exports.js | 2 + .../cjs/core/file/file.d.ts | 10 + .../cjs/core/file/file.js | 221 ++++++++ .../cjs/core/file/index.d.ts | 2 + .../cjs/core/file/index.js | 18 + .../cjs/core/file/types.d.ts | 66 +++ .../cjs/core/file/types.js | 2 + .../local-files-no-source/cjs/core/index.d.ts | 1 + .../local-files-no-source/cjs/core/index.js | 3 +- .../resources/params/client/Client.d.mts | 13 + .../resources/params/client/Client.mjs | 50 ++ .../esm/core/exports.d.mts | 1 + .../esm/core/exports.mjs | 1 + .../esm/core/file/exports.d.mts | 1 + .../esm/core/file/exports.mjs | 1 + .../esm/core/file/file.d.mts | 10 + .../esm/core/file/file.mjs | 184 +++++++ .../esm/core/file/index.d.mts | 2 + .../esm/core/file/index.mjs | 2 + .../esm/core/file/types.d.mts | 66 +++ .../esm/core/file/types.mjs | 1 + .../esm/core/index.d.mts | 1 + .../local-files-no-source/esm/core/index.mjs | 1 + .../local-files-no-source/snippet.json | 11 + .../resources/params/client/Client.ts | 68 +++ .../exhaustive/local-files/core/exports.ts | 1 + .../local-files/core/file/exports.ts | 1 + .../exhaustive/local-files/core/file/file.ts | 217 ++++++++ .../exhaustive/local-files/core/file/index.ts | 2 + .../exhaustive/local-files/core/file/types.ts | 81 +++ .../exhaustive/local-files/core/index.ts | 1 + .../exhaustive/local-files/snippet.json | 11 + .../exhaustive/multiple-exports/README.md | 46 ++ .../exhaustive/multiple-exports/reference.md | 71 +++ .../exhaustive/multiple-exports/snippet.json | 11 + .../resources/params/client/Client.ts | 68 +++ .../multiple-exports/src/core/exports.ts | 1 + .../multiple-exports/src/core/file/exports.ts | 1 + .../multiple-exports/src/core/file/file.ts | 217 ++++++++ .../multiple-exports/src/core/file/index.ts | 2 + .../multiple-exports/src/core/file/types.ts | 81 +++ .../multiple-exports/src/core/index.ts | 1 + .../tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../multiple-exports/tests/unit/test-file.txt | 1 + .../exhaustive/never-throw-errors/README.md | 46 ++ .../never-throw-errors/reference.md | 71 +++ .../never-throw-errors/snippet.json | 11 + .../resources/params/client/Client.ts | 84 +++ .../resources/params/client/index.ts | 1 + .../resources/params/client/uploadWithPath.ts | 36 ++ .../never-throw-errors/src/core/exports.ts | 1 + .../src/core/file/exports.ts | 1 + .../never-throw-errors/src/core/file/file.ts | 217 ++++++++ .../never-throw-errors/src/core/file/index.ts | 2 + .../never-throw-errors/src/core/file/types.ts | 81 +++ .../never-throw-errors/src/core/index.ts | 1 + .../tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../tests/unit/test-file.txt | 1 + .../exhaustive/no-custom-config/README.md | 46 ++ .../exhaustive/no-custom-config/reference.md | 71 +++ .../exhaustive/no-custom-config/snippet.json | 11 + .../resources/params/client/Client.ts | 68 +++ .../no-custom-config/src/core/exports.ts | 1 + .../no-custom-config/src/core/file/exports.ts | 1 + .../no-custom-config/src/core/file/file.ts | 217 ++++++++ .../no-custom-config/src/core/file/index.ts | 2 + .../no-custom-config/src/core/file/types.ts | 81 +++ .../no-custom-config/src/core/index.ts | 1 + .../tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../no-custom-config/tests/unit/test-file.txt | 1 + seed/ts-sdk/exhaustive/node-fetch/README.md | 46 ++ .../ts-sdk/exhaustive/node-fetch/reference.md | 71 +++ .../ts-sdk/exhaustive/node-fetch/snippet.json | 11 + .../resources/params/client/Client.ts | 68 +++ .../exhaustive/node-fetch/src/core/exports.ts | 1 + .../node-fetch/src/core/file/exports.ts | 1 + .../node-fetch/src/core/file/file.ts | 217 ++++++++ .../node-fetch/src/core/file/index.ts | 2 + .../node-fetch/src/core/file/types.ts | 81 +++ .../exhaustive/node-fetch/src/core/index.ts | 1 + .../node-fetch/tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../node-fetch/tests/unit/test-file.txt | 1 + .../resources/params/client/Client.ts | 68 +++ .../output-src-only/core/exports.ts | 1 + .../output-src-only/core/file/exports.ts | 1 + .../output-src-only/core/file/file.ts | 217 ++++++++ .../output-src-only/core/file/index.ts | 2 + .../output-src-only/core/file/types.ts | 81 +++ .../exhaustive/output-src-only/core/index.ts | 1 + .../exhaustive/output-src-only/snippet.json | 11 + seed/ts-sdk/exhaustive/package-path/README.md | 46 ++ .../exhaustive/package-path/reference.md | 71 +++ .../exhaustive/package-path/snippet.json | 11 + .../resources/params/client/Client.ts | 68 +++ .../src/test-packagePath/core/exports.ts | 1 + .../src/test-packagePath/core/file/exports.ts | 1 + .../src/test-packagePath/core/file/file.ts | 217 ++++++++ .../src/test-packagePath/core/file/index.ts | 2 + .../src/test-packagePath/core/file/types.ts | 81 +++ .../src/test-packagePath/core/index.ts | 1 + .../tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../test-packagePath/tests/unit/test-file.txt | 1 + .../parameter-naming-camel-case/README.md | 46 ++ .../parameter-naming-camel-case/reference.md | 71 +++ .../parameter-naming-camel-case/snippet.json | 11 + .../resources/params/client/Client.ts | 68 +++ .../src/core/exports.ts | 1 + .../src/core/file/exports.ts | 1 + .../src/core/file/file.ts | 217 ++++++++ .../src/core/file/index.ts | 2 + .../src/core/file/types.ts | 81 +++ .../src/core/index.ts | 1 + .../tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../tests/unit/test-file.txt | 1 + .../parameter-naming-original-name/README.md | 46 ++ .../reference.md | 71 +++ .../snippet.json | 11 + .../resources/params/client/Client.ts | 68 +++ .../src/core/exports.ts | 1 + .../src/core/file/exports.ts | 1 + .../src/core/file/file.ts | 217 ++++++++ .../src/core/file/index.ts | 2 + .../src/core/file/types.ts | 81 +++ .../src/core/index.ts | 1 + .../tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../tests/unit/test-file.txt | 1 + .../parameter-naming-snake-case/README.md | 46 ++ .../parameter-naming-snake-case/reference.md | 71 +++ .../parameter-naming-snake-case/snippet.json | 11 + .../resources/params/client/Client.ts | 68 +++ .../src/core/exports.ts | 1 + .../src/core/file/exports.ts | 1 + .../src/core/file/file.ts | 217 ++++++++ .../src/core/file/index.ts | 2 + .../src/core/file/types.ts | 81 +++ .../src/core/index.ts | 1 + .../tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../tests/unit/test-file.txt | 1 + .../parameter-naming-wire-value/README.md | 46 ++ .../parameter-naming-wire-value/reference.md | 71 +++ .../parameter-naming-wire-value/snippet.json | 11 + .../resources/params/client/Client.ts | 68 +++ .../src/core/exports.ts | 1 + .../src/core/file/exports.ts | 1 + .../src/core/file/file.ts | 217 ++++++++ .../src/core/file/index.ts | 2 + .../src/core/file/types.ts | 81 +++ .../src/core/index.ts | 1 + .../tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../tests/unit/test-file.txt | 1 + .../retain-original-casing/README.md | 46 ++ .../retain-original-casing/reference.md | 71 +++ .../retain-original-casing/snippet.json | 11 + .../resources/params/client/Client.ts | 68 +++ .../src/core/exports.ts | 1 + .../src/core/file/exports.ts | 1 + .../src/core/file/file.ts | 217 ++++++++ .../src/core/file/index.ts | 2 + .../src/core/file/types.ts | 81 +++ .../retain-original-casing/src/core/index.ts | 1 + .../tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../tests/unit/test-file.txt | 1 + seed/ts-sdk/exhaustive/serde-layer/README.md | 46 ++ .../exhaustive/serde-layer/reference.md | 71 +++ .../exhaustive/serde-layer/snippet.json | 11 + .../resources/params/client/Client.ts | 74 +++ .../serde-layer/src/core/exports.ts | 1 + .../serde-layer/src/core/file/exports.ts | 1 + .../serde-layer/src/core/file/file.ts | 217 ++++++++ .../serde-layer/src/core/file/index.ts | 2 + .../serde-layer/src/core/file/types.ts | 81 +++ .../exhaustive/serde-layer/src/core/index.ts | 1 + .../serde-layer/tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../serde-layer/tests/unit/test-file.txt | 1 + seed/ts-sdk/exhaustive/use-jest/README.md | 46 ++ seed/ts-sdk/exhaustive/use-jest/reference.md | 71 +++ seed/ts-sdk/exhaustive/use-jest/snippet.json | 11 + .../resources/params/client/Client.ts | 68 +++ .../exhaustive/use-jest/src/core/exports.ts | 1 + .../use-jest/src/core/file/exports.ts | 1 + .../exhaustive/use-jest/src/core/file/file.ts | 217 ++++++++ .../use-jest/src/core/file/index.ts | 2 + .../use-jest/src/core/file/types.ts | 81 +++ .../exhaustive/use-jest/src/core/index.ts | 1 + .../use-jest/tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../use-jest/tests/unit/test-file.txt | 1 + .../exhaustive/web-stream-wrapper/README.md | 46 ++ .../web-stream-wrapper/reference.md | 71 +++ .../web-stream-wrapper/snippet.json | 11 + .../resources/params/client/Client.ts | 68 +++ .../web-stream-wrapper/src/core/exports.ts | 1 + .../src/core/file/exports.ts | 1 + .../web-stream-wrapper/src/core/file/file.ts | 217 ++++++++ .../web-stream-wrapper/src/core/file/index.ts | 2 + .../web-stream-wrapper/src/core/file/types.ts | 81 +++ .../web-stream-wrapper/src/core/index.ts | 1 + .../tests/unit/file/file.test.ts | 499 ++++++++++++++++++ .../tests/unit/test-file.txt | 1 + 263 files changed, 19546 insertions(+), 1 deletion(-) create mode 100644 seed/ts-sdk/exhaustive/allow-extra-fields/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/allow-extra-fields/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/allow-extra-fields/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/allow-extra-fields/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/allow-extra-fields/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/allow-extra-fields/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/bigint-serde-layer/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/bigint-serde-layer/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/bigint/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/bigint/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/bigint/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/bigint/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/bigint/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/bigint/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/consolidate-type-files/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/consolidate-type-files/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/consolidate-type-files/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/consolidate-type-files/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/consolidate-type-files/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/consolidate-type-files/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/export-all-requests-at-root/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/export-all-requests-at-root/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/exports.d.ts create mode 100644 seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/exports.js create mode 100644 seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/file.d.ts create mode 100644 seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/file.js create mode 100644 seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/index.d.ts create mode 100644 seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/index.js create mode 100644 seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/types.d.ts create mode 100644 seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/types.js create mode 100644 seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/exports.d.mts create mode 100644 seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/exports.mjs create mode 100644 seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/file.d.mts create mode 100644 seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/file.mjs create mode 100644 seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/index.d.mts create mode 100644 seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/index.mjs create mode 100644 seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/types.d.mts create mode 100644 seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/types.mjs create mode 100644 seed/ts-sdk/exhaustive/local-files/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/local-files/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/local-files/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/local-files/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/multiple-exports/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/multiple-exports/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/multiple-exports/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/multiple-exports/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/multiple-exports/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/multiple-exports/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/never-throw-errors/src/api/resources/endpoints/resources/params/client/uploadWithPath.ts create mode 100644 seed/ts-sdk/exhaustive/never-throw-errors/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/never-throw-errors/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/never-throw-errors/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/never-throw-errors/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/never-throw-errors/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/never-throw-errors/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/no-custom-config/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/no-custom-config/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/no-custom-config/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/no-custom-config/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/no-custom-config/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/no-custom-config/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/node-fetch/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/node-fetch/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/node-fetch/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/node-fetch/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/node-fetch/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/node-fetch/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/output-src-only/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/output-src-only/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/output-src-only/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/output-src-only/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/package-path/src/test-packagePath/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/package-path/src/test-packagePath/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-camel-case/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-camel-case/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-original-name/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-original-name/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-snake-case/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-snake-case/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-wire-value/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/parameter-naming-wire-value/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/retain-original-casing/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/retain-original-casing/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/retain-original-casing/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/retain-original-casing/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/retain-original-casing/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/retain-original-casing/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/serde-layer/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/serde-layer/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/serde-layer/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/serde-layer/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/serde-layer/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/serde-layer/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/use-jest/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/use-jest/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/use-jest/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/use-jest/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/use-jest/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/use-jest/tests/unit/test-file.txt create mode 100644 seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/file/exports.ts create mode 100644 seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/file/file.ts create mode 100644 seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/file/index.ts create mode 100644 seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/file/types.ts create mode 100644 seed/ts-sdk/exhaustive/web-stream-wrapper/tests/unit/file/file.test.ts create mode 100644 seed/ts-sdk/exhaustive/web-stream-wrapper/tests/unit/test-file.txt diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/README.md b/seed/ts-sdk/exhaustive/allow-extra-fields/README.md index 13d52ef3b543..5858447844da 100644 --- a/seed/ts-sdk/exhaustive/allow-extra-fields/README.md +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/reference.md b/seed/ts-sdk/exhaustive/allow-extra-fields/reference.md index 8cdfc0637d93..561bac944746 100644 --- a/seed/ts-sdk/exhaustive/allow-extra-fields/reference.md +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/snippet.json b/seed/ts-sdk/exhaustive/allow-extra-fields/snippet.json index 5f934395b81e..0ac6dbe4b39c 100644 --- a/seed/ts-sdk/exhaustive/allow-extra-fields/snippet.json +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/params/client/Client.ts index 0dd63e84b8cb..26042aa010f7 100644 --- a/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/src/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/exports.ts b/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/file/file.ts b/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/file/index.ts b/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/file/types.ts b/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/index.ts b/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/allow-extra-fields/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6cd6509059b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/allow-extra-fields/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/allow-extra-fields/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/allow-extra-fields/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/bigint-serde-layer/README.md b/seed/ts-sdk/exhaustive/bigint-serde-layer/README.md index 13d52ef3b543..5858447844da 100644 --- a/seed/ts-sdk/exhaustive/bigint-serde-layer/README.md +++ b/seed/ts-sdk/exhaustive/bigint-serde-layer/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/bigint-serde-layer/reference.md b/seed/ts-sdk/exhaustive/bigint-serde-layer/reference.md index 4ce2bc3ec2d2..55b9efb46bcd 100644 --- a/seed/ts-sdk/exhaustive/bigint-serde-layer/reference.md +++ b/seed/ts-sdk/exhaustive/bigint-serde-layer/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/bigint-serde-layer/snippet.json b/seed/ts-sdk/exhaustive/bigint-serde-layer/snippet.json index fa5278507c48..bd67211cb6ec 100644 --- a/seed/ts-sdk/exhaustive/bigint-serde-layer/snippet.json +++ b/seed/ts-sdk/exhaustive/bigint-serde-layer/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/bigint-serde-layer/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/bigint-serde-layer/src/api/resources/endpoints/resources/params/client/Client.ts index 56bd66fcd23d..574b1f38fc7d 100644 --- a/seed/ts-sdk/exhaustive/bigint-serde-layer/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/bigint-serde-layer/src/api/resources/endpoints/resources/params/client/Client.ts @@ -556,4 +556,78 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: serializers.types.ObjectWithRequiredField.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/exports.ts b/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/file/file.ts b/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/file/index.ts b/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/file/types.ts b/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/index.ts b/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/index.ts index 70de501127aa..c8027da6c883 100644 --- a/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/bigint-serde-layer/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/bigint-serde-layer/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/bigint-serde-layer/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6acb74de1918 --- /dev/null +++ b/seed/ts-sdk/exhaustive/bigint-serde-layer/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/bigint-serde-layer/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/bigint-serde-layer/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/bigint-serde-layer/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/bigint/README.md b/seed/ts-sdk/exhaustive/bigint/README.md index 13d52ef3b543..5858447844da 100644 --- a/seed/ts-sdk/exhaustive/bigint/README.md +++ b/seed/ts-sdk/exhaustive/bigint/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/bigint/reference.md b/seed/ts-sdk/exhaustive/bigint/reference.md index f62e2ab58067..31cdc7295347 100644 --- a/seed/ts-sdk/exhaustive/bigint/reference.md +++ b/seed/ts-sdk/exhaustive/bigint/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/bigint/snippet.json b/seed/ts-sdk/exhaustive/bigint/snippet.json index 1d4c4545ff0a..461de9ab42b7 100644 --- a/seed/ts-sdk/exhaustive/bigint/snippet.json +++ b/seed/ts-sdk/exhaustive/bigint/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/params/client/Client.ts index 0dd63e84b8cb..26042aa010f7 100644 --- a/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/bigint/src/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/bigint/src/core/exports.ts b/seed/ts-sdk/exhaustive/bigint/src/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/bigint/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/bigint/src/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/bigint/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/bigint/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/bigint/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/bigint/src/core/file/file.ts b/seed/ts-sdk/exhaustive/bigint/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/bigint/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/bigint/src/core/file/index.ts b/seed/ts-sdk/exhaustive/bigint/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/bigint/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/bigint/src/core/file/types.ts b/seed/ts-sdk/exhaustive/bigint/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/bigint/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/bigint/src/core/index.ts b/seed/ts-sdk/exhaustive/bigint/src/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/bigint/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/bigint/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/bigint/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/bigint/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6acb74de1918 --- /dev/null +++ b/seed/ts-sdk/exhaustive/bigint/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/bigint/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/bigint/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/bigint/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/consolidate-type-files/README.md b/seed/ts-sdk/exhaustive/consolidate-type-files/README.md index 13d52ef3b543..5858447844da 100644 --- a/seed/ts-sdk/exhaustive/consolidate-type-files/README.md +++ b/seed/ts-sdk/exhaustive/consolidate-type-files/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/consolidate-type-files/reference.md b/seed/ts-sdk/exhaustive/consolidate-type-files/reference.md index 8cdfc0637d93..561bac944746 100644 --- a/seed/ts-sdk/exhaustive/consolidate-type-files/reference.md +++ b/seed/ts-sdk/exhaustive/consolidate-type-files/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/consolidate-type-files/snippet.json b/seed/ts-sdk/exhaustive/consolidate-type-files/snippet.json index 5f934395b81e..0ac6dbe4b39c 100644 --- a/seed/ts-sdk/exhaustive/consolidate-type-files/snippet.json +++ b/seed/ts-sdk/exhaustive/consolidate-type-files/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/consolidate-type-files/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/consolidate-type-files/src/api/resources/endpoints/resources/params/client/Client.ts index 0dd63e84b8cb..26042aa010f7 100644 --- a/seed/ts-sdk/exhaustive/consolidate-type-files/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/consolidate-type-files/src/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/exports.ts b/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/file/file.ts b/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/file/index.ts b/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/file/types.ts b/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/index.ts b/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/consolidate-type-files/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/consolidate-type-files/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/consolidate-type-files/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6cd6509059b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/consolidate-type-files/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/consolidate-type-files/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/consolidate-type-files/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/consolidate-type-files/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/export-all-requests-at-root/README.md b/seed/ts-sdk/exhaustive/export-all-requests-at-root/README.md index 13d52ef3b543..5858447844da 100644 --- a/seed/ts-sdk/exhaustive/export-all-requests-at-root/README.md +++ b/seed/ts-sdk/exhaustive/export-all-requests-at-root/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/export-all-requests-at-root/reference.md b/seed/ts-sdk/exhaustive/export-all-requests-at-root/reference.md index d325c6ee8b7f..e9bc56f537bf 100644 --- a/seed/ts-sdk/exhaustive/export-all-requests-at-root/reference.md +++ b/seed/ts-sdk/exhaustive/export-all-requests-at-root/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/export-all-requests-at-root/snippet.json b/seed/ts-sdk/exhaustive/export-all-requests-at-root/snippet.json index 5f934395b81e..0ac6dbe4b39c 100644 --- a/seed/ts-sdk/exhaustive/export-all-requests-at-root/snippet.json +++ b/seed/ts-sdk/exhaustive/export-all-requests-at-root/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/api/resources/endpoints/resources/params/client/Client.ts index 61fb02807e2d..2696d6e9a0ef 100644 --- a/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/exports.ts b/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/file/file.ts b/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/file/index.ts b/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/file/types.ts b/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/index.ts b/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/export-all-requests-at-root/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/export-all-requests-at-root/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/export-all-requests-at-root/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6cd6509059b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/export-all-requests-at-root/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/export-all-requests-at-root/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/export-all-requests-at-root/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/export-all-requests-at-root/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/api/resources/endpoints/resources/params/client/Client.d.ts b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/api/resources/endpoints/resources/params/client/Client.d.ts index 189f695d0e0f..dae805cd3713 100644 --- a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/api/resources/endpoints/resources/params/client/Client.d.ts +++ b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/api/resources/endpoints/resources/params/client/Client.d.ts @@ -116,4 +116,17 @@ export declare class ParamsClient { */ modifyWithInlinePath(request: SeedExhaustive.endpoints.ModifyResourceAtInlinedPath, requestOptions?: ParamsClient.RequestOptions): core.HttpResponsePromise; private __modifyWithInlinePath; + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + uploadWithPath(uploadable: core.file.Uploadable, param: string, requestOptions?: ParamsClient.RequestOptions): core.HttpResponsePromise; + private __uploadWithPath; } diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/api/resources/endpoints/resources/params/client/Client.js b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/api/resources/endpoints/resources/params/client/Client.js index 05d62db05fa0..1f77e121513f 100644 --- a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/api/resources/endpoints/resources/params/client/Client.js +++ b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/api/resources/endpoints/resources/params/client/Client.js @@ -425,5 +425,55 @@ class ParamsClient { return (0, handleNonStatusCodeError_js_1.handleNonStatusCodeError)(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); }); } + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + uploadWithPath(uploadable, param, requestOptions) { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + __uploadWithPath(uploadable, param, requestOptions) { + return __awaiter(this, void 0, void 0, function* () { + var _a, _b, _c, _d, _e, _f, _g, _h; + const _binaryUploadRequest = yield core.file.toBinaryUploadRequest(uploadable); + const _authRequest = yield this._options.authProvider.getAuthRequest(); + const _headers = (0, headers_js_1.mergeHeaders)(_authRequest.headers, (_a = this._options) === null || _a === void 0 ? void 0 : _a.headers, _binaryUploadRequest.headers, requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.headers); + const _response = yield core.fetcher({ + url: core.url.join((_b = (yield core.Supplier.get(this._options.baseUrl))) !== null && _b !== void 0 ? _b : (yield core.Supplier.get(this._options.environment)), `/params/path/${core.url.encodePathParam(param)}`), + method: "POST", + headers: _headers, + queryParameters: requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: ((_e = (_c = requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.timeoutInSeconds) !== null && _c !== void 0 ? _c : (_d = this._options) === null || _d === void 0 ? void 0 : _d.timeoutInSeconds) !== null && _e !== void 0 ? _e : 60) * 1000, + maxRetries: (_f = requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.maxRetries) !== null && _f !== void 0 ? _f : (_g = this._options) === null || _g === void 0 ? void 0 : _g.maxRetries, + abortSignal: requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.abortSignal, + fetchFn: (_h = this._options) === null || _h === void 0 ? void 0 : _h.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body, + rawResponse: _response.rawResponse, + }; + } + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + return (0, handleNonStatusCodeError_js_1.handleNonStatusCodeError)(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + }); + } } exports.ParamsClient = ParamsClient; diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/exports.d.ts b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/exports.d.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/exports.d.ts +++ b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/exports.d.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/exports.js b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/exports.js index d0522f6dc704..0018c0331231 100644 --- a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/exports.js +++ b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/exports.js @@ -14,5 +14,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./file/exports.js"), exports); __exportStar(require("./logging/exports.js"), exports); __exportStar(require("./pagination/exports.js"), exports); diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/exports.d.ts b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/exports.d.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/exports.d.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/exports.js b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/exports.js new file mode 100644 index 000000000000..c8ad2e549bdc --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/exports.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/file.d.ts b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/file.d.ts new file mode 100644 index 000000000000..0f1be8aac96e --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/file.d.ts @@ -0,0 +1,10 @@ +import type { Uploadable } from "./types.js"; +export declare function toBinaryUploadRequest(file: Uploadable): Promise<{ + body: Uploadable.FileLike; + headers?: Record; +}>; +export declare function toMultipartDataPart(file: Uploadable): Promise<{ + data: Uploadable.FileLike; + filename?: string; + contentType?: string; +}>; diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/file.js b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/file.js new file mode 100644 index 000000000000..b36a0732b7fe --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/file.js @@ -0,0 +1,221 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.toBinaryUploadRequest = toBinaryUploadRequest; +exports.toMultipartDataPart = toMultipartDataPart; +function toBinaryUploadRequest(file) { + return __awaiter(this, void 0, void 0, function* () { + const { data, filename, contentLength, contentType } = yield getFileWithMetadata(file); + const request = { + body: data, + headers: {}, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; + }); +} +function toMultipartDataPart(file) { + return __awaiter(this, void 0, void 0, function* () { + const { data, filename, contentType } = yield getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; + }); +} +function getFileWithMetadata(file_1) { + return __awaiter(this, arguments, void 0, function* (file, { noSniffFileSize } = {}) { + var _a, _b, _c, _d, _e; + if (isFileLike(file)) { + return getFileWithMetadata({ + data: file, + }, { noSniffFileSize }); + } + if ("path" in file) { + const fs = yield Promise.resolve().then(() => __importStar(require("fs"))); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = (_a = file.contentLength) !== null && _a !== void 0 ? _a : (noSniffFileSize === true ? undefined : yield tryGetFileSizeFromPath(file.path)); + const filename = (_b = file.filename) !== null && _b !== void 0 ? _b : getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = (_c = file.contentLength) !== null && _c !== void 0 ? _c : (yield tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = (_d = file.filename) !== null && _d !== void 0 ? _d : tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: (_e = file.contentType) !== null && _e !== void 0 ? _e : tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); + }); +} +function isFileLike(value) { + return (isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value)); +} +function tryGetFileSizeFromPath(path) { + return __awaiter(this, void 0, void 0, function* () { + try { + const fs = yield Promise.resolve().then(() => __importStar(require("fs"))); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = yield fs.promises.stat(path); + return fileStat.size; + } + catch (_fallbackError) { + return undefined; + } + }); +} +function tryGetNameFromFileLike(data) { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} +function tryGetContentLengthFromFileLike(data_1) { + return __awaiter(this, arguments, void 0, function* (data, { noSniffFileSize } = {}) { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return yield tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; + }); +} +function tryGetContentTypeFromFileLike(data) { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + return undefined; +} +function getNameFromPath(path) { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} +function isNamedValue(value) { + return typeof value === "object" && value != null && "name" in value; +} +function isPathedValue(value) { + return typeof value === "object" && value != null && "path" in value; +} +function isStreamLike(value) { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} +function isReadableStream(value) { + return typeof value === "object" && value != null && "getReader" in value; +} +function isBuffer(value) { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} +function isArrayBufferView(value) { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} +function isArrayBuffer(value) { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} +function isUint8Array(value) { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} +function isBlob(value) { + return typeof Blob !== "undefined" && value instanceof Blob; +} +function isFile(value) { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/index.d.ts b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/index.d.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/index.d.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/index.js b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/index.js new file mode 100644 index 000000000000..5b149cf90626 --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/index.js @@ -0,0 +1,18 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./file.js"), exports); +__exportStar(require("./types.js"), exports); diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/types.d.ts b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/types.d.ts new file mode 100644 index 000000000000..7826cad13cdb --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/types.d.ts @@ -0,0 +1,66 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; +export declare namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + type FileLike = ArrayBuffer | ArrayBufferLike | ArrayBufferView | Uint8Array | import("buffer").Buffer | import("buffer").Blob | import("buffer").File | import("stream").Readable | import("stream/web").ReadableStream | globalThis.Blob | globalThis.File | ReadableStream; + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + /** + * A file-like object with metadata, used for uploading files. + */ + type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/types.js b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/types.js new file mode 100644 index 000000000000..c8ad2e549bdc --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/file/types.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/index.d.ts b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/index.d.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/index.d.ts +++ b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/index.d.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/index.js b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/index.js index e73df0548bec..d11834508eb2 100644 --- a/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/index.js +++ b/seed/ts-sdk/exhaustive/local-files-no-source/cjs/core/index.js @@ -36,10 +36,11 @@ var __importStar = (this && this.__importStar) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -exports.url = exports.logging = void 0; +exports.url = exports.logging = exports.file = void 0; __exportStar(require("./auth/index.js"), exports); __exportStar(require("./base64.js"), exports); __exportStar(require("./fetcher/index.js"), exports); +exports.file = __importStar(require("./file/index.js")); exports.logging = __importStar(require("./logging/index.js")); __exportStar(require("./pagination/index.js"), exports); __exportStar(require("./runtime/index.js"), exports); diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/esm/api/resources/endpoints/resources/params/client/Client.d.mts b/seed/ts-sdk/exhaustive/local-files-no-source/esm/api/resources/endpoints/resources/params/client/Client.d.mts index e8a3f1eaaa72..99ce405ebe4d 100644 --- a/seed/ts-sdk/exhaustive/local-files-no-source/esm/api/resources/endpoints/resources/params/client/Client.d.mts +++ b/seed/ts-sdk/exhaustive/local-files-no-source/esm/api/resources/endpoints/resources/params/client/Client.d.mts @@ -116,4 +116,17 @@ export declare class ParamsClient { */ modifyWithInlinePath(request: SeedExhaustive.endpoints.ModifyResourceAtInlinedPath, requestOptions?: ParamsClient.RequestOptions): core.HttpResponsePromise; private __modifyWithInlinePath; + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + uploadWithPath(uploadable: core.file.Uploadable, param: string, requestOptions?: ParamsClient.RequestOptions): core.HttpResponsePromise; + private __uploadWithPath; } diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/esm/api/resources/endpoints/resources/params/client/Client.mjs b/seed/ts-sdk/exhaustive/local-files-no-source/esm/api/resources/endpoints/resources/params/client/Client.mjs index 98a161ae6a30..0dfe4c46f01f 100644 --- a/seed/ts-sdk/exhaustive/local-files-no-source/esm/api/resources/endpoints/resources/params/client/Client.mjs +++ b/seed/ts-sdk/exhaustive/local-files-no-source/esm/api/resources/endpoints/resources/params/client/Client.mjs @@ -389,4 +389,54 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); }); } + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + uploadWithPath(uploadable, param, requestOptions) { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + __uploadWithPath(uploadable, param, requestOptions) { + return __awaiter(this, void 0, void 0, function* () { + var _a, _b, _c, _d, _e, _f, _g, _h; + const _binaryUploadRequest = yield core.file.toBinaryUploadRequest(uploadable); + const _authRequest = yield this._options.authProvider.getAuthRequest(); + const _headers = mergeHeaders(_authRequest.headers, (_a = this._options) === null || _a === void 0 ? void 0 : _a.headers, _binaryUploadRequest.headers, requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.headers); + const _response = yield core.fetcher({ + url: core.url.join((_b = (yield core.Supplier.get(this._options.baseUrl))) !== null && _b !== void 0 ? _b : (yield core.Supplier.get(this._options.environment)), `/params/path/${core.url.encodePathParam(param)}`), + method: "POST", + headers: _headers, + queryParameters: requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: ((_e = (_c = requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.timeoutInSeconds) !== null && _c !== void 0 ? _c : (_d = this._options) === null || _d === void 0 ? void 0 : _d.timeoutInSeconds) !== null && _e !== void 0 ? _e : 60) * 1000, + maxRetries: (_f = requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.maxRetries) !== null && _f !== void 0 ? _f : (_g = this._options) === null || _g === void 0 ? void 0 : _g.maxRetries, + abortSignal: requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.abortSignal, + fetchFn: (_h = this._options) === null || _h === void 0 ? void 0 : _h.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body, + rawResponse: _response.rawResponse, + }; + } + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + }); + } } diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/exports.d.mts b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/exports.d.mts index 089ad6d89e5c..a35f68056e3c 100644 --- a/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/exports.d.mts +++ b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/exports.d.mts @@ -1,2 +1,3 @@ +export * from "./file/exports.mjs"; export * from "./logging/exports.mjs"; export * from "./pagination/exports.mjs"; diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/exports.mjs b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/exports.mjs index 089ad6d89e5c..a35f68056e3c 100644 --- a/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/exports.mjs +++ b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/exports.mjs @@ -1,2 +1,3 @@ +export * from "./file/exports.mjs"; export * from "./logging/exports.mjs"; export * from "./pagination/exports.mjs"; diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/exports.d.mts b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/exports.d.mts new file mode 100644 index 000000000000..bb9c17c6b064 --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/exports.d.mts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.mjs"; diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/exports.mjs b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/exports.mjs new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/exports.mjs @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/file.d.mts b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/file.d.mts new file mode 100644 index 000000000000..c00327cc8ca1 --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/file.d.mts @@ -0,0 +1,10 @@ +import type { Uploadable } from "./types.mjs"; +export declare function toBinaryUploadRequest(file: Uploadable): Promise<{ + body: Uploadable.FileLike; + headers?: Record; +}>; +export declare function toMultipartDataPart(file: Uploadable): Promise<{ + data: Uploadable.FileLike; + filename?: string; + contentType?: string; +}>; diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/file.mjs b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/file.mjs new file mode 100644 index 000000000000..0ccd178c5394 --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/file.mjs @@ -0,0 +1,184 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +export function toBinaryUploadRequest(file) { + return __awaiter(this, void 0, void 0, function* () { + const { data, filename, contentLength, contentType } = yield getFileWithMetadata(file); + const request = { + body: data, + headers: {}, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; + }); +} +export function toMultipartDataPart(file) { + return __awaiter(this, void 0, void 0, function* () { + const { data, filename, contentType } = yield getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; + }); +} +function getFileWithMetadata(file_1) { + return __awaiter(this, arguments, void 0, function* (file, { noSniffFileSize } = {}) { + var _a, _b, _c, _d, _e; + if (isFileLike(file)) { + return getFileWithMetadata({ + data: file, + }, { noSniffFileSize }); + } + if ("path" in file) { + const fs = yield import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = (_a = file.contentLength) !== null && _a !== void 0 ? _a : (noSniffFileSize === true ? undefined : yield tryGetFileSizeFromPath(file.path)); + const filename = (_b = file.filename) !== null && _b !== void 0 ? _b : getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = (_c = file.contentLength) !== null && _c !== void 0 ? _c : (yield tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = (_d = file.filename) !== null && _d !== void 0 ? _d : tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: (_e = file.contentType) !== null && _e !== void 0 ? _e : tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); + }); +} +function isFileLike(value) { + return (isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value)); +} +function tryGetFileSizeFromPath(path) { + return __awaiter(this, void 0, void 0, function* () { + try { + const fs = yield import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = yield fs.promises.stat(path); + return fileStat.size; + } + catch (_fallbackError) { + return undefined; + } + }); +} +function tryGetNameFromFileLike(data) { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} +function tryGetContentLengthFromFileLike(data_1) { + return __awaiter(this, arguments, void 0, function* (data, { noSniffFileSize } = {}) { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return yield tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; + }); +} +function tryGetContentTypeFromFileLike(data) { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + return undefined; +} +function getNameFromPath(path) { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} +function isNamedValue(value) { + return typeof value === "object" && value != null && "name" in value; +} +function isPathedValue(value) { + return typeof value === "object" && value != null && "path" in value; +} +function isStreamLike(value) { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} +function isReadableStream(value) { + return typeof value === "object" && value != null && "getReader" in value; +} +function isBuffer(value) { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} +function isArrayBufferView(value) { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} +function isArrayBuffer(value) { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} +function isUint8Array(value) { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} +function isBlob(value) { + return typeof Blob !== "undefined" && value instanceof Blob; +} +function isFile(value) { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/index.d.mts b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/index.d.mts new file mode 100644 index 000000000000..9be1bfb539fd --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/index.d.mts @@ -0,0 +1,2 @@ +export * from "./file.mjs"; +export * from "./types.mjs"; diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/index.mjs b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/index.mjs new file mode 100644 index 000000000000..9be1bfb539fd --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/index.mjs @@ -0,0 +1,2 @@ +export * from "./file.mjs"; +export * from "./types.mjs"; diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/types.d.mts b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/types.d.mts new file mode 100644 index 000000000000..7826cad13cdb --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/types.d.mts @@ -0,0 +1,66 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; +export declare namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + type FileLike = ArrayBuffer | ArrayBufferLike | ArrayBufferView | Uint8Array | import("buffer").Buffer | import("buffer").Blob | import("buffer").File | import("stream").Readable | import("stream/web").ReadableStream | globalThis.Blob | globalThis.File | ReadableStream; + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + /** + * A file-like object with metadata, used for uploading files. + */ + type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/types.mjs b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/types.mjs new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/file/types.mjs @@ -0,0 +1 @@ +export {}; diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/index.d.mts b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/index.d.mts index 18f30c659858..f4e6ee38cfa8 100644 --- a/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/index.d.mts +++ b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/index.d.mts @@ -1,6 +1,7 @@ export * from "./auth/index.mjs"; export * from "./base64.mjs"; export * from "./fetcher/index.mjs"; +export * as file from "./file/index.mjs"; export * as logging from "./logging/index.mjs"; export * from "./pagination/index.mjs"; export * from "./runtime/index.mjs"; diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/index.mjs b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/index.mjs index 18f30c659858..f4e6ee38cfa8 100644 --- a/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/index.mjs +++ b/seed/ts-sdk/exhaustive/local-files-no-source/esm/core/index.mjs @@ -1,6 +1,7 @@ export * from "./auth/index.mjs"; export * from "./base64.mjs"; export * from "./fetcher/index.mjs"; +export * as file from "./file/index.mjs"; export * as logging from "./logging/index.mjs"; export * from "./pagination/index.mjs"; export * from "./runtime/index.mjs"; diff --git a/seed/ts-sdk/exhaustive/local-files-no-source/snippet.json b/seed/ts-sdk/exhaustive/local-files-no-source/snippet.json index 72c45bc945b1..641835eb3d15 100644 --- a/seed/ts-sdk/exhaustive/local-files-no-source/snippet.json +++ b/seed/ts-sdk/exhaustive/local-files-no-source/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"./src/Client\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"./src/Client\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/local-files/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/local-files/api/resources/endpoints/resources/params/client/Client.ts index 0dd63e84b8cb..26042aa010f7 100644 --- a/seed/ts-sdk/exhaustive/local-files/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/local-files/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/local-files/core/exports.ts b/seed/ts-sdk/exhaustive/local-files/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/local-files/core/exports.ts +++ b/seed/ts-sdk/exhaustive/local-files/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/local-files/core/file/exports.ts b/seed/ts-sdk/exhaustive/local-files/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/local-files/core/file/file.ts b/seed/ts-sdk/exhaustive/local-files/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/local-files/core/file/index.ts b/seed/ts-sdk/exhaustive/local-files/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/local-files/core/file/types.ts b/seed/ts-sdk/exhaustive/local-files/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/local-files/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/local-files/core/index.ts b/seed/ts-sdk/exhaustive/local-files/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/local-files/core/index.ts +++ b/seed/ts-sdk/exhaustive/local-files/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/local-files/snippet.json b/seed/ts-sdk/exhaustive/local-files/snippet.json index 72c45bc945b1..641835eb3d15 100644 --- a/seed/ts-sdk/exhaustive/local-files/snippet.json +++ b/seed/ts-sdk/exhaustive/local-files/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"./src/Client\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"./src/Client\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/multiple-exports/README.md b/seed/ts-sdk/exhaustive/multiple-exports/README.md index ebf053d5f13c..cb7982cff710 100644 --- a/seed/ts-sdk/exhaustive/multiple-exports/README.md +++ b/seed/ts-sdk/exhaustive/multiple-exports/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Subpackage Exports](#subpackage-exports) @@ -79,6 +80,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/multiple-exports/reference.md b/seed/ts-sdk/exhaustive/multiple-exports/reference.md index 8cdfc0637d93..561bac944746 100644 --- a/seed/ts-sdk/exhaustive/multiple-exports/reference.md +++ b/seed/ts-sdk/exhaustive/multiple-exports/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/multiple-exports/snippet.json b/seed/ts-sdk/exhaustive/multiple-exports/snippet.json index 5f934395b81e..0ac6dbe4b39c 100644 --- a/seed/ts-sdk/exhaustive/multiple-exports/snippet.json +++ b/seed/ts-sdk/exhaustive/multiple-exports/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/multiple-exports/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/multiple-exports/src/api/resources/endpoints/resources/params/client/Client.ts index 0dd63e84b8cb..26042aa010f7 100644 --- a/seed/ts-sdk/exhaustive/multiple-exports/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/multiple-exports/src/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/multiple-exports/src/core/exports.ts b/seed/ts-sdk/exhaustive/multiple-exports/src/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/multiple-exports/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/multiple-exports/src/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/multiple-exports/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/multiple-exports/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/multiple-exports/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/multiple-exports/src/core/file/file.ts b/seed/ts-sdk/exhaustive/multiple-exports/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/multiple-exports/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/multiple-exports/src/core/file/index.ts b/seed/ts-sdk/exhaustive/multiple-exports/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/multiple-exports/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/multiple-exports/src/core/file/types.ts b/seed/ts-sdk/exhaustive/multiple-exports/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/multiple-exports/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/multiple-exports/src/core/index.ts b/seed/ts-sdk/exhaustive/multiple-exports/src/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/multiple-exports/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/multiple-exports/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/multiple-exports/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/multiple-exports/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6cd6509059b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/multiple-exports/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/multiple-exports/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/multiple-exports/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/multiple-exports/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/never-throw-errors/README.md b/seed/ts-sdk/exhaustive/never-throw-errors/README.md index 13d52ef3b543..5858447844da 100644 --- a/seed/ts-sdk/exhaustive/never-throw-errors/README.md +++ b/seed/ts-sdk/exhaustive/never-throw-errors/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/never-throw-errors/reference.md b/seed/ts-sdk/exhaustive/never-throw-errors/reference.md index 4002ff0cac87..09df84dfc070 100644 --- a/seed/ts-sdk/exhaustive/never-throw-errors/reference.md +++ b/seed/ts-sdk/exhaustive/never-throw-errors/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> core.APIResponse<SeedExhaustive.ObjectWithRequiredField, SeedExhaustive.endpoints.params.uploadWithPath.Error> +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/never-throw-errors/snippet.json b/seed/ts-sdk/exhaustive/never-throw-errors/snippet.json index 5f934395b81e..0ac6dbe4b39c 100644 --- a/seed/ts-sdk/exhaustive/never-throw-errors/snippet.json +++ b/seed/ts-sdk/exhaustive/never-throw-errors/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/never-throw-errors/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/never-throw-errors/src/api/resources/endpoints/resources/params/client/Client.ts index 7987d4d99214..78d94134e2be 100644 --- a/seed/ts-sdk/exhaustive/never-throw-errors/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/never-throw-errors/src/api/resources/endpoints/resources/params/client/Client.ts @@ -584,4 +584,88 @@ export class ParamsClient { rawResponse: _response.rawResponse, }; } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise< + core.APIResponse< + SeedExhaustive.types.ObjectWithRequiredField, + SeedExhaustive.endpoints.params.uploadWithPath.Error + > + > { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise< + core.WithRawResponse< + core.APIResponse< + SeedExhaustive.types.ObjectWithRequiredField, + SeedExhaustive.endpoints.params.uploadWithPath.Error + > + > + > { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: { + ok: true, + body: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + headers: _response.headers, + rawResponse: _response.rawResponse, + }, + rawResponse: _response.rawResponse, + }; + } + + return { + data: { + ok: false, + error: SeedExhaustive.endpoints.params.uploadWithPath.Error._unknown(_response.error), + rawResponse: _response.rawResponse, + }, + rawResponse: _response.rawResponse, + }; + } } diff --git a/seed/ts-sdk/exhaustive/never-throw-errors/src/api/resources/endpoints/resources/params/client/index.ts b/seed/ts-sdk/exhaustive/never-throw-errors/src/api/resources/endpoints/resources/params/client/index.ts index bddffe45311d..f785941ba084 100644 --- a/seed/ts-sdk/exhaustive/never-throw-errors/src/api/resources/endpoints/resources/params/client/index.ts +++ b/seed/ts-sdk/exhaustive/never-throw-errors/src/api/resources/endpoints/resources/params/client/index.ts @@ -7,3 +7,4 @@ export * as getWithQuery from "./getWithQuery.js"; export * as modifyWithInlinePath from "./modifyWithInlinePath.js"; export * as modifyWithPath from "./modifyWithPath.js"; export * from "./requests/index.js"; +export * as uploadWithPath from "./uploadWithPath.js"; diff --git a/seed/ts-sdk/exhaustive/never-throw-errors/src/api/resources/endpoints/resources/params/client/uploadWithPath.ts b/seed/ts-sdk/exhaustive/never-throw-errors/src/api/resources/endpoints/resources/params/client/uploadWithPath.ts new file mode 100644 index 000000000000..b3f0085c7899 --- /dev/null +++ b/seed/ts-sdk/exhaustive/never-throw-errors/src/api/resources/endpoints/resources/params/client/uploadWithPath.ts @@ -0,0 +1,36 @@ +// This file was auto-generated by Fern from our API Definition. + +import type * as core from "../../../../../../core/index.js"; +import type * as SeedExhaustive from "../../../../../index.js"; + +export type Error = SeedExhaustive.endpoints.params.uploadWithPath.Error._Unknown; + +export namespace Error { + export interface _Unknown { + statusCode: void; + content: core.Fetcher.Error; + } + + export interface _Visitor<_Result> { + _other: (value: core.Fetcher.Error) => _Result; + } +} + +export const Error = { + _unknown: (fetcherError: core.Fetcher.Error): SeedExhaustive.endpoints.params.uploadWithPath.Error._Unknown => { + return { + statusCode: undefined, + content: fetcherError, + }; + }, + + _visit: <_Result>( + value: SeedExhaustive.endpoints.params.uploadWithPath.Error, + visitor: SeedExhaustive.endpoints.params.uploadWithPath.Error._Visitor<_Result>, + ): _Result => { + switch (value.statusCode) { + default: + return visitor._other(value.content); + } + }, +} as const; diff --git a/seed/ts-sdk/exhaustive/never-throw-errors/src/core/exports.ts b/seed/ts-sdk/exhaustive/never-throw-errors/src/core/exports.ts index 69296d7100d6..358d1fb9806c 100644 --- a/seed/ts-sdk/exhaustive/never-throw-errors/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/never-throw-errors/src/core/exports.ts @@ -1 +1,2 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; diff --git a/seed/ts-sdk/exhaustive/never-throw-errors/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/never-throw-errors/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/never-throw-errors/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/never-throw-errors/src/core/file/file.ts b/seed/ts-sdk/exhaustive/never-throw-errors/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/never-throw-errors/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/never-throw-errors/src/core/file/index.ts b/seed/ts-sdk/exhaustive/never-throw-errors/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/never-throw-errors/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/never-throw-errors/src/core/file/types.ts b/seed/ts-sdk/exhaustive/never-throw-errors/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/never-throw-errors/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/never-throw-errors/src/core/index.ts b/seed/ts-sdk/exhaustive/never-throw-errors/src/core/index.ts index 92290bfadcac..42fbe52b699b 100644 --- a/seed/ts-sdk/exhaustive/never-throw-errors/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/never-throw-errors/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./runtime/index.js"; export * as url from "./url/index.js"; diff --git a/seed/ts-sdk/exhaustive/never-throw-errors/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/never-throw-errors/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6cd6509059b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/never-throw-errors/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/never-throw-errors/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/never-throw-errors/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/never-throw-errors/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/no-custom-config/README.md b/seed/ts-sdk/exhaustive/no-custom-config/README.md index 13d52ef3b543..5858447844da 100644 --- a/seed/ts-sdk/exhaustive/no-custom-config/README.md +++ b/seed/ts-sdk/exhaustive/no-custom-config/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/no-custom-config/reference.md b/seed/ts-sdk/exhaustive/no-custom-config/reference.md index 8cdfc0637d93..561bac944746 100644 --- a/seed/ts-sdk/exhaustive/no-custom-config/reference.md +++ b/seed/ts-sdk/exhaustive/no-custom-config/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/no-custom-config/snippet.json b/seed/ts-sdk/exhaustive/no-custom-config/snippet.json index 5f934395b81e..0ac6dbe4b39c 100644 --- a/seed/ts-sdk/exhaustive/no-custom-config/snippet.json +++ b/seed/ts-sdk/exhaustive/no-custom-config/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/params/client/Client.ts index 0dd63e84b8cb..26042aa010f7 100644 --- a/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/no-custom-config/src/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/no-custom-config/src/core/exports.ts b/seed/ts-sdk/exhaustive/no-custom-config/src/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/no-custom-config/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/no-custom-config/src/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/no-custom-config/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/no-custom-config/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/no-custom-config/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/no-custom-config/src/core/file/file.ts b/seed/ts-sdk/exhaustive/no-custom-config/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/no-custom-config/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/no-custom-config/src/core/file/index.ts b/seed/ts-sdk/exhaustive/no-custom-config/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/no-custom-config/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/no-custom-config/src/core/file/types.ts b/seed/ts-sdk/exhaustive/no-custom-config/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/no-custom-config/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/no-custom-config/src/core/index.ts b/seed/ts-sdk/exhaustive/no-custom-config/src/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/no-custom-config/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/no-custom-config/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/no-custom-config/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/no-custom-config/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6cd6509059b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/no-custom-config/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/no-custom-config/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/no-custom-config/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/no-custom-config/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/node-fetch/README.md b/seed/ts-sdk/exhaustive/node-fetch/README.md index 8cd3a430952c..b65eca51e5bf 100644 --- a/seed/ts-sdk/exhaustive/node-fetch/README.md +++ b/seed/ts-sdk/exhaustive/node-fetch/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/node-fetch/reference.md b/seed/ts-sdk/exhaustive/node-fetch/reference.md index 8cdfc0637d93..561bac944746 100644 --- a/seed/ts-sdk/exhaustive/node-fetch/reference.md +++ b/seed/ts-sdk/exhaustive/node-fetch/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/node-fetch/snippet.json b/seed/ts-sdk/exhaustive/node-fetch/snippet.json index 5f934395b81e..0ac6dbe4b39c 100644 --- a/seed/ts-sdk/exhaustive/node-fetch/snippet.json +++ b/seed/ts-sdk/exhaustive/node-fetch/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/node-fetch/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/node-fetch/src/api/resources/endpoints/resources/params/client/Client.ts index 0dd63e84b8cb..26042aa010f7 100644 --- a/seed/ts-sdk/exhaustive/node-fetch/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/node-fetch/src/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/node-fetch/src/core/exports.ts b/seed/ts-sdk/exhaustive/node-fetch/src/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/node-fetch/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/node-fetch/src/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/node-fetch/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/node-fetch/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/node-fetch/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/node-fetch/src/core/file/file.ts b/seed/ts-sdk/exhaustive/node-fetch/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/node-fetch/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/node-fetch/src/core/file/index.ts b/seed/ts-sdk/exhaustive/node-fetch/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/node-fetch/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/node-fetch/src/core/file/types.ts b/seed/ts-sdk/exhaustive/node-fetch/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/node-fetch/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/node-fetch/src/core/index.ts b/seed/ts-sdk/exhaustive/node-fetch/src/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/node-fetch/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/node-fetch/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/node-fetch/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/node-fetch/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6cd6509059b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/node-fetch/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/node-fetch/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/node-fetch/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/node-fetch/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/output-src-only/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/output-src-only/api/resources/endpoints/resources/params/client/Client.ts index 0dd63e84b8cb..26042aa010f7 100644 --- a/seed/ts-sdk/exhaustive/output-src-only/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/output-src-only/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/output-src-only/core/exports.ts b/seed/ts-sdk/exhaustive/output-src-only/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/output-src-only/core/exports.ts +++ b/seed/ts-sdk/exhaustive/output-src-only/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/output-src-only/core/file/exports.ts b/seed/ts-sdk/exhaustive/output-src-only/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/output-src-only/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/output-src-only/core/file/file.ts b/seed/ts-sdk/exhaustive/output-src-only/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/output-src-only/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/output-src-only/core/file/index.ts b/seed/ts-sdk/exhaustive/output-src-only/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/output-src-only/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/output-src-only/core/file/types.ts b/seed/ts-sdk/exhaustive/output-src-only/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/output-src-only/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/output-src-only/core/index.ts b/seed/ts-sdk/exhaustive/output-src-only/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/output-src-only/core/index.ts +++ b/seed/ts-sdk/exhaustive/output-src-only/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/output-src-only/snippet.json b/seed/ts-sdk/exhaustive/output-src-only/snippet.json index 72c45bc945b1..641835eb3d15 100644 --- a/seed/ts-sdk/exhaustive/output-src-only/snippet.json +++ b/seed/ts-sdk/exhaustive/output-src-only/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"./src/Client\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"./src/Client\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/package-path/README.md b/seed/ts-sdk/exhaustive/package-path/README.md index 13d52ef3b543..5858447844da 100644 --- a/seed/ts-sdk/exhaustive/package-path/README.md +++ b/seed/ts-sdk/exhaustive/package-path/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/package-path/reference.md b/seed/ts-sdk/exhaustive/package-path/reference.md index 7436963d3de3..eb045e2ab9c8 100644 --- a/seed/ts-sdk/exhaustive/package-path/reference.md +++ b/seed/ts-sdk/exhaustive/package-path/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/package-path/snippet.json b/seed/ts-sdk/exhaustive/package-path/snippet.json index 5f934395b81e..0ac6dbe4b39c 100644 --- a/seed/ts-sdk/exhaustive/package-path/snippet.json +++ b/seed/ts-sdk/exhaustive/package-path/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/api/resources/endpoints/resources/params/client/Client.ts index 0dd63e84b8cb..26042aa010f7 100644 --- a/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/exports.ts b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/exports.ts +++ b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/file/exports.ts b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/file/file.ts b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/file/index.ts b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/file/types.ts b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/index.ts b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/index.ts +++ b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..cbed0f072a1d --- /dev/null +++ b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../../../src/test-packagePath/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/package-path/src/test-packagePath/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/README.md b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/README.md index 13d52ef3b543..5858447844da 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/README.md +++ b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/reference.md b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/reference.md index 78b08901cb99..40ac2028d120 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/reference.md +++ b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/snippet.json b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/snippet.json index efcd155ac32b..c1afcb17a06a 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/snippet.json +++ b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/api/resources/endpoints/resources/params/client/Client.ts index 0dd63e84b8cb..26042aa010f7 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/exports.ts b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/file/file.ts b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/file/index.ts b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/file/types.ts b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/index.ts b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6cd6509059b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/parameter-naming-camel-case/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-camel-case/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/parameter-naming-original-name/README.md b/seed/ts-sdk/exhaustive/parameter-naming-original-name/README.md index 13d52ef3b543..5858447844da 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-original-name/README.md +++ b/seed/ts-sdk/exhaustive/parameter-naming-original-name/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/parameter-naming-original-name/reference.md b/seed/ts-sdk/exhaustive/parameter-naming-original-name/reference.md index 8cdfc0637d93..561bac944746 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-original-name/reference.md +++ b/seed/ts-sdk/exhaustive/parameter-naming-original-name/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/parameter-naming-original-name/snippet.json b/seed/ts-sdk/exhaustive/parameter-naming-original-name/snippet.json index 5f934395b81e..0ac6dbe4b39c 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-original-name/snippet.json +++ b/seed/ts-sdk/exhaustive/parameter-naming-original-name/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/api/resources/endpoints/resources/params/client/Client.ts index 0dd63e84b8cb..26042aa010f7 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/exports.ts b/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/file/file.ts b/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/file/index.ts b/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/file/types.ts b/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/index.ts b/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/parameter-naming-original-name/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/parameter-naming-original-name/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/parameter-naming-original-name/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6cd6509059b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-original-name/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/parameter-naming-original-name/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/parameter-naming-original-name/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-original-name/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/README.md b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/README.md index 13d52ef3b543..5858447844da 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/README.md +++ b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/reference.md b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/reference.md index 845328de5ff9..b4c0488d0789 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/reference.md +++ b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/snippet.json b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/snippet.json index 31893008d9f6..cd7647811b27 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/snippet.json +++ b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/api/resources/endpoints/resources/params/client/Client.ts index 0dd63e84b8cb..26042aa010f7 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/exports.ts b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/file/file.ts b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/file/index.ts b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/file/types.ts b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/index.ts b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6cd6509059b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/parameter-naming-snake-case/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-snake-case/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/README.md b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/README.md index 13d52ef3b543..5858447844da 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/README.md +++ b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/reference.md b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/reference.md index 8cdfc0637d93..561bac944746 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/reference.md +++ b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/snippet.json b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/snippet.json index 5f934395b81e..0ac6dbe4b39c 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/snippet.json +++ b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/api/resources/endpoints/resources/params/client/Client.ts index 0dd63e84b8cb..26042aa010f7 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/exports.ts b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/file/file.ts b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/file/index.ts b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/file/types.ts b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/index.ts b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6cd6509059b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/parameter-naming-wire-value/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/parameter-naming-wire-value/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/README.md b/seed/ts-sdk/exhaustive/retain-original-casing/README.md index 13d52ef3b543..5858447844da 100644 --- a/seed/ts-sdk/exhaustive/retain-original-casing/README.md +++ b/seed/ts-sdk/exhaustive/retain-original-casing/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/reference.md b/seed/ts-sdk/exhaustive/retain-original-casing/reference.md index 8cdfc0637d93..561bac944746 100644 --- a/seed/ts-sdk/exhaustive/retain-original-casing/reference.md +++ b/seed/ts-sdk/exhaustive/retain-original-casing/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/snippet.json b/seed/ts-sdk/exhaustive/retain-original-casing/snippet.json index 5f934395b81e..0ac6dbe4b39c 100644 --- a/seed/ts-sdk/exhaustive/retain-original-casing/snippet.json +++ b/seed/ts-sdk/exhaustive/retain-original-casing/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/params/client/Client.ts index 0dd63e84b8cb..26042aa010f7 100644 --- a/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/retain-original-casing/src/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/src/core/exports.ts b/seed/ts-sdk/exhaustive/retain-original-casing/src/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/retain-original-casing/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/retain-original-casing/src/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/retain-original-casing/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/retain-original-casing/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/src/core/file/file.ts b/seed/ts-sdk/exhaustive/retain-original-casing/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/retain-original-casing/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/src/core/file/index.ts b/seed/ts-sdk/exhaustive/retain-original-casing/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/retain-original-casing/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/src/core/file/types.ts b/seed/ts-sdk/exhaustive/retain-original-casing/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/retain-original-casing/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/src/core/index.ts b/seed/ts-sdk/exhaustive/retain-original-casing/src/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/retain-original-casing/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/retain-original-casing/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/retain-original-casing/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6cd6509059b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/retain-original-casing/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/retain-original-casing/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/retain-original-casing/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/retain-original-casing/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/serde-layer/README.md b/seed/ts-sdk/exhaustive/serde-layer/README.md index 13d52ef3b543..5858447844da 100644 --- a/seed/ts-sdk/exhaustive/serde-layer/README.md +++ b/seed/ts-sdk/exhaustive/serde-layer/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/serde-layer/reference.md b/seed/ts-sdk/exhaustive/serde-layer/reference.md index d3c78ac772e2..9bcf62391eb1 100644 --- a/seed/ts-sdk/exhaustive/serde-layer/reference.md +++ b/seed/ts-sdk/exhaustive/serde-layer/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/serde-layer/snippet.json b/seed/ts-sdk/exhaustive/serde-layer/snippet.json index fd4a39cf59ff..9473f9246a3d 100644 --- a/seed/ts-sdk/exhaustive/serde-layer/snippet.json +++ b/seed/ts-sdk/exhaustive/serde-layer/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/serde-layer/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/serde-layer/src/api/resources/endpoints/resources/params/client/Client.ts index 56bd66fcd23d..574b1f38fc7d 100644 --- a/seed/ts-sdk/exhaustive/serde-layer/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/serde-layer/src/api/resources/endpoints/resources/params/client/Client.ts @@ -556,4 +556,78 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: serializers.types.ObjectWithRequiredField.parseOrThrow(_response.body, { + unrecognizedObjectKeys: "passthrough", + allowUnrecognizedUnionMembers: true, + allowUnrecognizedEnumValues: true, + skipValidation: true, + breadcrumbsPrefix: ["response"], + }), + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/serde-layer/src/core/exports.ts b/seed/ts-sdk/exhaustive/serde-layer/src/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/serde-layer/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/serde-layer/src/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/serde-layer/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer/src/core/file/file.ts b/seed/ts-sdk/exhaustive/serde-layer/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer/src/core/file/index.ts b/seed/ts-sdk/exhaustive/serde-layer/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer/src/core/file/types.ts b/seed/ts-sdk/exhaustive/serde-layer/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/serde-layer/src/core/index.ts b/seed/ts-sdk/exhaustive/serde-layer/src/core/index.ts index 70de501127aa..c8027da6c883 100644 --- a/seed/ts-sdk/exhaustive/serde-layer/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/serde-layer/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/serde-layer/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/serde-layer/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6cd6509059b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/serde-layer/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/serde-layer/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/serde-layer/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/use-jest/README.md b/seed/ts-sdk/exhaustive/use-jest/README.md index 13d52ef3b543..5858447844da 100644 --- a/seed/ts-sdk/exhaustive/use-jest/README.md +++ b/seed/ts-sdk/exhaustive/use-jest/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/use-jest/reference.md b/seed/ts-sdk/exhaustive/use-jest/reference.md index 8cdfc0637d93..561bac944746 100644 --- a/seed/ts-sdk/exhaustive/use-jest/reference.md +++ b/seed/ts-sdk/exhaustive/use-jest/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/use-jest/snippet.json b/seed/ts-sdk/exhaustive/use-jest/snippet.json index 5f934395b81e..0ac6dbe4b39c 100644 --- a/seed/ts-sdk/exhaustive/use-jest/snippet.json +++ b/seed/ts-sdk/exhaustive/use-jest/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/use-jest/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/use-jest/src/api/resources/endpoints/resources/params/client/Client.ts index 0dd63e84b8cb..26042aa010f7 100644 --- a/seed/ts-sdk/exhaustive/use-jest/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/use-jest/src/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/use-jest/src/core/exports.ts b/seed/ts-sdk/exhaustive/use-jest/src/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/use-jest/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/use-jest/src/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/use-jest/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/use-jest/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/use-jest/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/use-jest/src/core/file/file.ts b/seed/ts-sdk/exhaustive/use-jest/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/use-jest/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/use-jest/src/core/file/index.ts b/seed/ts-sdk/exhaustive/use-jest/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/use-jest/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/use-jest/src/core/file/types.ts b/seed/ts-sdk/exhaustive/use-jest/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/use-jest/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/use-jest/src/core/index.ts b/seed/ts-sdk/exhaustive/use-jest/src/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/use-jest/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/use-jest/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/use-jest/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/use-jest/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6acb74de1918 --- /dev/null +++ b/seed/ts-sdk/exhaustive/use-jest/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/use-jest/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/use-jest/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/use-jest/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file! diff --git a/seed/ts-sdk/exhaustive/web-stream-wrapper/README.md b/seed/ts-sdk/exhaustive/web-stream-wrapper/README.md index 13d52ef3b543..5858447844da 100644 --- a/seed/ts-sdk/exhaustive/web-stream-wrapper/README.md +++ b/seed/ts-sdk/exhaustive/web-stream-wrapper/README.md @@ -12,6 +12,7 @@ The Seed TypeScript library provides convenient access to the Seed APIs from Typ - [Usage](#usage) - [Request and Response Types](#request-and-response-types) - [Exception Handling](#exception-handling) +- [File Uploads](#file-uploads) - [Pagination](#pagination) - [Advanced](#advanced) - [Additional Headers](#additional-headers) @@ -78,6 +79,51 @@ try { } ``` +## File Uploads + +You can upload files using the client: + +```typescript +import { createReadStream } from "fs"; + +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), ...); +await client.endpoints.params.uploadWithPath(new ReadableStream(), ...); +await client.endpoints.params.uploadWithPath(Buffer.from('binary data'), ...); +await client.endpoints.params.uploadWithPath(new Blob(['binary data'], { type: 'audio/mpeg' }), ...); +await client.endpoints.params.uploadWithPath(new File(['binary data'], 'file.mp3'), ...); +await client.endpoints.params.uploadWithPath(new ArrayBuffer(8), ...); +await client.endpoints.params.uploadWithPath(new Uint8Array([0, 1, 2]), ...); +``` +The client accepts a variety of types for file upload parameters: +* Stream types: `fs.ReadStream`, `stream.Readable`, and `ReadableStream` +* Buffered types: `Buffer`, `Blob`, `File`, `ArrayBuffer`, `ArrayBufferView`, and `Uint8Array` + +### Metadata + +You can configure metadata when uploading a file: +```typescript +const file: Uploadable.WithMetadata = { + data: createReadStream("path/to/file"), + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +Alternatively, you can upload a file directly from a file path: +```typescript +const file : Uploadable.FromPath = { + path: "path/to/file", + filename: "my-file", // optional + contentType: "audio/mpeg", // optional + contentLength: 1949, // optional +}; +``` + +The metadata is used to set the `Content-Length`, `Content-Type`, and `Content-Disposition` headers. If not provided, the client will attempt to determine them automatically. +For example, `fs.ReadStream` has a `path` property which the SDK uses to retrieve the file size from the filesystem without loading it into memory. + + ## Pagination List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items: diff --git a/seed/ts-sdk/exhaustive/web-stream-wrapper/reference.md b/seed/ts-sdk/exhaustive/web-stream-wrapper/reference.md index 8cdfc0637d93..561bac944746 100644 --- a/seed/ts-sdk/exhaustive/web-stream-wrapper/reference.md +++ b/seed/ts-sdk/exhaustive/web-stream-wrapper/reference.md @@ -1957,6 +1957,77 @@ await client.endpoints.params.modifyWithInlinePath({ + + + + +
client.endpoints.params.uploadWithPath(uploadable, param) -> SeedExhaustive.ObjectWithRequiredField +
+
+ +#### šŸ“ Description + +
+
+ +
+
+ +POST bytes with path param returning object +
+
+
+
+ +#### šŸ”Œ Usage + +
+
+ +
+
+ +```typescript +await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path"); + +``` +
+
+
+
+ +#### āš™ļø Parameters + +
+
+ +
+
+ +**uploadable:** `core.file.Uploadable` + +
+
+ +
+
+ +**param:** `string` + +
+
+ +
+
+ +**requestOptions:** `ParamsClient.RequestOptions` + +
+
+
+
+ +
diff --git a/seed/ts-sdk/exhaustive/web-stream-wrapper/snippet.json b/seed/ts-sdk/exhaustive/web-stream-wrapper/snippet.json index 5f934395b81e..0ac6dbe4b39c 100644 --- a/seed/ts-sdk/exhaustive/web-stream-wrapper/snippet.json +++ b/seed/ts-sdk/exhaustive/web-stream-wrapper/snippet.json @@ -353,6 +353,17 @@ "client": "import { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.modifyWithInlinePath({\n param: \"param\",\n body: \"string\"\n});\n" } }, + { + "id": { + "path": "/params/path/{param}", + "method": "POST", + "identifier_override": "endpoint_endpoints/params.uploadWithPath" + }, + "snippet": { + "type": "typescript", + "client": "import { createReadStream } from \"fs\";\nimport { SeedExhaustiveClient } from \"@fern/exhaustive\";\n\nconst client = new SeedExhaustiveClient({ environment: \"YOUR_BASE_URL\", token: \"YOUR_TOKEN\" });\nawait client.endpoints.params.uploadWithPath(createReadStream(\"path/to/file\"), \"upload-path\");\n" + } + }, { "id": { "path": "/primitive/string", diff --git a/seed/ts-sdk/exhaustive/web-stream-wrapper/src/api/resources/endpoints/resources/params/client/Client.ts b/seed/ts-sdk/exhaustive/web-stream-wrapper/src/api/resources/endpoints/resources/params/client/Client.ts index 0dd63e84b8cb..26042aa010f7 100644 --- a/seed/ts-sdk/exhaustive/web-stream-wrapper/src/api/resources/endpoints/resources/params/client/Client.ts +++ b/seed/ts-sdk/exhaustive/web-stream-wrapper/src/api/resources/endpoints/resources/params/client/Client.ts @@ -513,4 +513,72 @@ export class ParamsClient { return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/params/path/{param}"); } + + /** + * POST bytes with path param returning object + * + * @param {core.file.Uploadable} uploadable + * @param {string} param + * @param {ParamsClient.RequestOptions} requestOptions - Request-specific configuration. + * + * @example + * import { createReadStream } from "fs"; + * await client.endpoints.params.uploadWithPath(createReadStream("path/to/file"), "upload-path") + */ + public uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): core.HttpResponsePromise { + return core.HttpResponsePromise.fromPromise(this.__uploadWithPath(uploadable, param, requestOptions)); + } + + private async __uploadWithPath( + uploadable: core.file.Uploadable, + param: string, + requestOptions?: ParamsClient.RequestOptions, + ): Promise> { + const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + const _authRequest: core.AuthRequest = await this._options.authProvider.getAuthRequest(); + const _headers: core.Fetcher.Args["headers"] = mergeHeaders( + _authRequest.headers, + this._options?.headers, + _binaryUploadRequest.headers, + requestOptions?.headers, + ); + const _response = await core.fetcher({ + url: core.url.join( + (await core.Supplier.get(this._options.baseUrl)) ?? + (await core.Supplier.get(this._options.environment)), + `/params/path/${core.url.encodePathParam(param)}`, + ), + method: "POST", + headers: _headers, + queryParameters: requestOptions?.queryParams, + requestType: "bytes", + duplex: "half", + body: _binaryUploadRequest.body, + timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, + maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + abortSignal: requestOptions?.abortSignal, + fetchFn: this._options?.fetch, + logging: this._options.logging, + }); + if (_response.ok) { + return { + data: _response.body as SeedExhaustive.types.ObjectWithRequiredField, + rawResponse: _response.rawResponse, + }; + } + + if (_response.error.reason === "status-code") { + throw new errors.SeedExhaustiveError({ + statusCode: _response.error.statusCode, + body: _response.error.body, + rawResponse: _response.rawResponse, + }); + } + + return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/params/path/{param}"); + } } diff --git a/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/exports.ts b/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/exports.ts index d27744915f15..c21f05694e74 100644 --- a/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/exports.ts +++ b/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/exports.ts @@ -1,2 +1,3 @@ +export * from "./file/exports.js"; export * from "./logging/exports.js"; export * from "./pagination/exports.js"; diff --git a/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/file/exports.ts b/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/file/exports.ts new file mode 100644 index 000000000000..3b0b39675727 --- /dev/null +++ b/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/file/exports.ts @@ -0,0 +1 @@ +export type { Uploadable } from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/file/file.ts b/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/file/file.ts new file mode 100644 index 000000000000..0bacc484109e --- /dev/null +++ b/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/file/file.ts @@ -0,0 +1,217 @@ +import type { Uploadable } from "./types.js"; + +export async function toBinaryUploadRequest( + file: Uploadable, +): Promise<{ body: Uploadable.FileLike; headers?: Record }> { + const { data, filename, contentLength, contentType } = await getFileWithMetadata(file); + const request = { + body: data, + headers: {} as Record, + }; + if (filename) { + request.headers["Content-Disposition"] = `attachment; filename="${filename}"`; + } + if (contentType) { + request.headers["Content-Type"] = contentType; + } + if (contentLength != null) { + request.headers["Content-Length"] = contentLength.toString(); + } + return request; +} + +export async function toMultipartDataPart( + file: Uploadable, +): Promise<{ data: Uploadable.FileLike; filename?: string; contentType?: string }> { + const { data, filename, contentType } = await getFileWithMetadata(file, { + noSniffFileSize: true, + }); + return { + data, + filename, + contentType, + }; +} + +async function getFileWithMetadata( + file: Uploadable, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isFileLike(file)) { + return getFileWithMetadata( + { + data: file, + }, + { noSniffFileSize }, + ); + } + + if ("path" in file) { + const fs = await import("fs"); + if (!fs || !fs.createReadStream) { + throw new Error("File path uploads are not supported in this environment."); + } + const data = fs.createReadStream(file.path); + const contentLength = + file.contentLength ?? (noSniffFileSize === true ? undefined : await tryGetFileSizeFromPath(file.path)); + const filename = file.filename ?? getNameFromPath(file.path); + return { + data, + filename, + contentType: file.contentType, + contentLength, + }; + } + if ("data" in file) { + const data = file.data; + const contentLength = + file.contentLength ?? + (await tryGetContentLengthFromFileLike(data, { + noSniffFileSize, + })); + const filename = file.filename ?? tryGetNameFromFileLike(data); + return { + data, + filename, + contentType: file.contentType ?? tryGetContentTypeFromFileLike(data), + contentLength, + }; + } + + throw new Error(`Invalid FileUpload of type ${typeof file}: ${JSON.stringify(file)}`); +} + +function isFileLike(value: unknown): value is Uploadable.FileLike { + return ( + isBuffer(value) || + isArrayBufferView(value) || + isArrayBuffer(value) || + isUint8Array(value) || + isBlob(value) || + isFile(value) || + isStreamLike(value) || + isReadableStream(value) + ); +} + +async function tryGetFileSizeFromPath(path: string): Promise { + try { + const fs = await import("fs"); + if (!fs || !fs.promises || !fs.promises.stat) { + return undefined; + } + const fileStat = await fs.promises.stat(path); + return fileStat.size; + } catch (_fallbackError) { + return undefined; + } +} + +function tryGetNameFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isNamedValue(data)) { + return data.name; + } + if (isPathedValue(data)) { + return getNameFromPath(data.path.toString()); + } + return undefined; +} + +async function tryGetContentLengthFromFileLike( + data: Uploadable.FileLike, + { noSniffFileSize }: { noSniffFileSize?: boolean } = {}, +): Promise { + if (isBuffer(data)) { + return data.length; + } + if (isArrayBufferView(data)) { + return data.byteLength; + } + if (isArrayBuffer(data)) { + return data.byteLength; + } + if (isBlob(data)) { + return data.size; + } + if (isFile(data)) { + return data.size; + } + if (noSniffFileSize === true) { + return undefined; + } + if (isPathedValue(data)) { + return await tryGetFileSizeFromPath(data.path.toString()); + } + return undefined; +} + +function tryGetContentTypeFromFileLike(data: Uploadable.FileLike): string | undefined { + if (isBlob(data)) { + return data.type; + } + if (isFile(data)) { + return data.type; + } + + return undefined; +} + +function getNameFromPath(path: string): string | undefined { + const lastForwardSlash = path.lastIndexOf("/"); + const lastBackSlash = path.lastIndexOf("\\"); + const lastSlashIndex = Math.max(lastForwardSlash, lastBackSlash); + return lastSlashIndex >= 0 ? path.substring(lastSlashIndex + 1) : path; +} + +type NamedValue = { + name: string; +} & unknown; + +type PathedValue = { + path: string | { toString(): string }; +} & unknown; + +type StreamLike = { + read?: () => unknown; + pipe?: (dest: unknown) => unknown; +} & unknown; + +function isNamedValue(value: unknown): value is NamedValue { + return typeof value === "object" && value != null && "name" in value; +} + +function isPathedValue(value: unknown): value is PathedValue { + return typeof value === "object" && value != null && "path" in value; +} + +function isStreamLike(value: unknown): value is StreamLike { + return typeof value === "object" && value != null && ("read" in value || "pipe" in value); +} + +function isReadableStream(value: unknown): value is ReadableStream { + return typeof value === "object" && value != null && "getReader" in value; +} + +function isBuffer(value: unknown): value is Buffer { + return typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(value); +} + +function isArrayBufferView(value: unknown): value is ArrayBufferView { + return typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value); +} + +function isArrayBuffer(value: unknown): value is ArrayBuffer { + return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer; +} + +function isUint8Array(value: unknown): value is Uint8Array { + return typeof Uint8Array !== "undefined" && value instanceof Uint8Array; +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== "undefined" && value instanceof Blob; +} + +function isFile(value: unknown): value is File { + return typeof File !== "undefined" && value instanceof File; +} diff --git a/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/file/index.ts b/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/file/index.ts new file mode 100644 index 000000000000..fc16dd52e71c --- /dev/null +++ b/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/file/index.ts @@ -0,0 +1,2 @@ +export * from "./file.js"; +export * from "./types.js"; diff --git a/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/file/types.ts b/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/file/types.ts new file mode 100644 index 000000000000..531b6927f145 --- /dev/null +++ b/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/file/types.ts @@ -0,0 +1,81 @@ +/** + * A file that can be uploaded. Can be a file-like object (stream, buffer, blob, etc.), + * a path to a file, or an object with a file-like object and metadata. + */ +export type Uploadable = Uploadable.FileLike | Uploadable.FromPath | Uploadable.WithMetadata; + +export namespace Uploadable { + /** + * Various file-like objects that can be used to upload a file. + */ + export type FileLike = + | ArrayBuffer + | ArrayBufferLike + | ArrayBufferView + | Uint8Array + | import("buffer").Buffer + | import("buffer").Blob + | import("buffer").File + | import("stream").Readable + | import("stream/web").ReadableStream + | globalThis.Blob + | globalThis.File + | ReadableStream; + + /** + * A file path with optional metadata, used for uploading a file from the file system. + */ + export type FromPath = { + /** The path to the file to upload */ + path: string; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + */ + contentType?: string; + /** + * Optional file size in bytes. + * If not provided, the file size will be determined from the file system. + * The content length is used to set the `Content-Length` header in upload requests. + */ + contentLength?: number; + }; + + /** + * A file-like object with metadata, used for uploading files. + */ + export type WithMetadata = { + /** The file data */ + data: FileLike; + /** + * Optional override for the file name (defaults to basename of path). + * This is used to set the `Content-Disposition` header in upload requests. + */ + filename?: string; + /** + * Optional MIME type of the file (e.g., 'image/jpeg', 'text/plain'). + * This is used to set the `Content-Type` header in upload requests. + * + * If not provided, the content type may be determined from the data itself. + * * If the data is a `File`, `Blob`, or similar, the content type will be determined from the file itself, if the type is set. + * * Any other data type will not have a content type set, and the upload request will use `Content-Type: application/octet-stream` instead. + */ + contentType?: string; + /** + * Optional file size in bytes. + * The content length is used to set the `Content-Length` header in upload requests. + * If the content length is not provided and cannot be determined, the upload request will not include the `Content-Length` header, but will use `Transfer-Encoding: chunked` instead. + * + * If not provided, the file size will be determined depending on the data type. + * * If the data is of type `fs.ReadStream` (`createReadStream`), the size will be determined from the file system. + * * If the data is a `Buffer`, `ArrayBuffer`, `Uint8Array`, `Blob`, `File`, or similar, the size will be determined from the data itself. + * * If the data is a `Readable` or `ReadableStream`, the size will not be determined. + */ + contentLength?: number; + }; +} diff --git a/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/index.ts b/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/index.ts index f25662a55dd3..e8dddd634ae3 100644 --- a/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/index.ts +++ b/seed/ts-sdk/exhaustive/web-stream-wrapper/src/core/index.ts @@ -1,6 +1,7 @@ export * from "./auth/index.js"; export * from "./base64.js"; export * from "./fetcher/index.js"; +export * as file from "./file/index.js"; export * as logging from "./logging/index.js"; export * from "./pagination/index.js"; export * from "./runtime/index.js"; diff --git a/seed/ts-sdk/exhaustive/web-stream-wrapper/tests/unit/file/file.test.ts b/seed/ts-sdk/exhaustive/web-stream-wrapper/tests/unit/file/file.test.ts new file mode 100644 index 000000000000..6cd6509059b2 --- /dev/null +++ b/seed/ts-sdk/exhaustive/web-stream-wrapper/tests/unit/file/file.test.ts @@ -0,0 +1,499 @@ +import fs from "fs"; +import { join } from "path"; +import { Readable } from "stream"; +import { toBinaryUploadRequest, type Uploadable } from "../../../src/core/file/index"; + +describe("toBinaryUploadRequest", () => { + const TEST_FILE_PATH = join(__dirname, "..", "test-file.txt"); + const TEST_FILE_SIZE = fs.statSync(TEST_FILE_PATH).size.toString(); + + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("Buffer input", () => { + it("should handle Buffer with all metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: "text/plain", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Type": "text/plain", + "Content-Length": "42", + }); + }); + + it("should handle Buffer without metadata", async () => { + const buffer = Buffer.from("test data"); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + + it("should handle Buffer passed directly", async () => { + const buffer = Buffer.from("test data"); + + const result = await toBinaryUploadRequest(buffer); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "9", // buffer.length + }); + }); + }); + + describe("ArrayBuffer input", () => { + it("should handle ArrayBuffer with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const input: Uploadable.WithMetadata = { + data: arrayBuffer, + filename: "data.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + + it("should handle ArrayBuffer passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + + const result = await toBinaryUploadRequest(arrayBuffer); + + expect(result.body).toBe(arrayBuffer); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBuffer.byteLength + }); + }); + }); + + describe("Uint8Array input", () => { + it("should handle Uint8Array with metadata", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + const input: Uploadable.WithMetadata = { + data: uint8Array, + filename: "bytes.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="bytes.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "5", // uint8Array.byteLength + }); + }); + + it("should handle Uint8Array passed directly", async () => { + const uint8Array = new Uint8Array([1, 2, 3, 4, 5]); + + const result = await toBinaryUploadRequest(uint8Array); + + expect(result.body).toBe(uint8Array); + expect(result.headers).toEqual({ + "Content-Length": "5", // uint8Array.byteLength + }); + }); + }); + + describe("Blob input", () => { + it("should handle Blob with metadata", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "override.txt", + contentType: "text/html", // Override blob's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="override.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob with intrinsic type", async () => { + const blob = new Blob(["test content"], { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: blob, + filename: "data.json", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="data.json"', + "Content-Type": "application/json", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + + it("should handle Blob passed directly", async () => { + const blob = new Blob(["test content"], { type: "text/plain" }); + + const result = await toBinaryUploadRequest(blob); + + expect(result.body).toBe(blob); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", // Should use blob's type + "Content-Length": "12", // blob.size + }); + }); + }); + + describe("File input", () => { + it("should handle File with metadata", async () => { + const file = new File(["file content"], "original.txt", { type: "text/plain" }); + const input: Uploadable.WithMetadata = { + data: file, + filename: "renamed.txt", + contentType: "text/html", // Override file's type + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="renamed.txt"', + "Content-Type": "text/html", // Should use provided contentType + "Content-Length": "12", // file.size + }); + }); + + it("should handle File with intrinsic properties", async () => { + const file = new File(["file content"], "test.json", { type: "application/json" }); + const input: Uploadable.WithMetadata = { + data: file, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.json"', // Should use file's name + "Content-Type": "application/json", // Should use file's type + "Content-Length": "12", // file.size + }); + }); + + it("should handle File passed directly", async () => { + const file = new File(["file content"], "direct.txt", { type: "text/plain" }); + + const result = await toBinaryUploadRequest(file); + + expect(result.body).toBe(file); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="direct.txt"', + "Content-Type": "text/plain", + "Content-Length": "12", // file.size + }); + }); + }); + + describe("ReadableStream input", () => { + it("should handle ReadableStream with metadata", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + contentLength: 100, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + "Content-Length": "100", // Should use provided contentLength + }); + }); + + it("should handle ReadableStream without size", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + const input: Uploadable.WithMetadata = { + data: stream, + filename: "stream.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="stream.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from ReadableStream + }); + }); + + it("should handle ReadableStream passed directly", async () => { + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("stream data")); + controller.close(); + }, + }); + + const result = await toBinaryUploadRequest(stream); + + expect(result.body).toBe(stream); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("Node.js Readable stream input", () => { + it("should handle Readable stream with metadata", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + contentLength: 50, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + "Content-Length": "50", // Should use provided contentLength + }); + }); + + it("should handle Readable stream without size", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + const input: Uploadable.WithMetadata = { + data: readable, + filename: "readable.txt", + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="readable.txt"', + "Content-Type": "text/plain", + // No Content-Length header since it cannot be determined from Readable + }); + }); + + it("should handle Readable stream passed directly", async () => { + const readable = new Readable({ + read() { + this.push("readable data"); + this.push(null); + }, + }); + + const result = await toBinaryUploadRequest(readable); + + expect(result.body).toBe(readable); + expect(result.headers).toEqual({ + // No headers since no metadata provided and cannot be determined + }); + }); + }); + + describe("File path input (FromPath type)", () => { + it("should handle file path with all metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + filename: "custom.txt", + contentType: "text/html", + contentLength: 42, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="custom.txt"', + "Content-Type": "text/html", + "Content-Length": "42", // Should use provided contentLength + }); + }); + + it("should handle file path with minimal metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Type": "text/plain", + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + + it("should handle file path with no metadata", async () => { + const input: Uploadable.FromPath = { + path: TEST_FILE_PATH, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBeInstanceOf(fs.ReadStream); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test-file.txt"', // Should extract from path + "Content-Length": TEST_FILE_SIZE, // Should determine from file system (OS-agnostic) + }); + }); + }); + + describe("ArrayBufferView input", () => { + it("should handle ArrayBufferView with metadata", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + const input: Uploadable.WithMetadata = { + data: arrayBufferView, + filename: "view.bin", + contentType: "application/octet-stream", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="view.bin"', + "Content-Type": "application/octet-stream", + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + + it("should handle ArrayBufferView passed directly", async () => { + const arrayBuffer = new ArrayBuffer(10); + const arrayBufferView = new Int8Array(arrayBuffer); + + const result = await toBinaryUploadRequest(arrayBufferView); + + expect(result.body).toBe(arrayBufferView); + expect(result.headers).toEqual({ + "Content-Length": "10", // arrayBufferView.byteLength + }); + }); + }); + + describe("Edge cases", () => { + it("should handle empty headers when no metadata is available", async () => { + const buffer = Buffer.from(""); + const input: Uploadable.WithMetadata = { + data: buffer, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", + }); + }); + + it("should handle zero contentLength", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + contentLength: 0, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Length": "0", // Should use provided 0 + }); + }); + + it("should handle null filename", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: undefined, + contentType: "text/plain", + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Type": "text/plain", + "Content-Length": "4", + // No Content-Disposition since filename is undefined + }); + }); + + it("should handle null contentType", async () => { + const buffer = Buffer.from("test"); + const input: Uploadable.WithMetadata = { + data: buffer, + filename: "test.txt", + contentType: undefined, + }; + + const result = await toBinaryUploadRequest(input); + + expect(result.body).toBe(buffer); + expect(result.headers).toEqual({ + "Content-Disposition": 'attachment; filename="test.txt"', + "Content-Length": "4", + // No Content-Type since contentType is undefined + }); + }); + }); +}); diff --git a/seed/ts-sdk/exhaustive/web-stream-wrapper/tests/unit/test-file.txt b/seed/ts-sdk/exhaustive/web-stream-wrapper/tests/unit/test-file.txt new file mode 100644 index 000000000000..c66d471e359c --- /dev/null +++ b/seed/ts-sdk/exhaustive/web-stream-wrapper/tests/unit/test-file.txt @@ -0,0 +1 @@ +This is a test file!