@sap/odata-v4
Version:
OData V4.0 server library
176 lines (157 loc) • 7.76 kB
JavaScript
;
const stream = require('stream');
const BufferedWriter = require('../utils/BufferedWriter');
const ResourceJsonDeserializer = require('./ResourceJsonDeserializer');
const ServiceResolutions = require('../core/ServiceResolutions');
const BatchContext = require('../batch/BatchContext');
const BatchRequestListBuilder = require('./BatchRequestListBuilder');
const ValueTextDeserializer = require('./ValueTextDeserializer');
const RepresentationKinds = require('../format/RepresentationKind').Kinds;
const JsonContentTypeInfo = require('../format/JsonContentTypeInfo');
const EdmPrimitiveTypeKind = require('../edm/EdmPrimitiveTypeKind');
const DeserializationError = require('../errors/DeserializationError');
/**
* The DeserializerFactory is responsible for creating parsing facade functions
* for a specific payload type regarding to the corresponding request
* representation kind like ENTITY and the corresponding request mime type.
*/
class DeserializerFactory {
/**
* Creates and returns the deserialization facade function for application/json requests.
* This function is called on deserialization of the incoming payload.
*
* @param {Edm} edm the current EDM instance
* @param {RepresentationKind.Kinds} representationKind the representation kind
* @returns {Function} The deserialization facade function
*/
static createJsonDeserializer(edm, representationKind) {
const getPropertyFunction = uriInfo => uriInfo.getLastSegment().getProperty();
function getCreateDeserializerFunction(name) {
return (contract) => {
const jsonContentTypeInfo = new JsonContentTypeInfo(contract.getContentTypeInfo());
let expand = [];
const deserializer = new ResourceJsonDeserializer(edm, jsonContentTypeInfo);
return (edmObject, value) => {
return {
body: deserializer[name](edmObject, value, expand),
deserializer, expand
};
};
};
}
switch (representationKind) {
case RepresentationKinds.ENTITY:
return DeserializerFactory._createDeserializer(uriInfo => uriInfo.getFinalEdmType(),
getCreateDeserializerFunction('deserializeEntity'));
case RepresentationKinds.PRIMITIVE:
return DeserializerFactory._createDeserializer(getPropertyFunction,
getCreateDeserializerFunction('deserializePrimitiveProperty'));
case RepresentationKinds.PRIMITIVE_COLLECTION:
return DeserializerFactory._createDeserializer(getPropertyFunction,
getCreateDeserializerFunction('deserializePrimitivePropertyCollection'));
case RepresentationKinds.COMPLEX:
return DeserializerFactory._createDeserializer(getPropertyFunction,
getCreateDeserializerFunction('deserializeComplexProperty'));
case RepresentationKinds.COMPLEX_COLLECTION:
return DeserializerFactory._createDeserializer(getPropertyFunction,
getCreateDeserializerFunction('deserializeComplexPropertyCollection'));
case RepresentationKinds.ACTION_PARAMETERS:
return DeserializerFactory._createDeserializer(uriInfo => uriInfo.getLastSegment().getAction(),
getCreateDeserializerFunction('deserializeActionParameters'));
case RepresentationKinds.REFERENCE:
return DeserializerFactory._createDeserializer(uriInfo => uriInfo.getFinalEdmType(),
getCreateDeserializerFunction('deserializeReference'));
default:
return null;
}
}
/**
* Create and return the deserialization function for text/plain requests
* containing a single primitive value.
* This function is called on deserialization of the incoming payload.
*
* @returns {Function} the deserialization function
*/
static createTextValueDeserializer() {
return DeserializerFactory._createDeserializer(
uriInfo => uriInfo.getLastSegment(-1).getProperty(),
() => {
const deserializer = new ValueTextDeserializer();
return (edmProperty, value) => {
return {
body: deserializer.convertPrimitiveValue(edmProperty, value.toString()),
deserializer
};
};
});
}
/**
* Create and return the deserialization function for binary requests.
* This function is called on deserialization of the incoming payload.
*
* @returns {Function} the deserialization function
*/
static createBinaryDeserializer() {
return (request, next) => {
if (request.getUriInfo() && request.getUriInfo().getFinalEdmType() === EdmPrimitiveTypeKind.Stream) {
next(null, request.getIncomingRequest().on('error', next).pipe(new stream.PassThrough()));
} else {
request.getIncomingRequest()
.on('error', next)
.pipe(new BufferedWriter())
.on('error', next)
.on('result', rawBody => next(null, rawBody));
}
};
}
/**
* Create and return the deserialization function for binary requests.
* This function is called on deserialization of the incoming payload.
*
* @returns {Function} the deserialization function
*/
static createMultipartMixedDeserializer() {
return (request, next) => {
const batchRequestListBuilder =
new BatchRequestListBuilder({ resolutionStrategy: ServiceResolutions.viaBasePath() });
batchRequestListBuilder.build(request, (err, list) =>
err ?
next(err) :
next(null, {
boundary: batchRequestListBuilder.getBatchBoundary(),
requests: list,
semantic: BatchContext.SEMANTIC.MULTIPART
})
);
};
}
/**
* Create and return a deserialization function.
* @param {Function} edmFunction function to get from URI info the EDM object for the parse function
* @param {Function} createDeserializerFunction function to create the deserialize function
* (with parameters EDM object and input)
* @returns {Function} the deserialization function
*/
static _createDeserializer(edmFunction, createDeserializerFunction) {
return (request, next) => {
request.getIncomingRequest()
.on('error', next)
.pipe(new BufferedWriter())
.on('error', next)
.on('result', rawBody => {
try {
const edmObject = edmFunction(request.getUriInfo());
const deserializeFunction = createDeserializerFunction(request.getContract());
const result = deserializeFunction(edmObject, rawBody);
if (result.expand && result.expand.length > 0) {
request.setDeepInsertExpand(result.expand);
}
next(null, result.body);
} catch (error) {
next(new DeserializationError('Error while deserializing payload', error), rawBody);
}
});
};
}
}
module.exports = DeserializerFactory;