Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RestError to have details that point to swagger specified error shape #5437

Merged
merged 10 commits into from
Oct 19, 2019
10 changes: 10 additions & 0 deletions sdk/core/core-http/lib/policies/deserializationPolicy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,18 @@ export function deserializeResponseBody(
valueToDeserialize,
"error.body"
);
// Setting the parsedBody on response to enable flattening as per operationSpec
error.response!.parsedBody = error.body;
}
}

if (parsedResponse.headers && defaultResponseSpec.headersMapper) {
error.response!.parsedHeaders = operationSpec.serializer.deserialize(
defaultResponseSpec.headersMapper,
parsedResponse.headers.rawHeaders(),
"operationRes.parsedHeaders"
);
}
} catch (defaultError) {
error.message = `Error \"${defaultError.message}\" occurred in deserializing the responseBody - \"${parsedResponse.bodyAsText}\" for the default response.`;
}
Expand Down
1 change: 1 addition & 0 deletions sdk/core/core-http/lib/restError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export class RestError extends Error {
request?: WebResource;
response?: HttpOperationResponse;
body?: any;
details?: unknown;
constructor(
message: string,
code?: string,
Expand Down
28 changes: 24 additions & 4 deletions sdk/core/core-http/lib/serviceClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ export class ServiceClient {
* @param {OperationSpec} operationSpec The OperationSpec to use to populate the httpRequest.
* @param {ServiceCallback} callback The callback to call when the response is received.
*/
sendOperationRequest(
async sendOperationRequest(
operationArguments: OperationArguments,
operationSpec: OperationSpec,
callback?: ServiceCallback<any>
Expand Down Expand Up @@ -431,9 +431,29 @@ export class ServiceClient {
httpRequest.streamResponseBody = isStreamOperation(operationSpec);
}

result = this.sendRequest(httpRequest).then((res) =>
flattenResponse(res, operationSpec.responses[res.status])
);
let rawResponse: HttpOperationResponse;
let sendRequestError;
try {
rawResponse = await this.sendRequest(httpRequest);
} catch (error) {
sendRequestError = error;
}
if (sendRequestError) {
if (sendRequestError.response){
sendRequestError.details = flattenResponse(
sendRequestError.response,
operationSpec.responses[sendRequestError.statusCode] ||
operationSpec.responses["default"]
);
}
result = Promise.reject(
sendRequestError
);
} else {
result = Promise.resolve(
flattenResponse(rawResponse!, operationSpec.responses[rawResponse!.status])
);
}
} catch (error) {
result = Promise.reject(error);
}
Expand Down
66 changes: 65 additions & 1 deletion sdk/core/core-http/test/policies/deserializationPolicyTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { assert } from "chai";
import { HttpHeaders } from "../../lib/httpHeaders";
import { HttpOperationResponse } from "../../lib/httpOperationResponse";
import { HttpClient, OperationSpec, Serializer } from "../../lib/coreHttp";
import { HttpClient, OperationSpec, Serializer, CompositeMapper } from "../../lib/coreHttp";
import {
DeserializationPolicy,
deserializationPolicy,
Expand Down Expand Up @@ -501,6 +501,70 @@ describe("deserializationPolicy", function() {
});
assert.strictEqual(deserializedResponse.parsedHeaders, undefined);
});

it(`with default response headers`, async function() {
const BodyMapper: CompositeMapper = {
serializedName: "getproperties-body",
type: {
name: "Composite",
className: "PropertiesBody",
modelProperties: {
message: {
type: {
name: "String"
}
}
}
}
};

const HeadersMapper: CompositeMapper = {
serializedName: "getproperties-headers",
type: {
name: "Composite",
className: "PropertiesHeaders",
modelProperties: {
errorCode: {
serializedName: "x-ms-error-code",
type: {
name: "String"
}
}
}
}
};

const serializer = new Serializer(HeadersMapper, true);

const operationSpec: OperationSpec = {
httpMethod: "GET",
responses: {
default: {
headersMapper: HeadersMapper,
bodyMapper: BodyMapper
}
},
serializer
};

const response: HttpOperationResponse = {
request: createRequest(operationSpec),
status: 500,
headers: new HttpHeaders({
"x-ms-error-code": "InvalidResourceNameHeader"
}),
bodyAsText: '{"message": "InvalidResourceNameBody"}'
};

try {
await deserializeResponse(response);
assert.fail();
} catch (e) {
assert(e);
assert.strictEqual(e.response.parsedHeaders.errorCode, "InvalidResourceNameHeader");
assert.strictEqual(e.response.parsedBody.message, "InvalidResourceNameBody");
}
});
});
});

Expand Down
88 changes: 86 additions & 2 deletions sdk/core/core-http/test/serviceClientTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@
import { assert } from "chai";
import { HttpClient } from "../lib/httpClient";
import { QueryCollectionFormat } from "../lib/queryCollectionFormat";
import { DictionaryMapper, MapperType, Serializer, Mapper } from "../lib/serializer";
import {
DictionaryMapper,
MapperType,
Serializer,
Mapper,
CompositeMapper
} from "../lib/serializer";
import {
serializeRequestBody,
ServiceClient,
Expand All @@ -16,7 +22,8 @@ import {
HttpHeaders,
deserializationPolicy,
RestResponse,
isNode
isNode,
OperationSpec
} from "../lib/coreHttp";
import { ParameterPath } from "../lib/operationParameter";

Expand Down Expand Up @@ -961,6 +968,83 @@ describe("ServiceClient", function() {
assert.strictEqual(parameterValue, 5);
});
});

it("should deserialize error response headers", async function() {
const BodyMapper: CompositeMapper = {
serializedName: "getproperties-body",
type: {
name: "Composite",
className: "PropertiesBody",
modelProperties: {
message: {
type: {
name: "String"
}
}
}
}
};

const HeadersMapper: CompositeMapper = {
serializedName: "getproperties-headers",
type: {
name: "Composite",
className: "PropertiesHeaders",
modelProperties: {
errorCode: {
serializedName: "x-ms-error-code",
type: {
name: "String"
}
}
}
}
};

const serializer = new Serializer(HeadersMapper, true);

const operationSpec: OperationSpec = {
httpMethod: "GET",
responses: {
default: {
headersMapper: HeadersMapper,
bodyMapper: BodyMapper
}
},
baseUrl: "httpbin.org",
serializer
};

let request = new WebResource();
request.operationSpec = operationSpec;

const httpClient: HttpClient = {
sendRequest: (req) => {
request = req;
return Promise.resolve({
request,
status: 500,
headers: new HttpHeaders({
"x-ms-error-code": "InvalidResourceNameHeader"
}),
bodyAsText: '{"message": "InvalidResourceNameBody"}'
});
}
};

const client = new ServiceClient(undefined, {
httpClient,
requestPolicyFactories: [deserializationPolicy()]
});

try {
await client.sendOperationRequest({}, operationSpec);
assert.fail();
} catch (ex) {
assert.strictEqual(ex.details.errorCode, "InvalidResourceNameHeader");
assert.strictEqual(ex.details.message, "InvalidResourceNameBody");
}
});
});

function stringToByteArray(str: string): Uint8Array {
Expand Down