History

Mark Google media downloads as binary responses (#1096)

batumilove committed 4 months ago

Commit 4ef21991 parent 907502e2 files · +93 −11Browse files

Unified · Split

2 files
  1. packages/plugins/google/src/sdk/discovery.test.ts +58 −0
  2. packages/plugins/google/src/sdk/discovery.ts +35 −11
packages/plugins/google/src/sdk/discovery.test.ts+58−0
@@ -274,6 +274,64 @@ it.effect("converts Google Discovery documents into Executor-preserving OpenAPI · 273 unmodified lines
274274 }),
275275 );
276276
277+it.effect("marks Google Discovery media-download methods as binary responses", () =>
278+ Effect.gen(function* () {
279+ const result = yield* convertGoogleDiscoveryToOpenApi({
280+ discoveryUrl: "https://www.googleapis.com/discovery/v1/apis/drive/v3/rest",
281+ // @effect-diagnostics-next-line preferSchemaOverJson:off
282+ documentText: JSON.stringify({
283+ name: "drive",
284+ version: "v3",
285+ title: "Drive API",
286+ rootUrl: "https://www.googleapis.com/",
287+ servicePath: "drive/v3/",
288+ resources: {
289+ files: {
290+ methods: {
291+ export: {
292+ id: "drive.files.export",
293+ httpMethod: "GET",
294+ path: "files/{fileId}/export",
295+ supportsMediaDownload: true,
296+ useMediaDownloadService: true,
297+ parameters: {
298+ fileId: { location: "path", required: true, type: "string" },
299+ mimeType: { location: "query", required: true, type: "string" },
300+ },
301+ },
302+ },
303+ },
304+ },
305+ schemas: {},
306+ }),
307+ });
308+
309+ const spec = decodeConvertedSpec(result.specText);
310+ const operation = spec.paths["/files/{fileId}/export"]?.get;
311+ expect(operation?.responses).toMatchObject({
312+ "200": {
313+ content: {
314+ "application/octet-stream": {
315+ schema: { type: "string", format: "binary" },
316+ },
317+ },
318+ },
319+ });
320+
321+ const parsed = yield* parse(result.specText);
322+ const extracted = yield* extract(parsed);
323+ const exportOperation = extracted.operations.find(
324+ (candidate) => candidate.operationId === "files.export",
325+ );
326+ expect(exportOperation?.operationId).toBe("files.export");
327+ const responseFileHint = Option.flatMap(
328+ exportOperation?.responseBody ?? Option.none(),
329+ (body) => body.fileHint,
330+ );
331+ expect(Option.isSome(responseFileHint)).toBe(true);
332+ }),
333+);
334+
277335 it.effect("bundles Google Discovery documents into one Google OpenAPI source", () =>
278336 Effect.gen(function* () {
279337 const result = yield* convertGoogleDiscoveryBundleToOpenApi({
packages/plugins/google/src/sdk/discovery.ts+35−11
@@ -76,11 +76,12 @@ type OpenApiOperationObject = { · 75 unmodified lines
7676 readonly responses: {
7777 readonly "200": {
7878 readonly description: "Successful response";
79− readonly content: {
80− readonly "application/json": {
79+ readonly content: Record<
80+ string,
81+ {
8182 readonly schema: OpenApiSchemaObject;
82− };
83− };
83+ }
84+ >;
8485 };
8586 };
8687 readonly security?: readonly Record<string, readonly string[]>[];
@@ -174,6 +175,8 @@ const DiscoveryMethod = Schema.Struct({ · 87 unmodified lines
174175 request: Schema.optional(DiscoveryRef),
175176 response: Schema.optional(DiscoveryRef),
176177 scopes: TextArray,
178+ supportsMediaDownload: Schema.optional(Schema.Boolean),
179+ useMediaDownloadService: Schema.optional(Schema.Boolean),
177180 });
178181 type DiscoveryMethod = typeof DiscoveryMethod.Type;
179182
@@ -570,6 +573,33 @@ const discoveryDocumentInfo = ( · 390 unmodified lines
570573 };
571574 });
572575
576+const googleDiscoveryResponseContent = (
577+ method: DiscoveryMethod,
578+ schemaNameForRef: (name: string) => string,
579+): OpenApiOperationObject["responses"]["200"]["content"] => {
580+ if (
581+ (method.supportsMediaDownload === true || method.useMediaDownloadService === true) &&
582+ method.response === undefined
583+ ) {
584+ return {
585+ "application/octet-stream": {
586+ schema: {
587+ type: "string",
588+ format: "binary",
589+ },
590+ },
591+ };
592+ }
593+
594+ return {
595+ "application/json": {
596+ schema: method.response?.$ref
597+ ? { $ref: schemaRef(schemaNameForRef(method.response.$ref)) }
598+ : {},
599+ },
600+ };
601+};
602+
573603 const buildDiscoveryOperation = (input: {
574604 readonly document: DiscoveryDocument;
575605 readonly method: DiscoveryMethod;
@@ -635,13 +665,7 @@ const buildDiscoveryOperation = (input: { · 59 unmodified lines
635665 responses: {
636666 "200": {
637667 description: "Successful response",
638− content: {
639− "application/json": {
640− schema: input.method.response?.$ref
641− ? { $ref: schemaRef(schemaNameForRef(input.method.response.$ref)) }
642− : {},
643− },
644− },
668+ content: googleDiscoveryResponseContent(input.method, schemaNameForRef),
645669 },
646670 },
647671 ...(methodScopes.length > 0 ? { security: [{ googleOAuth2: methodScopes }] } : {}),