Skip to content

Commit 809f7cb

Browse files
bburdaclaude
andcommitted
fix(scripts): follow the tightened script request bodies
The gateway now describes both script request bodies instead of leaving them open. The upload body declares a required `file` part with optional `metadata`; the execution body is ScriptExecutionRequest, with `execution_type` required. Two casts in api-dispatch.ts existed only because those bodies used to be untyped, and they stop compiling against the 0.7.0 client. The execution cast is gone. StartScriptExecutionRequest already has the same shape as the generated ScriptExecutionRequest, so the body passes through and a future divergence between the two becomes a type error here. The upload keeps a cast, because FormData is a DOM interface and can never be assignable to a generated object type, but it now names the declared body instead of Record<string, unknown>, so a change to the parts is caught at compile time rather than as a 400 at runtime. No request path changed between 0.6.0 and 0.7.0, so nothing else is affected. The client pin moves to ^0.7.0 and the package version follows it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015wk3HGgcpJyVQB6BK297Xw
1 parent d5054b7 commit 809f7cb

3 files changed

Lines changed: 23 additions & 18 deletions

File tree

package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "ros2_medkit_web_ui",
33
"private": true,
4-
"version": "0.6.0",
4+
"version": "0.7.0",
55
"type": "module",
66
"description": "Simple web UI for browsing SOVD entity trees via discovery endpoints",
77
"repository": {
@@ -42,7 +42,7 @@
4242
"@radix-ui/react-slot": "^1.2.3",
4343
"@radix-ui/react-switch": "^1.2.6",
4444
"@radix-ui/react-tooltip": "^1.2.8",
45-
"@selfpatch/ros2-medkit-client-ts": "^0.6.0",
45+
"@selfpatch/ros2-medkit-client-ts": "^0.7.0",
4646
"@tailwindcss/vite": "^4.1.14",
4747
"@uiw/react-codemirror": "^4.25.11",
4848
"class-variance-authority": "^0.7.1",

src/lib/api-dispatch.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
* typed path based on the entity type string.
2222
*/
2323

24-
import type { MedkitClient } from '@selfpatch/ros2-medkit-client-ts';
24+
import type { MedkitClient, paths } from '@selfpatch/ros2-medkit-client-ts';
2525
import type { SovdResourceEntityType, LifecycleAction, ScriptEntityType, StartScriptExecutionRequest } from './types';
2626
import type { LogsQueryParams, LogsConfiguration } from './log-types';
2727

@@ -730,13 +730,19 @@ export function getEntityScript(
730730
}
731731
}
732732

733+
/** The upload body both script paths declare: a required `file` part plus optional `metadata`. */
734+
type ScriptUploadBody = NonNullable<
735+
paths['/apps/{app_id}/scripts']['post']['requestBody']
736+
>['content']['multipart/form-data'];
737+
733738
/**
734739
* Multipart upload.
735740
*
736-
* The spec declares the body as `{type: object, additionalProperties: true}`, so
737-
* the generated type is `{ [key: string]: unknown }` and FormData (a DOM interface)
738-
* is not assignable to it. bodySerializer returns the FormData unchanged so fetch
739-
* sets Content-Type with the multipart boundary itself - the gateway rejects the
741+
* FormData is a DOM interface, so it is never assignable to the generated body
742+
* object however the spec describes it - hence the cast, which names the real
743+
* body type so a change to the declared parts is a type error here rather than
744+
* a runtime 400. bodySerializer returns the FormData unchanged so fetch sets
745+
* Content-Type with the multipart boundary itself - the gateway rejects the
740746
* request without it.
741747
*/
742748
export function uploadEntityScript(
@@ -745,7 +751,7 @@ export function uploadEntityScript(
745751
entityId: string,
746752
form: FormData
747753
) {
748-
const body = form as unknown as Record<string, unknown>;
754+
const body = form as unknown as ScriptUploadBody;
749755
const bodySerializer = (value: unknown) => value as FormData;
750756
switch (entityType) {
751757
case 'apps':
@@ -784,9 +790,8 @@ export function deleteEntityScript(
784790
/**
785791
* Start an execution.
786792
*
787-
* The spec declares this request body as a bare `type: object`, so the generated
788-
* type is `Record<string, never>` and any real body fails the type check. The cast
789-
* keeps the runtime payload correct; removing it requires a spec fix in the gateway.
793+
* StartScriptExecutionRequest mirrors the generated ScriptExecutionRequest, so the
794+
* body passes straight through and a divergence between the two shows up here.
790795
*/
791796
export function startScriptExecution(
792797
client: MedkitClient,
@@ -795,7 +800,7 @@ export function startScriptExecution(
795800
scriptId: string,
796801
request: StartScriptExecutionRequest
797802
) {
798-
const body = request as unknown as Record<string, never>;
803+
const body = request;
799804
switch (entityType) {
800805
case 'apps':
801806
return client.POST('/apps/{app_id}/scripts/{script_id}/executions', {

0 commit comments

Comments
 (0)