azurite
Version:
An open source Azure Storage API compatible server
254 lines • 12.6 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.serialize = exports.deserialize = void 0;
const tslib_1 = require("tslib");
const msRest = tslib_1.__importStar(require("@azure/ms-rest-js"));
const Mappers = tslib_1.__importStar(require("../artifacts/mappers"));
const xml_1 = require("./xml");
async function deserialize(context, req, spec, logger) {
const parameters = {};
// Deserialize query parameters
for (const queryParameter of spec.queryParameters || []) {
if (!queryParameter.mapper.serializedName) {
throw new TypeError(`QueryParameter mapper doesn't include valid "serializedName"`);
}
const queryKey = queryParameter.mapper.serializedName;
let queryValueOriginal = req.getQuery(queryKey);
if (queryValueOriginal !== undefined &&
queryParameter.collectionFormat !== undefined &&
queryParameter.mapper.type.name === "Sequence") {
queryValueOriginal = `${queryValueOriginal}`.split(queryParameter.collectionFormat);
}
const queryValue = spec.serializer.deserialize(queryParameter.mapper, queryValueOriginal, queryKey);
// TODO: Currently validation is only in serialize method,
// remove when adding validateConstraints to deserialize()
// TODO: Make serialize return ServerError according to different validations?
spec.serializer.serialize(queryParameter.mapper, queryValue);
setParametersValue(parameters, queryParameter.parameterPath, queryValue);
}
// Deserialize header parameters
for (const headerParameter of spec.headerParameters || []) {
if (!headerParameter.mapper.serializedName) {
throw new TypeError(`HeaderParameter mapper doesn't include valid "serializedName"`);
}
const headerCollectionPrefix = headerParameter.mapper
.headerCollectionPrefix;
if (headerCollectionPrefix) {
const dictionary = {};
const headers = req.getHeaders();
for (const headerKey of Object.keys(headers)) {
if (headerKey
.toLowerCase()
.startsWith(headerCollectionPrefix.toLocaleLowerCase())) {
// TODO: Validate collection type by serializer
dictionary[headerKey.substring(headerCollectionPrefix.length)] = spec.serializer.serialize(headerParameter.mapper.type.value, headers[headerKey], headerKey);
}
}
setParametersValue(parameters, headerParameter.parameterPath, dictionary);
}
else {
const headerKey = headerParameter.mapper.serializedName;
const headerValueOriginal = req.getHeader(headerKey);
const headerValue = spec.serializer.deserialize(headerParameter.mapper, headerValueOriginal, headerKey);
// TODO: Currently validation is only in serialize method,
// remove when adding validateConstraints to deserialize()
spec.serializer.serialize(headerParameter.mapper, headerValue);
setParametersValue(parameters, headerParameter.parameterPath, headerValue);
}
}
// Deserialize body
const bodyParameter = spec.requestBody;
if (bodyParameter && bodyParameter.mapper.type.name === "Stream") {
setParametersValue(parameters, "body", req.getBodyStream());
}
else if (bodyParameter) {
const jsonContentTypes = ["application/json", "text/json"];
const xmlContentTypes = ["application/xml", "application/atom+xml"];
const contentType = req.getHeader("content-type") || "";
const contentComponents = !contentType
? []
: contentType.split(";").map(component => component.toLowerCase());
const isRequestWithJSON = contentComponents.some(component => jsonContentTypes.indexOf(component) !== -1); // TODO
const isRequestWithXML = spec.isXML ||
contentComponents.some(component => xmlContentTypes.indexOf(component) !== -1);
// const isRequestWithStream = false;
const body = await readRequestIntoText(req);
logger.debug(`deserialize(): Raw request body string is (removed all empty characters) ${body.replace(/\s/g, "")}`, context.contextID);
req.setBody(body);
let parsedBody = {};
if (isRequestWithJSON) {
// read body
parsedBody = JSON.parse(body);
}
else if (isRequestWithXML) {
parsedBody = (await (0, xml_1.parseXML)(body)) || {};
}
let valueToDeserialize = parsedBody;
if (spec.isXML &&
bodyParameter.mapper.type.name === msRest.MapperType.Sequence) {
valueToDeserialize =
typeof valueToDeserialize === "object"
? valueToDeserialize[bodyParameter.mapper.xmlElementName]
: [];
}
parsedBody = spec.serializer.deserialize(bodyParameter.mapper, valueToDeserialize, bodyParameter.mapper.serializedName);
// Validation purpose only, because only serialize supports validation
// TODO: Inject convenience layer error into deserialize; Drop @azure/ms-rest-js, move logic into generated code
spec.serializer.serialize(bodyParameter.mapper, parsedBody);
setParametersValue(parameters, bodyParameter.parameterPath, parsedBody);
setParametersValue(parameters, "body", req.getBody());
}
return parameters;
}
exports.deserialize = deserialize;
async function readRequestIntoText(req) {
return new Promise((resolve, reject) => {
const segments = [];
const bodyStream = req.getBodyStream();
bodyStream.on("data", buffer => {
segments.push(buffer);
});
bodyStream.on("error", reject);
bodyStream.on("end", () => {
const joined = segments.join("");
resolve(joined);
});
});
}
function setParametersValue(parameters, parameterPath, parameterValue) {
if (typeof parameterPath === "string") {
parameters[parameterPath] = parameterValue;
}
else if (Array.isArray(parameterPath)) {
let leafParent = parameters;
for (let i = 0; i < parameterPath.length - 1; i++) {
const currentPropertyName = parameterPath[i];
if (!leafParent[currentPropertyName]) {
leafParent[currentPropertyName] = {};
}
leafParent = leafParent[currentPropertyName];
}
const lastPropertyName = parameterPath[parameterPath.length - 1];
leafParent[lastPropertyName] = parameterValue;
}
else {
throw new TypeError(`parameterPath is not string or string[]`);
}
}
async function serialize(context, res, spec, handlerResponse, logger) {
const statusCodeInResponse = handlerResponse.statusCode;
res.setStatusCode(statusCodeInResponse);
const responseSpec = spec.responses[statusCodeInResponse];
if (!responseSpec) {
throw new TypeError(`Request specification doesn't include provided response status code`);
}
// Serialize headers
const headerSerializer = new msRest.Serializer(Mappers);
const headersMapper = responseSpec.headersMapper;
if (headersMapper && headersMapper.type.name === "Composite") {
const mappersForAllHeaders = headersMapper.type.modelProperties || {};
// Handle headerMapper one by one
for (const key in mappersForAllHeaders) {
if (mappersForAllHeaders.hasOwnProperty(key)) {
const headerMapper = mappersForAllHeaders[key];
const headerName = headerMapper.serializedName;
const headerValueOriginal = handlerResponse[key];
const headerValueSerialized = headerSerializer.serialize(headerMapper, headerValueOriginal);
// Handle collection of headers starting with same prefix, such as x-ms-meta prefix
const headerCollectionPrefix = headerMapper
.headerCollectionPrefix;
if (headerCollectionPrefix !== undefined &&
headerValueOriginal !== undefined) {
for (const collectionHeaderPartialName in headerValueSerialized) {
if (headerValueSerialized.hasOwnProperty(collectionHeaderPartialName)) {
const collectionHeaderValueSerialized = headerValueSerialized[collectionHeaderPartialName];
const collectionHeaderName = `${headerCollectionPrefix}${collectionHeaderPartialName}`;
if (collectionHeaderName &&
collectionHeaderValueSerialized !== undefined) {
res.setHeader(collectionHeaderName, collectionHeaderValueSerialized);
}
}
}
}
else {
if (headerName && headerValueSerialized !== undefined) {
res.setHeader(headerName, headerValueSerialized);
}
}
}
}
}
// Serialize XML bodies
if (spec.isXML &&
responseSpec.bodyMapper &&
responseSpec.bodyMapper.type.name !== "Stream") {
let body = spec.serializer.serialize(responseSpec.bodyMapper, handlerResponse);
// When root element is sequence type, should wrap with because serialize() doesn't do that
if (responseSpec.bodyMapper.type.name === "Sequence") {
const sequenceElementName = responseSpec.bodyMapper.xmlElementName;
if (sequenceElementName !== undefined) {
const newBody = {};
newBody[sequenceElementName] = body;
body = newBody;
}
}
const xmlBody = (0, xml_1.stringifyXML)(body, {
rootName: responseSpec.bodyMapper.xmlName ||
responseSpec.bodyMapper.serializedName
});
res.setContentType(`application/xml`);
// TODO: Should send response in a serializer?
res.getBodyStream().write(xmlBody);
logger.debug(`Serializer: Raw response body string is ${xmlBody}`, context.contextID);
logger.info(`Serializer: Start returning stream body.`, context.contextID);
}
// Serialize JSON bodies
if (!spec.isXML &&
responseSpec.bodyMapper &&
responseSpec.bodyMapper.type.name !== "Stream") {
let body = spec.serializer.serialize(responseSpec.bodyMapper, handlerResponse);
// When root element is sequence type, should wrap with because serialize() doesn't do that
if (responseSpec.bodyMapper.type.name === "Sequence") {
const sequenceElementName = responseSpec.bodyMapper.xmlElementName;
if (sequenceElementName !== undefined) {
const newBody = {};
newBody[sequenceElementName] = body;
body = newBody;
}
}
if (!res.getHeader("content-type")) {
res.setContentType("application/json");
}
const jsonBody = JSON.stringify(body);
// TODO: Should send response in a serializer?
res.getBodyStream().write(jsonBody);
logger.debug(`Serializer: Raw response body string is ${jsonBody}`, context.contextID);
logger.info(`Serializer: Start returning stream body.`, context.contextID);
}
// Serialize stream body
// TODO: Move to end middleware for end tracking
if (handlerResponse.body &&
responseSpec.bodyMapper &&
responseSpec.bodyMapper.type.name === "Stream") {
logger.info(`Serializer: Start returning stream body.`, context.contextID);
await new Promise((resolve, reject) => {
handlerResponse.body
.on("error", reject)
.pipe(res.getBodyStream())
.on("error", reject)
.on("close", resolve);
});
// const totalTimeInMS = context.startTime
// ? new Date().getTime() - context.startTime.getTime()
// : undefined;
// logger.info(
// tslint:disable-next-line:max-line-length
// `Serializer: End response. TotalTimeInMS=${totalTimeInMS} StatusCode=${res.getStatusCode()} StatusMessage=${res.getStatusMessage()} Headers=${JSON.stringify(
// res.getHeaders()
// )}`,
// context.contextID
// );
}
}
exports.serialize = serialize;
//# sourceMappingURL=serializer.js.map
;