pailingual-odata
Version:
TypeScript client for OData v4 services
341 lines • 13.4 kB
JavaScript
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./metadata", "./serialization", "./utils"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const metadata_1 = require("./metadata");
const serialization_1 = require("./serialization");
const utils_1 = require("./utils");
class Query {
constructor(_apiMetadata, _entityMetadata, _options) {
this._apiMetadata = _apiMetadata;
this._entityMetadata = _entityMetadata;
this._options = _options;
this._segments = [];
this.params = {};
this._method = "get";
}
static create(apiMetadata, entityMetadata, options) {
return new Query(apiMetadata, entityMetadata, options)._freeze();
}
_clone(beforeFreeze) {
let res = new Query(this._apiMetadata, this._entityMetadata, this._options);
res._segments = [...this._segments];
let cloneArray = (a) => a ? [...a] : null;
for (const name of Object.getOwnPropertyNames(this.params)) {
let val = this.params[name];
if (val != undefined) {
res.params[name] = Array.isArray(val)
? cloneArray(val)
: val;
}
}
res._method = this._method;
res._payload = this._payload;
if (beforeFreeze)
beforeFreeze(res);
return res._freeze();
}
_freeze() {
//freeze segments
this._segments = Object.freeze(this._segments);
//freeze params
Object.getOwnPropertyNames(this.params).forEach(n => {
var v = this.params[n];
if (v !== null && typeof v === "object")
Object.freeze(v);
});
this.params = Object.freeze(this.params);
this._payload = Object.freeze(this._payload);
//freze query
return Object.freeze(this);
}
_action(metadata, args) {
return this._clone(q => {
q._segments.push(new ActionSegment(metadata));
q._method = "post";
q._entityMetadata = metadata.returnType && metadata.returnType.type;
if (args && args.length > 0) {
let payload = {};
let edmProps = {};
if (metadata.parameters) {
for (let i = 0; i < args.length; i++) {
const param = metadata.parameters[i];
payload[param.name] = args[i];
edmProps[param.name] = param.type;
}
}
q._entityMetadata = new metadata_1.EdmEntityType("", edmProps);
q._payload = payload;
}
});
}
_func(metadata, args) {
return this._clone(q => {
q._entityMetadata = metadata.returnType && metadata.returnType.type;
q._segments.push(new FuncSegment(metadata, args));
});
}
byKey(keyExpr) {
return this._clone(q => {
let pos = q._segments.length;
if (q._segments[pos - 1] instanceof CastSegment)
pos--;
q._segments.splice(pos, 0, new KeySegment(keyExpr));
});
}
cast(fullTypeName) {
return this._clone(q => q._segments.push(new CastSegment(fullTypeName)));
}
navigate(property, entityMetadata) {
return this._clone(q => {
q._entityMetadata = entityMetadata;
q._segments.push(new NavigationSegment(property));
});
}
operation(metadata, args) {
return metadata.isAction
? this._action(metadata, args)
: this._func(metadata, args);
}
count(o = { inline: false }) {
return this._clone(q => {
if (o.inline)
q.params.count = true;
else
q._segments.push(new CountSegment());
});
}
delete() {
return this._clone(q => {
q._method = "delete";
});
}
expand(expand, expr) {
return this._clone(q => {
let expandParam = q.params.expand == null
? q.params.expand = []
: q.params.expand;
expandParam.push({ expand, expr });
});
}
filter(expr) {
return this._clone(q => {
let filters = (q.params.filter == null)
? q.params.filter = new Array()
: q.params.filter;
filters.push(expr);
});
}
insert(payload) {
return this._clone(q => {
q._payload = payload;
q._method = "post";
});
}
orderBy(expressions) {
return this._clone(q => {
if (expressions) {
if (!q.params.orderBy)
q.params.orderBy = [];
q.params.orderBy.push(...expressions);
}
});
}
search(expr) {
return this._clone(q => {
if (!q.params.search)
q.params.search = [];
q.params.search.push(expr);
});
}
select(fields) {
return this._clone(q => q.params.select = fields);
}
skip(num) {
return this._clone(q => q.params.skip = num);
}
top(num) {
return this._clone(q => q.params.top = num);
}
update(payload, put) {
return this._clone(q => {
q._payload = payload;
q._method = put ? "put" : "patch";
});
}
url(queryParams = true, options) {
const apiRoot = (this._apiMetadata && this._apiMetadata.apiRoot) || "";
const params = queryParams ? this.params : undefined;
const opt = Object.assign({}, this._options, options);
let url = apiRoot + this._segments
.map(s => s.toUrlFragment(opt))
.join("");
if (params) {
const urlParams = this.buildParams(opt, '&');
if (urlParams)
return url + "?" + urlParams;
}
return url;
}
buildParams(options, separator = "&") {
return Object.getOwnPropertyNames(this.params)
.map(n => this.params[n] && this.processParameter(n, this.params[n], options))
.filter(v => v)
.join(separator);
}
processParameter(name, value, options) {
switch (name) {
case "select":
return "$select=" + value.join(",");
case "expand":
return "$expand=" + value.map((e) => this.expandToString(e, options)).join(",");
case "filter":
return "$filter=" + value.join(" and ");
case "orderBy":
return "$orderby=" + value.join(",");
case "skip":
return "$skip=" + value;
case "top":
return "$top=" + value;
case "count":
return value === true ? "$count=true" : undefined;
case "search":
return "$search=" + value.join(" AND ");
}
}
expandToString(e, options) {
if (e.expr)
return utils_1.expandExpressionBuild(e.expand, e.expr, this._apiMetadata, this._entityMetadata, options);
return e.expand;
}
exec(options) {
var opt = Object.assign({}, this._options, options);
var url = this.url(true, opt);
return this._fetchData(url, opt);
}
_fetchData(url, options) {
const fetchApi = (options && options.fetch) || fetch;
const inputFormatter = serialization_1.getFormatter(options.format || "application/json");
const body = this._payload ? inputFormatter.serialize(this._payload, this._entityMetadata, options) : null;
return fetchApi(url, {
method: this._method,
body,
headers: { "Content-Type": inputFormatter.contentType, "X-Requested-With": "XMLHttpRequest" },
credentials: options.credentials
})
.then(response => new Promise((resolve, reject) => {
if (response.body)
response.text().then(body => resolve({ response, body }), reject);
else
resolve({ response });
}))
.then(data => {
const bodyStr = data.body;
const response = data.response;
let contentType = response.headers.get("Content-Type");
if (response.ok) {
if (bodyStr && bodyStr.length > 0) {
if (!contentType) {
if (utils_1.startsWith(bodyStr, "{"))
contentType = "application/json";
}
else
contentType = contentType.split(";")[0].trim();
const outputFormatter = serialization_1.getFormatter(contentType);
return outputFormatter.deserialize(bodyStr, this._apiMetadata, options);
}
else
return null;
}
else {
try {
var odError = bodyStr && JSON.parse(bodyStr);
}
catch (_a) { }
let error = (odError && odError.error) || response.statusText;
throw { status: response.status, error };
}
});
}
}
exports.Query = Query;
class Segment {
}
class NavigationSegment extends Segment {
constructor(property) {
super();
this.property = property;
}
toUrlFragment() {
return "/" + this.property;
}
}
class KeySegment extends Segment {
constructor(key) {
super();
this.key = key;
}
toUrlFragment() {
return "(" + this.key + ")";
}
}
class ActionSegment extends Segment {
constructor(_metadata) {
super();
this._metadata = _metadata;
}
toUrlFragment(options) {
const actionName = this._metadata.bindingTo
&& options.enableUnqualifiedNameCall != true
? this._metadata.getFullName()
: this._metadata.name;
return "/" + actionName;
}
}
class FuncSegment extends Segment {
constructor(__metadata, __args) {
super();
this.__metadata = __metadata;
this.__args = __args;
}
toUrlFragment(options) {
const actionName = this.__metadata.bindingTo
&& options.enableUnqualifiedNameCall != true
? this.__metadata.getFullName()
: this.__metadata.name;
const serializedArgs = this.__args.map((a, i) => {
if (this.__metadata.parameters) {
const paramMetadata = this.__metadata.parameters[i];
if (paramMetadata != null) {
const v = serialization_1.serializeValue(a, paramMetadata.type.type, true);
return [paramMetadata.name, v].join("=");
}
}
throw new Error(`Parameter '${i}', for function '${actionName}', not defined in metadata`);
});
return `/${actionName}(${serializedArgs.join(",")})`;
}
}
class CountSegment extends Segment {
toUrlFragment() {
return "/$count";
}
}
class CastSegment extends Segment {
constructor(__fullTypeName) {
super();
this.__fullTypeName = __fullTypeName;
}
toUrlFragment() {
return "/" + this.__fullTypeName;
}
}
});
//# sourceMappingURL=query.js.map