Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sdk/core/core-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"test:node": "npm run build:test:node && npm run unit-test:node && npm run integration-test:node",
"test": "npm run clean && npm run build:ts && npm run bundle:test:node && npm run unit-test:node && npm run bundle:test:browser && npm run unit-test:browser && npm run integration-test:node && npm run integration-test:browser",
"unit-test:browser": "karma start --single-run",
"unit-test:node": "mocha --require source-map-support/register --reporter ../../../common/tools/mocha-multi-reporter.js dist-test/index.node.js",
"unit-test:node": "cross-env TS_NODE_FILES=true mocha -r esm -r ts-node/register --timeout 50000 --reporter ../../../common/tools/mocha-multi-reporter.js --colors --exclude \"test/**/*.browser.ts\" \"test/**/*.ts\"",
"unit-test": "npm run unit-test:node && npm run unit-test:browser"
},
"files": [
Expand Down
36 changes: 25 additions & 11 deletions sdk/core/core-client/review/core-client.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,8 @@ export const deserializationPolicyName = "deserializationPolicy";
// @public
export interface DeserializationPolicyOptions {
expectedContentTypes?: DeserializationContentTypes;
parseXML?: (str: string, opts?: {
includeRoot?: boolean;
}) => Promise<any>;
parseXML?: (str: string, opts?: XmlOptions) => Promise<any>;
serializerOptions?: SerializerOptions;
}

// @public (undocumented)
Expand Down Expand Up @@ -203,6 +202,7 @@ export interface OperationArguments {
export interface OperationOptions {
abortSignal?: AbortSignalLike;
requestOptions?: OperationRequestOptions;
serializerOptions?: SerializerOptions;
tracingOptions?: OperationTracingOptions;
}

Expand Down Expand Up @@ -312,19 +312,24 @@ export interface SequenceMapperType {
// @public
export interface Serializer {
// (undocumented)
deserialize(mapper: Mapper, responseBody: any, objectName: string): any;
deserialize(mapper: Mapper, responseBody: any, objectName: string, options?: SerializerOptions): any;
// (undocumented)
readonly isXML: boolean;
// (undocumented)
readonly modelMappers: {
[key: string]: any;
};
// (undocumented)
serialize(mapper: Mapper, object: any, objectName?: string): any;
serialize(mapper: Mapper, object: any, objectName?: string, options?: SerializerOptions): any;
// (undocumented)
validateConstraints(mapper: Mapper, value: any, objectName: string): void;
}

// @public
export interface SerializerOptions {
xml: XmlOptions;
}

// @public
export class ServiceClient {
constructor(options?: ServiceClientOptions);
Expand All @@ -337,14 +342,10 @@ export interface ServiceClientOptions {
baseUri?: string;
credential?: TokenCredential;
httpsClient?: HttpsClient;
parseXML?: (str: string, opts?: {
includeRoot?: boolean;
}) => Promise<any>;
parseXML?: (str: string, opts?: XmlOptions) => Promise<any>;
pipeline?: Pipeline;
requestContentType?: string;
stringifyXML?: (obj: any, opts?: {
rootName?: string;
}) => string;
stringifyXML?: (obj: any, opts?: XmlOptions) => string;
}

// @public (undocumented)
Expand All @@ -359,6 +360,19 @@ export interface SpanConfig {
packagePrefix: string;
}

// @public
export const XML_ATTRKEY = "$";

// @public
export const XML_CHARKEY = "_";

// @public
export interface XmlOptions {
includeRoot?: boolean;
rootName?: string;
xmlCharKey?: string;
}


// (No @packageDocumentation comment for this package)

Expand Down
45 changes: 38 additions & 7 deletions sdk/core/core-client/src/deserializationPolicy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import {
OperationRequest,
OperationResponseMap,
FullOperationResponse,
OperationSpec
OperationSpec,
SerializerOptions,
XmlOptions,
XML_CHARKEY,
RequiredSerializerOptions
} from "./interfaces";
import { MapperTypeNames } from "./serializer";
import { isStreamOperation } from "./interfaceHelpers";
Expand All @@ -38,7 +42,12 @@ export interface DeserializationPolicyOptions {
/**
* A function that is able to parse XML. Required for XML support.
*/
parseXML?: (str: string, opts?: { includeRoot?: boolean }) => Promise<any>;
parseXML?: (str: string, opts?: XmlOptions) => Promise<any>;

/**
* Configures behavior of xml parser and builder.
*/
serializerOptions?: SerializerOptions;
}

/**
Expand Down Expand Up @@ -66,12 +75,26 @@ export function deserializationPolicy(options: DeserializationPolicyOptions = {}
const jsonContentTypes = options.expectedContentTypes?.json ?? defaultJsonContentTypes;
const xmlContentTypes = options.expectedContentTypes?.xml ?? defaultXmlContentTypes;
const parseXML = options.parseXML;
const serializerOptions = options.serializerOptions;
const updatedOptions: RequiredSerializerOptions = {
xml: {
rootName: serializerOptions?.xml.rootName ?? "",
includeRoot: serializerOptions?.xml.includeRoot ?? false,
xmlCharKey: serializerOptions?.xml.xmlCharKey ?? XML_CHARKEY
}
};

return {
name: deserializationPolicyName,
async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {
const response = await next(request);
return deserializeResponseBody(jsonContentTypes, xmlContentTypes, response, parseXML);
return deserializeResponseBody(
jsonContentTypes,
xmlContentTypes,
response,
updatedOptions,
parseXML
);
}
};
}
Expand Down Expand Up @@ -110,9 +133,16 @@ async function deserializeResponseBody(
jsonContentTypes: string[],
xmlContentTypes: string[],
response: PipelineResponse,
parseXML?: (str: string, opts?: { includeRoot?: boolean }) => Promise<any>
options: RequiredSerializerOptions,
parseXML?: (str: string, opts?: XmlOptions) => Promise<any>
): Promise<PipelineResponse> {
const parsedResponse = await parse(jsonContentTypes, xmlContentTypes, response, parseXML);
const parsedResponse = await parse(
jsonContentTypes,
xmlContentTypes,
response,
options,
parseXML
);
if (!shouldDeserializeResponse(parsedResponse)) {
return parsedResponse;
}
Expand Down Expand Up @@ -281,7 +311,8 @@ async function parse(
jsonContentTypes: string[],
xmlContentTypes: string[],
operationResponse: FullOperationResponse,
parseXML?: (str: string, opts?: { includeRoot?: boolean }) => Promise<any>
opts: RequiredSerializerOptions,
parseXML?: (str: string, opts?: XmlOptions) => Promise<any>
): Promise<FullOperationResponse> {
if (!operationResponse.request.streamResponseBody && operationResponse.bodyAsText) {
const text = operationResponse.bodyAsText;
Expand All @@ -301,7 +332,7 @@ async function parse(
if (!parseXML) {
throw new Error("Parsing XML not supported.");
}
const body = await parseXML(text);
const body = await parseXML(text, opts.xml);
operationResponse.parsedBody = body;
return operationResponse;
}
Expand Down
6 changes: 5 additions & 1 deletion sdk/core/core-client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ export {
OperationResponse,
FullOperationResponse,
PolymorphicDiscriminator,
SpanConfig
SpanConfig,
XML_ATTRKEY,
XML_CHARKEY,
XmlOptions,
SerializerOptions
} from "./interfaces";
export {
deserializationPolicy,
Expand Down
64 changes: 56 additions & 8 deletions sdk/core/core-client/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,45 @@ import {
PipelineRequest
} from "@azure/core-https";

/**
* Default key used to access the XML attributes.
*/
export const XML_ATTRKEY = "$";
/**
* Default key used to access the XML value content.
*/
export const XML_CHARKEY = "_";
/**
* Options to govern behavior of xml parser and builder.
*/
export interface XmlOptions {
/**
* indicates the name of the root element in the resulting XML when building XML.
*/
rootName?: string;
/**
* indicates whether the root element is to be included or not in the output when parsing XML.
*/
includeRoot?: boolean;
/**
* key used to access the XML value content when parsing XML.
*/
xmlCharKey?: string;
}
/**
* Options to configure serialization/de-serialization behavior.
*/
export interface SerializerOptions {
/**
* Options to configure xml parser/builder behavior.
*/
xml: XmlOptions;
}

export type RequiredSerializerOptions = {
[K in keyof SerializerOptions]: Required<SerializerOptions[K]>;
};

/**
* This interface extends a generic `PipelineRequest` to include
* additional metadata about the request.
Expand Down Expand Up @@ -57,6 +96,10 @@ export interface OperationOptions {
* Options used when tracing is enabled.
*/
tracingOptions?: OperationTracingOptions;
/**
* Options to override serialization/de-serialization behavior.
*/
serializerOptions?: SerializerOptions;
}

/**
Expand Down Expand Up @@ -100,7 +143,7 @@ export interface OperationArguments {
[parameterName: string]: unknown;

/**
* The optional arugments that are provided to an operation.
* The optional arguments that are provided to an operation.
*/
options?: OperationOptions;
}
Expand Down Expand Up @@ -306,8 +349,13 @@ export interface Serializer {
readonly modelMappers: { [key: string]: any };
readonly isXML: boolean;
validateConstraints(mapper: Mapper, value: any, objectName: string): void;
serialize(mapper: Mapper, object: any, objectName?: string): any;
deserialize(mapper: Mapper, responseBody: any, objectName: string): any;
serialize(mapper: Mapper, object: any, objectName?: string, options?: SerializerOptions): any;
deserialize(
mapper: Mapper,
responseBody: any,
objectName: string,
options?: SerializerOptions
): any;
}

export interface MapperConstraints {
Expand Down Expand Up @@ -401,23 +449,23 @@ export interface BaseMapper {
*/
xmlElementName?: string;
/**
* Whether or not the current propery should have a wrapping XML element
* Whether or not the current property should have a wrapping XML element
*/
xmlIsWrapped?: boolean;
/**
* Whether or not the current propery is readonly
* Whether or not the current property is readonly
*/
readOnly?: boolean;
/**
* Whether or not the current propery is a constant
* Whether or not the current property is a constant
*/
isConstant?: boolean;
/**
* Whether or not the current propery is required
* Whether or not the current property is required
*/
required?: boolean;
/**
* Whether or not the current propery allows mull as a value
* Whether or not the current property allows mull as a value
*/
nullable?: boolean;
/**
Expand Down
Loading