Browse Source

Generate Angular proxies with responseType and Accept from ContentTypes

Refs abpframework/abp#23732
pull/25639/head
maliming 4 months ago
parent
commit
e7b37b7031
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 10
      npm/ng-packs/packages/schematics/src/commands/api/files-service/proxy/__namespace@dir__/__name@kebab__.service.ts.template
  2. 7
      npm/ng-packs/packages/schematics/src/models/api-definition.ts
  3. 2
      npm/ng-packs/packages/schematics/src/models/method.ts
  4. 543
      npm/ng-packs/packages/schematics/src/tests/action-to-body-mapper.spec.ts
  5. 179
      npm/ng-packs/packages/schematics/src/tests/proxy-service-template-render.spec.ts
  6. 103
      npm/ng-packs/packages/schematics/src/utils/service.ts

10
npm/ng-packs/packages/schematics/src/commands/api/files-service/proxy/__namespace@dir__/__name@kebab__.service.ts.template

@ -11,15 +11,17 @@ export class <%= name %>Service {
<%
const isBlob = body.isBlobMethod() ;
const responseType = isBlob ? "Blob":body.responseType;
const httpResponseType = body.httpResponseType;
const acceptHeader = body.acceptHeader;
%>
<%= camel(signature.name) %> = (<%= serializeParameters(signature.parameters) %>) =>
this.restService.request<<%= body.requestType %>, <%= responseType %>>({
method: '<%= body.method %>',<%
if (body.responseType === 'string') { %>
responseType: 'text',<% } %><%
if (isBlob) { %>
responseType: 'blob',<% } %>
if (httpResponseType && httpResponseType !== 'json') { %>
responseType: '<%= httpResponseType %>',<% } %><%
if (acceptHeader) { %>
headers: { Accept: '<%= acceptHeader %>' },<% } %>
url: <%= body.url %>,<%
if (body.dictParamVar && !body.params.length) { %>
params: <%= body.dictParamVar %>,<% } %><%

7
npm/ng-packs/packages/schematics/src/models/api-definition.ts

@ -68,7 +68,7 @@ export interface Action {
supportedVersions: string[];
parametersOnMethod: ParameterInSignature[];
parameters: ParameterInBody[];
returnValue: TypeDef;
returnValue: ReturnValueDef;
}
export interface ParameterInSignature {
@ -100,6 +100,11 @@ export interface TypeDef {
typeSimple: string;
}
export interface ReturnValueDef extends TypeDef {
contentTypes?: string[];
isRemoteStream?: boolean;
}
export interface TypeWithEnum {
isEnum: boolean;
type: string;

2
npm/ng-packs/packages/schematics/src/models/method.ts

@ -45,6 +45,8 @@ export class Body {
responseTypeWithNamespace: string;
requestType = 'any';
responseType: string;
httpResponseType?: 'json' | 'text' | 'blob' | 'arraybuffer';
acceptHeader?: string;
url: string;
registerActionParameter = (param: ParameterInBody) => {

543
npm/ng-packs/packages/schematics/src/tests/action-to-body-mapper.spec.ts

@ -0,0 +1,543 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { describe, expect, test } from 'vitest';
import { eBindingSourceId } from '../enums';
import { Action } from '../models';
import { createActionToBodyMapper } from '../utils/service';
const TEMPLATE_PATH = join(
__dirname,
'..',
'commands',
'api',
'files-service',
'proxy',
'__namespace@dir__',
'__name@kebab__.service.ts.template',
);
function buildAction(overrides: Partial<Action>): Action {
return {
uniqueName: 'GetStatusAsync',
name: 'GetStatus',
httpMethod: 'GET',
url: 'api/app/test-service/status',
supportedVersions: [],
parametersOnMethod: [],
parameters: [],
returnValue: { type: 'System.String', typeSimple: 'string' },
...overrides,
} as Action;
}
describe('createActionToBodyMapper — string return value', () => {
const mapBody = createActionToBodyMapper();
test('without contentTypes falls back to text mode (legacy behavior)', () => {
const body = mapBody(buildAction({}));
expect(body.responseType).toBe('string');
expect(body.httpResponseType).toBe('text');
expect(body.acceptHeader).toBeUndefined();
});
test('with contentTypes containing application/json picks json + Accept: application/json', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.String',
typeSimple: 'string',
contentTypes: ['application/json', 'text/plain'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('json');
expect(body.acceptHeader).toBe('application/json');
});
test('with only text/* contentTypes picks text + Accept: text/plain', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.String',
typeSimple: 'string',
contentTypes: ['text/plain', 'text/csv'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('text');
expect(body.acceptHeader).toBe('text/plain');
});
});
describe('createActionToBodyMapper — IRemoteStreamContent return value', () => {
const mapBody = createActionToBodyMapper();
test('always picks blob + Accept: application/octet-stream regardless of contentTypes', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'Volo.Abp.Content.IRemoteStreamContent',
typeSimple: 'Volo.Abp.Content.IRemoteStreamContent',
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('blob');
expect(body.acceptHeader).toBe('application/octet-stream');
expect(body.isBlobMethod()).toBe(true);
});
test('binary-only contentTypes also picks blob + octet-stream', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.Byte[]',
typeSimple: 'byte[]',
contentTypes: ['application/pdf'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('blob');
expect(body.acceptHeader).toBe('application/octet-stream');
});
});
describe('createActionToBodyMapper — other return values', () => {
const mapBody = createActionToBodyMapper();
test('object return without contentTypes has no httpResponseType / acceptHeader (defaults to json)', () => {
const body = mapBody(
buildAction({
returnValue: { type: 'My.Project.UserDto', typeSimple: 'My.Project.UserDto' },
} as Partial<Action>),
);
expect(body.httpResponseType).toBeUndefined();
expect(body.acceptHeader).toBeUndefined();
});
test('void return has no httpResponseType / acceptHeader', () => {
const body = mapBody(
buildAction({
returnValue: { type: 'System.Void', typeSimple: 'void' },
} as Partial<Action>),
);
expect(body.httpResponseType).toBeUndefined();
expect(body.acceptHeader).toBeUndefined();
});
test('registers a query parameter via the binding source', () => {
const body = mapBody(
buildAction({
parameters: [
{
nameOnMethod: 'id',
name: 'id',
jsonName: null,
type: 'System.Guid',
typeSimple: 'string',
isOptional: false,
defaultValue: null,
constraintTypes: null,
bindingSourceId: eBindingSourceId.Query,
descriptorName: '',
},
],
}),
);
expect(body.params).toEqual(['id']);
});
});
describe('createActionToBodyMapper — IsRemoteStream backend flag', () => {
const mapBody = createActionToBodyMapper();
test('isRemoteStream=true forces blob even if Type is a custom subclass name', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'My.Project.CustomStreamContent',
typeSimple: 'My.Project.CustomStreamContent',
isRemoteStream: true,
contentTypes: ['text/plain', 'application/json'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('blob');
expect(body.acceptHeader).toBe('application/octet-stream');
});
test('isRemoteStream=false with stream-content type-name still detected by type name (legacy)', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'Volo.Abp.Content.IRemoteStreamContent',
typeSimple: 'Volo.Abp.Content.IRemoteStreamContent',
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('blob');
expect(body.acceptHeader).toBe('application/octet-stream');
});
});
describe('createActionToBodyMapper — +json suffix detection', () => {
const mapBody = createActionToBodyMapper();
test('application/problem+json picked as json', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.String',
typeSimple: 'string',
contentTypes: ['application/problem+json'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('json');
expect(body.acceptHeader).toBe('application/json');
});
test('text/json picked as json (informal but real-world)', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.String',
typeSimple: 'string',
contentTypes: ['text/json'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('json');
expect(body.acceptHeader).toBe('application/json');
});
test('application/vnd.api+json picked as json', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.String',
typeSimple: 'string',
contentTypes: ['application/vnd.api+json'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('json');
});
});
describe('createActionToBodyMapper — expanded binary whitelist', () => {
const mapBody = createActionToBodyMapper();
test.each([
['application/wasm'],
['font/woff2'],
['application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
['application/vnd.ms-excel'],
['application/vnd.oasis.opendocument.spreadsheet'],
['application/x-msdownload'],
['application/rtf'],
['application/x-rar-compressed'],
['application/x-bzip2'],
['application/x-iso9660-image'],
['application/java-archive'],
['application/epub+zip'],
['model/gltf-binary'],
])('"%s" picked as blob', mediaType => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.Byte[]',
typeSimple: 'byte[]',
contentTypes: [mediaType],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('blob');
});
});
describe('createActionToBodyMapper — contentTypes precedence and edge cases', () => {
const mapBody = createActionToBodyMapper();
test('isBlobMethod() type detection wins over json in contentTypes', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'Volo.Abp.Content.RemoteStreamContent',
typeSimple: 'Volo.Abp.Content.RemoteStreamContent',
contentTypes: ['application/json', 'text/plain'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('blob');
expect(body.acceptHeader).toBe('application/octet-stream');
});
test('IRemoteStreamContent[] array also picked as blob', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'Volo.Abp.Content.IRemoteStreamContent[]',
typeSimple: 'Volo.Abp.Content.IRemoteStreamContent[]',
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('blob');
expect(body.acceptHeader).toBe('application/octet-stream');
});
test('case-insensitive json detection (APPLICATION/JSON)', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.String',
typeSimple: 'string',
contentTypes: ['APPLICATION/JSON'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('json');
expect(body.acceptHeader).toBe('application/json');
});
test('image/* contentTypes alone picks blob', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.Byte[]',
typeSimple: 'byte[]',
contentTypes: ['image/png', 'image/jpeg'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('blob');
expect(body.acceptHeader).toBe('application/octet-stream');
});
test('video/* and audio/* picked as blob', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.Byte[]',
typeSimple: 'byte[]',
contentTypes: ['video/mp4', 'audio/mpeg'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('blob');
});
test('application/pdf contentTypes picked as blob', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.Byte[]',
typeSimple: 'byte[]',
contentTypes: ['application/pdf'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('blob');
});
test('empty contentTypes falls through to legacy string→text behavior', () => {
const body = mapBody(
buildAction({
returnValue: { type: 'System.String', typeSimple: 'string', contentTypes: [] },
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('text');
expect(body.acceptHeader).toBeUndefined();
});
test('mixed text/* and application/json picks json', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.String',
typeSimple: 'string',
contentTypes: ['text/json', 'text/plain', 'application/json'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('json');
expect(body.acceptHeader).toBe('application/json');
});
test('contentTypes with json-suffix variants still picks json', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.String',
typeSimple: 'string',
contentTypes: ['application/json; charset=utf-8'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('json');
});
test('non-string non-blob type with contentTypes containing json defaults appropriately', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'My.Project.UserDto',
typeSimple: 'My.Project.UserDto',
contentTypes: ['application/json'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('json');
expect(body.acceptHeader).toBe('application/json');
});
test('json contentType with charset parameter is normalized', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.String',
typeSimple: 'string',
contentTypes: ['application/json; charset=utf-8'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('json');
expect(body.acceptHeader).toBe('application/json');
});
test('text contentType with charset parameter is normalized', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.String',
typeSimple: 'string',
contentTypes: ['text/plain ; charset=utf-8 '],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('text');
expect(body.acceptHeader).toBe('text/plain');
});
test('mixed text/plain (with charset) and application/json picks json after normalize', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.String',
typeSimple: 'string',
contentTypes: ['text/plain; charset=utf-8', 'application/json; charset=utf-8'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('json');
});
test('text/csv only (custom text format) picks text', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'System.String',
typeSimple: 'string',
contentTypes: ['text/csv'],
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('text');
expect(body.acceptHeader).toBe('text/plain');
});
});
describe('createActionToBodyMapper — backward compatibility', () => {
const mapBody = createActionToBodyMapper();
test('legacy api-definition without contentTypes works (string)', () => {
const body = mapBody(
buildAction({
returnValue: { type: 'System.String', typeSimple: 'string' } as Partial<Action>['returnValue'],
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('text');
expect(body.acceptHeader).toBeUndefined();
});
test('legacy api-definition without contentTypes works (IRemoteStreamContent)', () => {
const body = mapBody(
buildAction({
returnValue: {
type: 'Volo.Abp.Content.IRemoteStreamContent',
typeSimple: 'Volo.Abp.Content.IRemoteStreamContent',
},
} as Partial<Action>),
);
expect(body.httpResponseType).toBe('blob');
expect(body.acceptHeader).toBe('application/octet-stream');
});
test('legacy api-definition without contentTypes works (object)', () => {
const body = mapBody(
buildAction({
returnValue: { type: 'My.Project.UserDto', typeSimple: 'My.Project.UserDto' },
} as Partial<Action>),
);
expect(body.httpResponseType).toBeUndefined();
expect(body.acceptHeader).toBeUndefined();
});
test('void / no return preserves legacy behavior', () => {
const body = mapBody(
buildAction({
returnValue: { type: 'System.Void', typeSimple: 'void' },
} as Partial<Action>),
);
expect(body.httpResponseType).toBeUndefined();
expect(body.acceptHeader).toBeUndefined();
});
});
describe('proxy service template emission', () => {
const template = readFileSync(TEMPLATE_PATH, 'utf8');
test('reads body.httpResponseType and body.acceptHeader from body', () => {
expect(template).toContain('body.httpResponseType');
expect(template).toContain('body.acceptHeader');
});
test('emits Accept header conditional', () => {
expect(template).toMatch(/headers:\s*\{\s*Accept:/);
});
test('emits responseType only for non-json httpResponseType', () => {
expect(template).toMatch(/httpResponseType\s*&&\s*httpResponseType\s*!==\s*'json'/);
});
});

179
npm/ng-packs/packages/schematics/src/tests/proxy-service-template-render.spec.ts

@ -0,0 +1,179 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { template as lodashTemplate } from 'lodash';
import { describe, expect, test } from 'vitest';
/**
* Smoke test that actually renders the proxy `.service.ts.template` against
* representative body configurations and asserts the emitted code matches
* what the runtime contract requires.
*
* This catches template-syntax / control-flow regressions that a string
* `toContain` check on the template source would silently let through.
*/
const TEMPLATE_PATH = join(
__dirname,
'..',
'commands',
'api',
'files-service',
'proxy',
'__namespace@dir__',
'__name@kebab__.service.ts.template',
);
const TEMPLATE_SRC = readFileSync(TEMPLATE_PATH, 'utf8');
function render(context: Record<string, unknown>): string {
const compiled = lodashTemplate(TEMPLATE_SRC, {
imports: {
camel: (s: string) => s.charAt(0).toLowerCase() + s.slice(1),
serializeParameters: (params: Array<{ name: string; type: string; default?: string }>) =>
params.map(p => `${p.name}: ${p.type}`).join(', '),
},
});
return compiled(context);
}
function buildContext(body: Partial<MockBody>) {
return {
apiName: 'Default',
name: 'Sample',
namespace: 'app',
imports: [
{ keyword: 'import', specifiers: ['RestService', 'Rest'], path: '@abp/ng.core' },
{ keyword: 'import', specifiers: ['Injectable', 'inject'], path: '@angular/core' },
],
methods: [
{
body: makeBody(body),
signature: {
name: 'GetSampleAsync',
parameters: [],
},
},
],
};
}
interface MockBody {
method: string;
url: string;
responseType: string;
responseTypeWithNamespace: string;
httpResponseType?: string;
acceptHeader?: string;
body?: string;
params: string[];
dictParamVar?: string;
requestType: string;
isBlobMethod(): boolean;
}
function makeBody(overrides: Partial<MockBody>): MockBody {
return {
method: 'GET',
url: "'/api/sample'",
responseType: 'any',
responseTypeWithNamespace: 'any',
httpResponseType: undefined,
acceptHeader: undefined,
body: undefined,
params: [],
dictParamVar: undefined,
requestType: 'any',
isBlobMethod: () => false,
...overrides,
};
}
describe('proxy service template — rendered output', () => {
test('default JSON body emits no responseType and no Accept header', () => {
const output = render(buildContext({
responseType: 'MyDto',
responseTypeWithNamespace: 'My.Project.MyDto',
}));
expect(output).toContain("method: 'GET'");
expect(output).not.toContain('responseType:');
expect(output).not.toContain('headers:');
});
test('json httpResponseType emits Accept but no responseType (default is json)', () => {
const output = render(buildContext({
responseType: 'string',
responseTypeWithNamespace: 'string',
httpResponseType: 'json',
acceptHeader: 'application/json',
}));
expect(output).toContain("headers: { Accept: 'application/json' }");
expect(output).not.toContain('responseType:');
});
test('text httpResponseType emits both responseType and Accept', () => {
const output = render(buildContext({
responseType: 'string',
responseTypeWithNamespace: 'string',
httpResponseType: 'text',
acceptHeader: 'text/plain',
}));
expect(output).toContain("responseType: 'text'");
expect(output).toContain("headers: { Accept: 'text/plain' }");
});
test('blob (IRemoteStreamContent) emits Blob return type + responseType + Accept', () => {
const output = render(buildContext({
responseType: 'Volo.Abp.Content.IRemoteStreamContent',
responseTypeWithNamespace: 'Volo.Abp.Content.IRemoteStreamContent',
isBlobMethod: () => true,
httpResponseType: 'blob',
acceptHeader: 'application/octet-stream',
}));
expect(output).toContain("responseType: 'blob'");
expect(output).toContain('Blob>');
expect(output).toContain("headers: { Accept: 'application/octet-stream' }");
});
test('arraybuffer httpResponseType emits responseType', () => {
const output = render(buildContext({
responseType: 'ArrayBuffer',
responseTypeWithNamespace: 'ArrayBuffer',
httpResponseType: 'arraybuffer',
acceptHeader: 'application/octet-stream',
}));
expect(output).toContain("responseType: 'arraybuffer'");
expect(output).toContain("headers: { Accept: 'application/octet-stream' }");
});
test('no acceptHeader → no headers line', () => {
const output = render(buildContext({
responseType: 'string',
responseTypeWithNamespace: 'string',
httpResponseType: 'text',
acceptHeader: undefined,
}));
expect(output).toContain("responseType: 'text'");
expect(output).not.toContain('headers:');
});
test('rendered service code is valid TypeScript-shaped (closing braces / semicolons)', () => {
const output = render(buildContext({
responseType: 'string',
responseTypeWithNamespace: 'string',
httpResponseType: 'json',
acceptHeader: 'application/json',
}));
expect(output).toContain('@Injectable({');
expect(output).toContain('providedIn: \'root\'');
expect(output).toContain('export class SampleService');
expect(output).toContain('this.restService.request<any,');
expect(output.match(/}/g)!.length).toBeGreaterThanOrEqual(3);
});
});

103
npm/ng-packs/packages/schematics/src/utils/service.ts

@ -97,7 +97,20 @@ export function createActionToBodyMapper() {
}
}
const responseTypeWithNamespace = returnValue.typeSimple;
const body = new Body({ method: httpMethod, responseType, url, responseTypeWithNamespace });
const { httpResponseType, acceptHeader } = resolveHttpResponseAndAccept(
responseType,
responseTypeWithNamespace,
returnValue.contentTypes,
returnValue.isRemoteStream,
);
const body = new Body({
method: httpMethod,
responseType,
url,
responseTypeWithNamespace,
httpResponseType,
acceptHeader,
});
parameters.forEach(body.registerActionParameter);
@ -105,6 +118,94 @@ export function createActionToBodyMapper() {
};
}
function normalizeMediaType(mediaType: string): string {
const semi = mediaType.indexOf(';');
return (semi < 0 ? mediaType : mediaType.slice(0, semi)).trim().toLowerCase();
}
function isJsonMediaType(normalized: string): boolean {
return normalized === 'application/json' || normalized === 'text/json' || normalized.endsWith('+json');
}
function isBinaryMediaType(mediaType: string): boolean {
const m = normalizeMediaType(mediaType);
if (
m === 'application/octet-stream' ||
m === 'application/pdf' ||
m === 'application/zip' ||
m === 'application/x-zip-compressed' ||
m === 'application/gzip' ||
m === 'application/x-tar' ||
m === 'application/x-7z-compressed' ||
m === 'application/wasm' ||
m === 'application/x-msdownload' ||
m === 'application/x-msdos-program' ||
m === 'application/rtf' ||
m === 'application/x-rar-compressed' ||
m === 'application/x-rar' ||
m === 'application/x-bzip2' ||
m === 'application/x-iso9660-image' ||
m === 'application/x-apple-diskimage' ||
m === 'application/java-archive' ||
m === 'application/epub+zip' ||
m === 'model/gltf-binary'
) {
return true;
}
if (m.startsWith('image/') || m.startsWith('video/') || m.startsWith('audio/') || m.startsWith('font/')) {
return true;
}
// Office / OpenDocument / generic vnd.* (excluding +json / +xml which are structured text)
if (
m.startsWith('application/vnd.openxmlformats-') ||
m.startsWith('application/vnd.ms-') ||
m.startsWith('application/vnd.oasis.opendocument.')
) {
return true;
}
if (m.startsWith('application/vnd.') && !m.endsWith('+json') && !m.endsWith('+xml')) {
return true;
}
return false;
}
function resolveHttpResponseAndAccept(
responseType: string,
responseTypeWithNamespace: string,
contentTypes: string[] | undefined,
isRemoteStreamFlag: boolean | undefined,
): { httpResponseType?: 'json' | 'text' | 'blob' | 'arraybuffer'; acceptHeader?: string } {
if (
isRemoteStreamFlag ||
isRemoteStreamContent(responseTypeWithNamespace) ||
isRemoteStreamContentArray(responseTypeWithNamespace)
) {
return { httpResponseType: 'blob', acceptHeader: 'application/octet-stream' };
}
if (contentTypes && contentTypes.length > 0) {
const normalized = contentTypes.map(normalizeMediaType);
if (normalized.some(isJsonMediaType)) {
return { httpResponseType: 'json', acceptHeader: 'application/json' };
}
if (normalized.every(ct => ct.startsWith('text/'))) {
return { httpResponseType: 'text', acceptHeader: 'text/plain' };
}
if (normalized.every(isBinaryMediaType)) {
return { httpResponseType: 'blob', acceptHeader: 'application/octet-stream' };
}
}
if (responseType === 'string') {
return { httpResponseType: 'text' };
}
return {};
}
export function createActionToSignatureMapper() {
const adaptType = createTypeAdapter();

Loading…
Cancel
Save