@sap/odata-v4
Version:
OData V4.0 server library
162 lines (155 loc) • 9.75 kB
JavaScript
;
const crypto = require('crypto');
const EdmProvider = require('./provider/EdmProvider');
const CsdlJsonProvider = require('./provider/CsdlJsonProvider');
const UriParser = require('./uri/UriParser');
const Service = require('./core/Service');
const Components = require('./core/ComponentManager').Components;
const Dispatcher = require('./core/Dispatcher');
const MetadataHandler = require('./handler/MetadataHandler');
const ServiceHandler = require('./handler/ServiceHandler');
const BatchHandler = require('./handler/BatchHandler');
const ResponseContentNegotiator = require('./format/ResponseContentNegotiator');
const RepresentationKinds = require('./format/RepresentationKind').Kinds;
const ContentTypes = require('./format/ContentTypeInfo').ContentTypes;
const SerializerFactory = require('./serializer/SerializerFactory');
const MetadataXmlSerializer = require('./serializer/MetadataXmlSerializer');
const ServiceJsonSerializer = require('./serializer/ServiceJsonSerializer');
const BatchMultipartSerializer = require('./serializer/BatchMultipartSerializer');
const ErrorJsonSerializer = require('./serializer/ErrorJsonSerializer');
const ErrorXmlSerializer = require('./serializer/ErrorXmlSerializer');
const DebugJsonSerializer = require('./serializer/DebugJsonSerializer');
const DebugHtmlSerializer = require('./serializer/DebugHtmlSerializer');
const RequestValidator = require('./validator/RequestValidator');
const DeserializerFactory = require('./deserializer/DeserializerFactory');
/**
* OData service factory to create a full featured odata service
*/
class ServiceFactory {
/**
* Creates a full featured odata service.
*
* @param {Object} metadata The metadata JSON with which the CSDL and then EDM providers can be created
* @param {Object} [edmConfiguration={}] A configuration object to configure the behavior of edm artifacts
* The example shows the configuration for an EntitySet and Singleton, that are ETAG enabled.
* const configuration = {
* 'the.edm.namespace': {
* 'TheNameOfTheConcurrentEntitySet': {
* isConcurrent: true
* },
* 'TheNameOfTheConcurrentSingleton': {
* isConcurrent: true
* }
* }
* };
*
* @returns {Service} A full featured odata service
*/
static createService(metadata, edmConfiguration = {}) {
const edmProvider = new EdmProvider(new CsdlJsonProvider(metadata), edmConfiguration);
return new Service(edmProvider)
.log(null, entry => entry)
.on('request', (request, response, next) => {
// This is the default listener called on on each request.
next(/* error */);
})
.on('debug', (request, response, next) => {
// We disable the debug mode by default.
// Overwrite this listener to enable debug mode.
// To get the info if debug mode is requested
// call 'request.getContract().isDebug(): true|false'.
request.getContract().enableDebugMode(false);
next(/* error */);
})
.on('error', (request, response, next, error) => {
// This listener subscribes to all error events.
// The error object is the original error thrown.
// Another error-like object with properties like .statusCode or .message could be passed on.
next(null, error);
})
.setOdataVersion('4.0')
.setMetadataEtag(crypto.createHash('sha256').update(JSON.stringify(metadata)).digest('base64'))
.use(Components.DISPATCHER, new Dispatcher())
.use(Components.URI_PARSER, new UriParser(edmProvider))
.use(Components.METADATA_HANDLER, MetadataHandler.read)
.use(Components.SERVICE_HANDLER, ServiceHandler.read)
.use(Components.BATCH_EXECUTE_HANDLER, BatchHandler.handle)
.use(Components.CONTENT_NEGOTIATOR, new ResponseContentNegotiator())
.use(Components.REQUEST_VALIDATOR, new RequestValidator())
.parse(RepresentationKinds.BATCH, ContentTypes.MULTIPART_MIXED,
DeserializerFactory.createMultipartMixedDeserializer())
.parse(RepresentationKinds.ENTITY, ContentTypes.JSON,
DeserializerFactory.createJsonDeserializer(edmProvider, RepresentationKinds.ENTITY))
.parse(RepresentationKinds.PRIMITIVE, ContentTypes.JSON,
DeserializerFactory.createJsonDeserializer(edmProvider, RepresentationKinds.PRIMITIVE))
.parse(RepresentationKinds.PRIMITIVE_COLLECTION, ContentTypes.JSON,
DeserializerFactory.createJsonDeserializer(edmProvider, RepresentationKinds.PRIMITIVE_COLLECTION))
.parse(RepresentationKinds.COMPLEX, ContentTypes.JSON,
DeserializerFactory.createJsonDeserializer(edmProvider, RepresentationKinds.COMPLEX))
.parse(RepresentationKinds.COMPLEX_COLLECTION, ContentTypes.JSON,
DeserializerFactory.createJsonDeserializer(edmProvider, RepresentationKinds.COMPLEX_COLLECTION))
.parse(RepresentationKinds.REFERENCE, ContentTypes.JSON,
DeserializerFactory.createJsonDeserializer(edmProvider, RepresentationKinds.REFERENCE))
.parse(RepresentationKinds.PRIMITIVE_VALUE, ContentTypes.TEXT_PLAIN,
DeserializerFactory.createTextValueDeserializer())
.parse(RepresentationKinds.ACTION_PARAMETERS, ContentTypes.JSON,
DeserializerFactory.createJsonDeserializer(edmProvider, RepresentationKinds.ACTION_PARAMETERS))
// Register the binary parser for all content types sent for representation kind BINARY.
// There is special logic for this in the request content negotiator.
.parse(RepresentationKinds.BINARY, '', DeserializerFactory.createBinaryDeserializer())
.format(RepresentationKinds.METADATA, ContentTypes.XML, (context, data, options, next) => {
next(null, new MetadataXmlSerializer(edmProvider, context.getLogger()).serialize(data));
})
.format(RepresentationKinds.SERVICE, ContentTypes.JSON, (context, data, options, next) => {
next(null, new ServiceJsonSerializer(edmProvider).serialize(data));
})
.format(RepresentationKinds.BATCH, ContentTypes.MULTIPART_MIXED, (context, data, options, next) => {
const response = context.getResponse();
new BatchMultipartSerializer(data).execute(response);
next();
})
.format(RepresentationKinds.ENTITY, ContentTypes.JSON,
SerializerFactory.resourceEntity, SerializerFactory.checkJsonParameters)
.format(RepresentationKinds.ENTITY_COLLECTION, ContentTypes.JSON,
SerializerFactory.resourceEntityCollection, SerializerFactory.checkJsonParameters)
.format(RepresentationKinds.PRIMITIVE, ContentTypes.JSON,
SerializerFactory.resourcePrimitive, SerializerFactory.checkJsonParameters)
.format(RepresentationKinds.PRIMITIVE_COLLECTION, ContentTypes.JSON,
SerializerFactory.resourcePrimitiveCollection, SerializerFactory.checkJsonParameters)
.format(RepresentationKinds.COMPLEX, ContentTypes.JSON,
SerializerFactory.resourceComplex, SerializerFactory.checkJsonParameters)
.format(RepresentationKinds.COMPLEX_COLLECTION, ContentTypes.JSON,
SerializerFactory.resourceComplexCollection, SerializerFactory.checkJsonParameters)
.format(RepresentationKinds.REFERENCE, ContentTypes.JSON,
SerializerFactory.resourceReference, SerializerFactory.checkJsonParameters)
.format(RepresentationKinds.REFERENCE_COLLECTION, ContentTypes.JSON,
SerializerFactory.resourceReferenceCollection, SerializerFactory.checkJsonParameters)
.format(RepresentationKinds.PRIMITIVE_VALUE, ContentTypes.TEXT_PLAIN, SerializerFactory.value)
.format(RepresentationKinds.BINARY, ContentTypes.TEXT_PLAIN, SerializerFactory.value)
.format(RepresentationKinds.COUNT, ContentTypes.TEXT_PLAIN, (context, data, options, next) => {
next(null, data.value.toString());
})
.format(RepresentationKinds.NO_CONTENT, null, (context, data, options, next) => {
context.getLogger().path('Entering ServiceFactory.format(NO_CONTENT, null)');
context.getLogger().info('No payload available');
next();
})
.format(RepresentationKinds.ERROR, ContentTypes.XML, (error, response, next) => {
const data = new ErrorXmlSerializer(error).serialize();
next(null, data);
})
.format(RepresentationKinds.ERROR, ContentTypes.JSON, (error, response, next) => {
const data = new ErrorJsonSerializer(error).serialize();
next(null, data);
}, SerializerFactory.checkJsonParameters)
.format(RepresentationKinds.DEBUG, ContentTypes.JSON, (context, buffer, error, next) => {
const data = new DebugJsonSerializer(context, buffer.toString(), error).serialize();
next(null, data);
})
.format(RepresentationKinds.DEBUG, ContentTypes.HTML, (context, buffer, error, next) => {
const data = new DebugHtmlSerializer(context, buffer.toString(), error).serialize();
next(null, data);
});
}
}
module.exports = ServiceFactory;