From 94f6f642961017cd01f3b131d5195f702ecdf6d9 Mon Sep 17 00:00:00 2001 From: saga-agent Date: Fri, 17 Jul 2026 11:03:23 +0000 Subject: [PATCH] [verified] Fix Google Photos upload endpoint --- .changeset/fix-google-photos-upload-path.md | 5 +++ .../src/providers/google/discovery.test.ts | 36 +++++++++++++++++++ .../openapi/src/providers/google/discovery.ts | 2 +- 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 .changeset/fix-google-photos-upload-path.md diff --git a/.changeset/fix-google-photos-upload-path.md b/.changeset/fix-google-photos-upload-path.md new file mode 100644 index 000000000..bb02c51c0 --- /dev/null +++ b/.changeset/fix-google-photos-upload-path.md @@ -0,0 +1,5 @@ +--- +"@executor-js/plugin-openapi": patch +--- + +Use the versioned Google Photos raw upload endpoint so generated upload tools send media to `/v1/uploads` instead of the invalid `/uploads` path. diff --git a/packages/plugins/openapi/src/providers/google/discovery.test.ts b/packages/plugins/openapi/src/providers/google/discovery.test.ts index 2d2d6fcb8..957968585 100644 --- a/packages/plugins/openapi/src/providers/google/discovery.test.ts +++ b/packages/plugins/openapi/src/providers/google/discovery.test.ts @@ -491,6 +491,42 @@ it.effect("marks Google Discovery media-download methods as binary responses", ( }), ); +it.effect("uses the versioned Google Photos raw upload endpoint", () => + Effect.gen(function* () { + const result = yield* convertGoogleDiscoveryToOpenApi({ + discoveryUrl: "https://www.googleapis.com/discovery/v1/apis/photoslibrary/v1/rest", + // @effect-diagnostics-next-line preferSchemaOverJson:off + documentText: JSON.stringify({ + name: "photoslibrary", + version: "v1", + title: "Google Photos Library API", + rootUrl: "https://photoslibrary.googleapis.com/", + servicePath: "", + auth: { + oauth2: { + scopes: { + "https://www.googleapis.com/auth/photoslibrary.appendonly": { + description: "Upload to Google Photos", + }, + }, + }, + }, + resources: {}, + schemas: {}, + }), + }); + + const spec = decodeConvertedSpec(result.specText); + const upload = spec.paths["/v1/uploads"]?.post; + expect(upload).toMatchObject({ + operationId: "photoslibrary.mediaItems.upload", + "x-executor-toolPath": "photoslibrary.mediaItems.upload", + "x-executor-pathTemplate": "/v1/uploads", + servers: [{ url: "https://photoslibrary.googleapis.com/" }], + }); + }), +); + it.effect("supplies documented scopes when Picker Discovery omits auth metadata", () => Effect.gen(function* () { const result = yield* convertGoogleDiscoveryToOpenApi({ diff --git a/packages/plugins/openapi/src/providers/google/discovery.ts b/packages/plugins/openapi/src/providers/google/discovery.ts index 6b53c255f..96f2abeef 100644 --- a/packages/plugins/openapi/src/providers/google/discovery.ts +++ b/packages/plugins/openapi/src/providers/google/discovery.ts @@ -829,7 +829,7 @@ const GOOGLE_OAUTH_SECURITY_SCHEME = "googleOAuth2"; const GOOGLE_PHOTOS_LIBRARY_SERVICE = "photoslibrary"; const GOOGLE_PHOTOS_APPENDONLY_SCOPE = "https://www.googleapis.com/auth/photoslibrary.appendonly"; const GOOGLE_PHOTOS_UPLOAD_TOOL_PATH = "photoslibrary.mediaItems.upload"; -const GOOGLE_PHOTOS_UPLOAD_PATH = "/uploads"; +const GOOGLE_PHOTOS_UPLOAD_PATH = "/v1/uploads"; const isGooglePhotosService = (service: string): boolean => service === GOOGLE_PHOTOS_LIBRARY_SERVICE || service === GOOGLE_PHOTOS_PICKER_SERVICE;