UNPKG

breeze2-odata4

Version:

Library to allow OData 4 support for breezejs 2

544 lines 24.1 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); var breeze_client_1 = require("breeze-client"); var ts_odatajs_1 = require("ts-odatajs"); var annotation_adapter_1 = require("./adapters/annotation-adapter"); var navigation_adapter_1 = require("./adapters/navigation-adapter"); var breeze_jsonResultsAdapter_factory_1 = require("./breeze-jsonResultsAdapter-factory"); var class_registry_1 = require("./class-registry"); var custom_decorator_1 = require("./decorators/custom-decorator"); var display_name_decorator_1 = require("./decorators/display-name-decorator"); var store_generated_pattern_decorator_1 = require("./decorators/store-generated-pattern-decorator"); var validator_decorator_1 = require("./decorators/validator-decorator"); var odata_error_1 = require("./odata-error"); var utilities_1 = require("./utilities"); var ProxyDataService = (function () { function ProxyDataService() { } return ProxyDataService; }()); exports.ProxyDataService = ProxyDataService; Object.setPrototypeOf(ProxyDataService.prototype, breeze_client_1.config.getAdapter('dataService', 'WebApi').prototype); var OData4DataService = (function (_super) { __extends(OData4DataService, _super); function OData4DataService() { var _this = _super.call(this) || this; _this.innerAdapter = breeze_client_1.config.getAdapter('dataService', 'WebApi'); _this.metadataAdapters = []; _this.functions = []; _this.actions = []; _this.name = 'OData4'; _this.headers = { 'OData-Version': '4.0' }; _this.metadataAcceptHeader = 'application/json;odata.metadata=full'; _this.jsonResultsAdapter = breeze_jsonResultsAdapter_factory_1.getJsonResultsAdapter(); return _this; } OData4DataService.register = function () { breeze_client_1.config.registerAdapter('dataService', OData4DataService); }; OData4DataService.prototype._catchNoConnectionError = function (err) { return breeze_client_1.AbstractDataServiceAdapter._catchNoConnectionError(err); }; OData4DataService.prototype._createChangeRequestInterceptor = function (saveContext, saveBundle) { var innerAdapterExtend = this.innerAdapter; return innerAdapterExtend._createChangeRequestInterceptor(saveContext, saveBundle); }; OData4DataService.prototype.checkForRecomposition = function (interfaceInitializedArgs) { this.innerAdapter.checkForRecomposition(interfaceInitializedArgs); }; OData4DataService.prototype.initialize = function () { this.fixODataFormats(); class_registry_1.ClassRegistry.MetadataAdapters.add(navigation_adapter_1.NavigationAdapter, annotation_adapter_1.AnnotationAdapter); class_registry_1.ClassRegistry.AnnotationDecorators.add(store_generated_pattern_decorator_1.StoreGeneratedPatternDecorator, display_name_decorator_1.DisplayNameDecorator, custom_decorator_1.CustomDecorator, validator_decorator_1.ValidatorDecorator); this.metadataAdapters = class_registry_1.ClassRegistry.MetadataAdapters.get(); }; OData4DataService.prototype.getAbsoluteUrl = function (dataService, url) { var serviceName = dataService.qualifyUrl(''); var base = breeze_client_1.core.stringStartsWith(url, serviceName) ? '' : serviceName; if (window && serviceName.startsWith('//')) { base = "" + location.origin + (breeze_client_1.core.stringStartsWith(serviceName, '/') ? '' : '/') + base; } return base + url; }; OData4DataService.prototype.fetchMetadata = function (metadataStore, dataService) { var _this = this; var associations = {}; var serviceName = dataService.serviceName; var url = this.getAbsoluteUrl(dataService, '$metadata'); return new Promise(function (resolve, reject) { ts_odatajs_1.oData.read({ requestUri: url, headers: Object.assign({ Accept: _this.metadataAcceptHeader }, _this.headers) }, function (data, response) { if (!data || !data.dataServices) { var error = new Error("Metadata query failed for: " + url); return reject(error); } _this.metadata = data; var csdlMetadata = _this.metadata.dataServices; _this.metadataAdapters.forEach(function (a) { ts_odatajs_1.oData.utils.forEachSchema(csdlMetadata, a.adapt.bind(a)); }); if (!metadataStore.hasMetadataFor(serviceName)) { try { metadataStore.importMetadata(csdlMetadata); } catch (e) { reject(new Error("Metadata query failed for " + url + "; Unable to process returned metadata: " + e.message)); } metadataStore.addDataService(dataService); } _this.actions = utilities_1.getActions(_this.metadata, metadataStore); _this.functions = utilities_1.getFunctions(_this.metadata, metadataStore); resolve(csdlMetadata); }, function (error) { var err = _this.createError(error, url); err.message = "Metadata query failed for: " + url + "; " + (err.message || ''); reject(err); }, ts_odatajs_1.oData.metadataHandler); }); }; OData4DataService.prototype.executeQuery = function (mappingContext) { var _this = this; var query = mappingContext.query; var request = this.getRequest(mappingContext); return new Promise(function (resolve, reject) { ts_odatajs_1.oData.request(request, function (data, response) { var inlineCount; var results; if (data) { inlineCount = Number(data['@odata.count']); results = data.value; } resolve({ results: results, query: query, inlineCount: inlineCount, httpResponse: response }); }, function (error) { var err = _this.createError(error, request.requestUri); reject(err); }); }); }; OData4DataService.prototype.saveChanges = function (saveContext, saveBundle) { var _this = this; var adapter = (saveContext.adapter = this); saveContext.routePrefix = this.getAbsoluteUrl(saveContext.dataService, ''); var url = saveContext.routePrefix + "$batch"; var requestData = this.createChangeRequests(saveContext, saveBundle); var tempKeys = saveContext.tempKeys; var contentKeys = saveContext.contentKeys; return new Promise(function (resolve, reject) { ts_odatajs_1.oData.request({ requestUri: url, method: 'POST', data: requestData }, function (data, response) { var entities = []; var keyMappings = []; var saveResult = { entities: entities, keyMappings: keyMappings }; data.__batchResponses.forEach(function (br) { br.__changeResponses.forEach(function (cr, index) { var chResponse = cr.response || cr; var statusCode = chResponse.statusCode; if (!statusCode || Number(statusCode) >= 400) { var err = _this.createError(cr, url); reject(err); return; } var contentId = index + 1; var rawEntity = chResponse.data; if (rawEntity) { var tempKey = tempKeys[contentId]; if (tempKey) { var entityType = tempKey.entityType; if (entityType.autoGeneratedKeyType !== breeze_client_1.AutoGeneratedKeyType.None) { var tempValue = tempKey.values[0]; var realKey = entityType.getEntityKeyFromRawEntity(rawEntity, breeze_client_1.DataProperty.getRawValueFromServer); var keyMapping = { entityTypeName: entityType.name, tempValue: tempValue, realValue: realKey.values[0] }; keyMappings.push(keyMapping); } } entities.push(rawEntity); } else { var origEntity = contentKeys[contentId]; entities.push(origEntity); } }); }); resolve(saveResult); }, function (err) { var error = _this.createError(err, url); reject(error); }, ts_odatajs_1.oData.batch.batchHandler, undefined, _this.metadata); }); }; OData4DataService.prototype.createChangeRequests = function (saveContext, saveBundle) { var _this = this; var changeRequestInterceptor = this._createChangeRequestInterceptor(saveContext, saveBundle); var changeRequests = []; var tempKeys = []; var contentKeys = []; var entityManager = saveContext.entityManager; var helper = entityManager.helper; var id = 0; var routePrefix = saveContext.routePrefix; saveBundle.entities.forEach(function (entity, index) { var aspect = entity.entityAspect; id = id + 1; var request = { headers: Object.assign({ 'Content-ID': id.toString(), 'Content-Type': 'application/json;IEEE754Compatible=true' }, _this.headers), requestUri: null, method: null }; contentKeys[id] = entity; if (aspect.entityState.isAdded()) { var resourceName = saveContext.resourceName || entity.entityType.defaultResourceName; request.requestUri = routePrefix + entity.entityType.defaultResourceName; request.method = 'POST'; request.data = helper.unwrapInstance(entity, _this.transformValue); tempKeys[id] = aspect.getKey(); } else if (aspect.entityState.isModified()) { _this.updateDeleteMergeRequest(request, aspect, routePrefix); request.method = 'PATCH'; request.data = helper.unwrapChangedValues(entity, entityManager.metadataStore, _this.transformValue); } else if (aspect.entityState.isDeleted()) { _this.updateDeleteMergeRequest(request, aspect, routePrefix); request.method = 'DELETE'; } else { return; } request = changeRequestInterceptor.getRequest(request, entity, index); changeRequests.push(request); }); saveContext.contentKeys = contentKeys; saveContext.tempKeys = tempKeys; changeRequestInterceptor.done(changeRequests); var changeRequestSet = [ { __changeRequests: changeRequests } ]; var batchRequest = { __batchRequests: changeRequestSet }; return batchRequest; }; OData4DataService.prototype.getRequest = function (mappingContext) { var query = mappingContext.query; var method = 'GET'; var request = { method: method, requestUri: this.getUrl(mappingContext), headers: Object.assign({}, this.headers) }; if (!query.parameters) { return request; } method = query.parameters['$method'] || method; delete query.parameters['$method']; if (method === 'GET') { request = Object.assign({}, request, { requestUri: this.addQueryString(request.requestUri, query.parameters) }); } else { var data = query.parameters['$data'] ? this.getData(mappingContext, query.parameters['$data']) : query.parameters; request = Object.assign({}, request, { method: method, data: data }); } return request; }; OData4DataService.prototype.getData = function (mappingContext, data) { if (!data) { return null; } if (!this.areValidPropertyNames(mappingContext.metadataStore, data)) { return data; } var helper = mappingContext.entityManager.helper; if (data.entityType || data.complexType) { return helper.unwrapInstance(data, null); } var config = this.getInvokableConfig(mappingContext.query.resourceName); var actionEntry = this.actions.find(function (e) { return e.config === config; }); if (!actionEntry) { return data; } var paramIndex = config.isBound ? 1 : 0; var param = config.parameter.find(function (p, idx) { if (idx < paramIndex || p.type.startsWith('Edm.')) { return false; } return true; }); if (!param) { return data; } var edmType = utilities_1.getEdmTypeFromTypeName(this.metadata, param.type); if (!edmType) { return data; } var structuralType = utilities_1.adaptStructuralType(mappingContext.metadataStore, edmType); if (structuralType instanceof breeze_client_1.EntityType) { data = structuralType.createEntity(data); return helper.unwrapInstance(data, null); } else if (structuralType instanceof breeze_client_1.ComplexType) { data = structuralType.createInstance(data); return helper.unwrapInstance(data, null); } return data; }; OData4DataService.prototype.areValidPropertyNames = function (metadataStore, data) { var props = Object.keys(data); var result = props.every(function (p) { var sp = metadataStore.namingConvention.clientPropertyNameToServer(p); var cp = metadataStore.namingConvention.serverPropertyNameToClient(sp); return p === cp; }); return result; }; OData4DataService.prototype.getInvokableConfig = function (url) { var urlParts = url ? url.split('/') : []; var binding = urlParts[0]; var invokableName = urlParts.pop().replace(/\([^\)]*\)/, ''); var actionConfig = ts_odatajs_1.oData.utils.lookupAction(invokableName, this.metadata); var functionConfig = ts_odatajs_1.oData.utils.lookupFunction(invokableName, this.metadata); return actionConfig || functionConfig; }; OData4DataService.prototype.getUrl = function (mappingContext) { var query = mappingContext.query; var url = this.getAbsoluteUrl(mappingContext.dataService, mappingContext.getUrl()); return url; }; OData4DataService.prototype.addQueryString = function (url, parameters) { var queryString = this.toQueryString(parameters); if (!queryString) { return url; } var sep = url.indexOf('?') < 0 ? '?' : '&'; url += sep + queryString; return url; }; OData4DataService.prototype.transformValue = function (prop, val) { if (prop.isUnmapped) { return undefined; } if (prop.dataType instanceof breeze_client_1.DataType && prop.dataType.quoteJsonOData) { val = val != null ? val.toString() : val; } return val; }; OData4DataService.prototype.updateDeleteMergeRequest = function (request, aspect, routePrefix) { var uriKey; var extraMetadata = aspect.extraMetadata; if (!extraMetadata) { uriKey = this.getUriKey(aspect); aspect.extraMetadata = { uriKey: uriKey }; } else { uriKey = extraMetadata['uriKey'] || this.getUriKey(aspect); if (extraMetadata['etag']) { request.headers['If-Match'] = extraMetadata['etag']; } } request.requestUri = uriKey.indexOf('//') > 0 ? uriKey : routePrefix + uriKey; }; OData4DataService.prototype.getUriKey = function (aspect) { var _this = this; var entityType = aspect.entity.entityType; var resourceName = entityType.defaultResourceName; var kps = entityType.keyProperties; var uriKeyValue = kps.length === 1 ? this.fmtProperty(kps[0], aspect) : kps.map(function (kp) { return kp.nameOnServer + "=" + _this.fmtProperty(kp, aspect); }); var uriKey = resourceName + "(" + uriKeyValue + ")"; return uriKey; }; OData4DataService.prototype.fmtProperty = function (prop, aspect) { if (prop.dataType instanceof breeze_client_1.DataType) { return prop.dataType.fmtOData(aspect.getPropertyValue(prop.name)); } else { throw new Error('Not implemented'); } }; OData4DataService.prototype.createError = function (error, url) { var result = new odata_error_1.ODataError(); var response = error && error.response; if (!response) { result.message = error; result.statusText = error; return result; } result.message = response.statusText; result.statusText = response.statusText; result.status = Number(response.statusCode); if (url) { result.url = url; } result.body = response.body; if (response.body) { var nextErr = void 0; try { var body = JSON.parse(response.body); result.body = body; if (body['odata.error']) { body = body['odata.error']; } var msg = ''; do { nextErr = body.error || body.innererror; if (!nextErr) { msg = msg + this.getMessage(body); } nextErr = nextErr || body.internalexception; body = nextErr || body; } while (nextErr); if (msg.length > 0) { result.message = msg; } } catch (e) { } } this._catchNoConnectionError(result); return result; }; OData4DataService.prototype.getMessage = function (body) { var msg = body['message'] || body['Message'] || ''; return (typeof msg === 'string' ? msg : msg.value) + '; '; }; OData4DataService.prototype.fixODataFormats = function () { breeze_client_1.DataType.Int64.fmtOData = fmtFloat; breeze_client_1.DataType.Decimal.fmtOData = fmtFloat; breeze_client_1.DataType.Double.fmtOData = fmtFloat; breeze_client_1.DataType.DateTime.fmtOData = fmtDateTime; breeze_client_1.DataType.DateTimeOffset.fmtOData = fmtDateTimeOffset; breeze_client_1.DataType.Time.fmtOData = fmtTime; breeze_client_1.DataType.Guid.fmtOData = fmtGuid; utilities_1.OData4DataType.Duration = utilities_1.OData4DataType.Time; var DataTypeExtend = breeze_client_1.DataType; DataTypeExtend.addSymbol = function (propertiesObj) { var newSymbol = new breeze_client_1.DataType(propertiesObj); return newSymbol; }; utilities_1.OData4DataType.Stream = DataTypeExtend.addSymbol({ defaultValue: '', parse: breeze_client_1.DataType.String.parse, fmtOData: breeze_client_1.DataType.String.fmtOData, validatorCtor: breeze_client_1.Validator.string }); utilities_1.OData4DataType.TimeOfDay = DataTypeExtend.addSymbol({ defaultValue: '00:00', parse: breeze_client_1.DataType.String.parse, fmtOData: breeze_client_1.DataType.String.fmtOData, validatorCtor: breeze_client_1.Validator.string }); function fmtFloat(val) { if (val === null) { return null; } if (typeof val === 'string') { val = parseFloat(val); } return val; } function fmtDateTime(val) { if (!val) { return null; } try { return val.toISOString(); } catch (e) { throwError("'%1' is not a valid dateTime", val); } } function fmtDateTimeOffset(val) { if (!val) { return null; } try { return val.toISOString(); } catch (e) { throwError("'%1' is not a valid dateTimeOffset", val); } } function fmtTime(val) { if (!val) { return null; } if (!breeze_client_1.core.isDuration(val)) { throwError("'%1' is not a valid ISO 8601 duration", val); } return val; } function fmtGuid(val) { if (!val) { return null; } if (!breeze_client_1.core.isGuid(val)) { throwError("'%1' is not a valid guid", val); } return val; } function throwError(msg, val) { msg = breeze_client_1.core.formatString(msg, val); throw new Error(msg); } }; OData4DataService.prototype.toQueryString = function (payload) { if (!payload) { return null; } var result = Object.keys(payload) .map(function (key) { return encodeURIComponent(key) + "=" + encodeURIComponent(payload[key]); }) .join('&'); return result; }; return OData4DataService; }(ProxyDataService)); exports.OData4DataService = OData4DataService; //# sourceMappingURL=breeze-odata4-dataService.js.map