breeze2-odata4
Version:
Library to allow OData 4 support for breezejs 2
544 lines • 24.1 kB
JavaScript
"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