kipon-xrmservice
Version:
Dynamics 365 CE web api wrapper for angular, making life easier when building angular based web resources
1,235 lines (1,234 loc) • 286 kB
JavaScript
import { Injectable } from '@angular/core';
import { HttpHeaders } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { map } from 'rxjs/operators';
import { Expand } from './xrm.service';
import * as i0 from "@angular/core";
import * as i1 from "@angular/common/http";
import * as i2 from "./xrm.service";
const XRMCONTEXTSERVICE_EMPTY_GUID = "00000000-0000-0000-0000-000000000000";
export class FunctionPropertyValue {
constructor(v) {
this.v = '';
this.v = v;
}
functionPropertyValueAsString() {
return this.v;
}
}
export class Entity {
constructor(pluralName, keyName, updateable = false, logicalname = null) {
this._updateable = false;
this._pluralName = pluralName;
this._keyName = keyName;
this._updateable = updateable;
if (logicalname != null && logicalname != '') {
this._logicalName = logicalname;
}
else {
let x = this._pluralName != null ? this._pluralName.toLowerCase() : null;
switch (x) {
case "emails":
this._logicalName = "email";
break;
case "appointments":
this._logicalName = "appointment";
break;
case "letters":
this._logicalName = "letter";
break;
case "phonecalls":
this._logicalName = "phonecall";
break;
case "tasks":
this._logicalName = "task";
break;
case "campaignresponses":
this._logicalName = "campaignresponse";
break;
default: {
if (this._keyName.toLowerCase() == "activityid" && x != null) {
this._logicalName = x.substr(0, x.length - 1);
}
else {
this._logicalName = this._keyName.substr(0, (this._keyName.length - 2)).toLowerCase();
}
break;
}
}
}
}
ToEntityReference(associatednavigationproperty = null) {
return new EntityReference(this.id, this._pluralName, associatednavigationproperty, this._logicalName);
}
ignoreColumn(prop) {
if (prop == "_pluralName" || prop == "_logicalName" || prop == "_keyName" || prop == "id" || prop == '_updateable' || prop == '$expand' || prop == 'access') {
return true;
}
return false;
}
columns(webapi = false) {
let result = [];
let columns = this._keyName;
for (var prop in this) {
if (prop == this._keyName)
continue;
if (this.ignoreColumn(prop))
continue;
let v = this[prop];
if (typeof v !== 'undefined' && v != null) {
if (Array.isArray(v)) {
continue;
}
if (v instanceof Entity) {
continue;
}
}
if (this.hasOwnProperty(prop)) {
if (webapi && this[prop] instanceof EntityReference) {
result.push("_" + prop + "_value");
}
else {
result.push(prop);
}
}
}
return result;
}
}
export class Entities extends Array {
constructor(fType, tType, refName, leftToRight, t) {
super(0);
Object.setPrototypeOf(this, new.target.prototype);
this.parentType = fType;
this.childType = tType;
this.refName = refName;
this.leftToRight = leftToRight;
this.push(t);
}
add(entity) {
let fromType = this.parentType;
let fromId = this.parentId;
let toType = this.childType;
let toId = entity.id;
if (!this.leftToRight) {
fromType = this.childType;
fromId = entity.id;
toType = this.parentType;
toId = this.parentId;
}
return this.xrmService.associate(fromType, fromId, toType, toId, this.refName).pipe(map(r => {
this.push(entity);
return null;
}));
}
remove(entity) {
let fromType = this.parentType;
let fromId = this.parentId;
let toType = this.childType;
let toId = entity.id;
if (!this.leftToRight) {
fromType = this.childType;
fromId = entity.id;
toType = this.parentType;
toId = this.parentId;
}
return this.xrmService.disassociate(fromType, fromId, toType, toId, this.refName).pipe(map(r => {
var index = this.indexOf(entity);
if (index >= 0) {
this.splice(index, 1);
}
return null;
}));
}
}
export class EntityReference {
constructor(id = null, pluralName = null, associatednavigationproperty = null, logicalname = null) {
this.id = id;
this.pluralName = pluralName;
this.associatednavigationproperty = associatednavigationproperty;
this.logicalname = logicalname;
if (this.pluralName != null && logicalname == null) {
switch (this.pluralName.toLowerCase()) {
case "emails":
this.logicalname = "email";
break;
case "appointments":
this.logicalname = "appointment";
break;
case "letters":
this.logicalname = "letter";
break;
case "phonecalls":
this.logicalname = "phonecall";
break;
case "tasks":
this.logicalname = "task";
break;
default: {
this.logicalname = this.pluralName.substr(0, (this.pluralName.length - 1)).toLowerCase();
break;
}
}
}
}
replace(v1, v2) {
this.id.replace(v1, v2);
}
meta(pluralName, associatednavigationproperty) {
this.pluralName = pluralName;
this.associatednavigationproperty = associatednavigationproperty;
return this;
}
clone() {
let result = new EntityReference();
result.id = this.id;
result.name = this.name;
result.logicalname = this.name;
result.associatednavigationproperty = this.associatednavigationproperty;
return result;
}
associatednavigationpropertyname() {
if (this.associatednavigationproperty == null || this.associatednavigationproperty == '') {
throw 'navigation property has not been set for this EntityReference instance';
}
if (this.associatednavigationproperty.endsWith('@odata.bind')) {
return this.associatednavigationproperty;
}
return this.associatednavigationproperty + '@odata.bind';
}
equals(ref) {
return this.id == ref.id && this.logicalname == ref.logicalname;
}
toJsonProperty() {
return { '@odata.id': "'" + this.pluralName + "(" + this.id.replace("{", "").replace("}", "") + ")" + "'" };
}
static same(ref1, ref2) {
if (ref1 == null && ref2 == null) {
return true;
}
let id1 = null;
let id2 = null;
if (ref1 != null)
id1 = ref1.id;
if (ref2 != null)
id2 = ref2.id;
return id1 == id2;
}
}
export class OptionSetValue {
constructor(value = null, name = null) {
this.value = value;
this.name = name;
}
equals(o) {
if (this.value == null && (o == null || o.value == null))
return true;
return this.value == o.value;
}
clone() {
let r = new OptionSetValue();
r.name = this.name;
r.value = this.value;
return r;
}
toJsonProperty() {
return this.value;
}
static same(o1, o2) {
if (o1 == null && o2 == null)
return true;
let v1 = null;
let v2 = null;
if (o1 != null)
v1 = o1.value;
if (o2 != null)
v2 = o2.value;
return v1 == v2;
}
}
export var Operator;
(function (Operator) {
Operator[Operator["And"] = 0] = "And";
Operator[Operator["Or"] = 1] = "Or";
})(Operator || (Operator = {}));
export var Comparator;
(function (Comparator) {
Comparator[Comparator["Equals"] = 1] = "Equals";
Comparator[Comparator["NotEquals"] = 2] = "NotEquals";
Comparator[Comparator["Contains"] = 3] = "Contains";
Comparator[Comparator["NotContains"] = 4] = "NotContains";
Comparator[Comparator["DoesNotContainsData"] = 5] = "DoesNotContainsData";
Comparator[Comparator["ContainsData"] = 6] = "ContainsData";
Comparator[Comparator["StartsWith"] = 7] = "StartsWith";
Comparator[Comparator["NotStartsWith"] = 8] = "NotStartsWith";
Comparator[Comparator["EndsWith"] = 9] = "EndsWith";
Comparator[Comparator["NotEndsWith"] = 10] = "NotEndsWith";
Comparator[Comparator["GreaterThan"] = 11] = "GreaterThan";
Comparator[Comparator["GreaterThanOrEqual"] = 12] = "GreaterThanOrEqual";
Comparator[Comparator["LessThan"] = 13] = "LessThan";
Comparator[Comparator["LessThanOrEQual"] = 14] = "LessThanOrEQual";
Comparator[Comparator["Useroruserhierarchy"] = 100] = "Useroruserhierarchy";
Comparator[Comparator["Userteams"] = 101] = "Userteams";
})(Comparator || (Comparator = {}));
export class ColumnBuilder {
constructor() {
this.columns = null;
this.hasEntityReference = false;
}
}
export class Filter {
constructor() {
this["raw"] = false;
}
toQueryString(prototype) {
if (this["raw"] == true) {
return this.field;
}
if (this.operator == 100) {
return 'ownerid eq-useroruserhierarchy';
}
if (this.operator == 101) {
return 'ownerid eq-userteams';
}
let result = '';
let _f = this.field;
if (this.operator == Comparator.Equals && this.value == null) {
// this.operator = Comparator.DoesNotContainsData;
}
if (this.operator == Comparator.NotEquals && this.value == null) {
// this.operator = Comparator.ContainsData;
}
switch (this.operator) {
case Comparator.ContainsData: {
return _f + ' ne null';
}
case Comparator.DoesNotContainsData: {
return _f + ' eq null';
}
}
if (this.value == null) {
throw new Error("value is required for operation type " + this.operator);
}
let _v = "'" + this.value + "'";
if (typeof this.value == 'number') {
_v = this.value.toString();
}
if (typeof this.value == 'boolean') {
_v = this.value.valueOf() ? 'true' : 'false';
}
if (prototype[this.field] instanceof OptionSetValue) {
if (this.value != null && this.value.hasOwnProperty('value')) {
_v = this.value.value;
}
}
let isEref = false;
if (prototype[this.field] instanceof EntityReference) {
_f = "_" + this.field + "_value";
if (this.value != null) {
if (typeof this.value == 'string') {
_v = this.value.replace('{', '').replace('}', '');
}
else {
_v = this.value.id.replace('{', '').replace('}', '');
}
}
isEref = true;
}
if (!isEref && _f.startsWith('_') && _f.endsWith('_value') && _v != null && this.value != null) {
if (typeof this.value === "string") {
_v = this.value.replace('{', '').replace('}', '');
}
else {
if (this.value.hasOwnProperty("id")) {
_v = this.value.id.replace('{', '').replace('}', '');
}
}
}
var isDate = false;
if (prototype[this.field] instanceof Date) {
if (this.value instanceof Date) {
_v = this.value.toISOString();
}
else {
_v = this.value.toString();
}
isDate = true;
}
if (!isDate && this.value instanceof Date) {
_v = this.value.toISOString();
isDate = true;
}
if (_f == prototype._keyName) {
_v = this.value.replace('{', '').replace('}', '');
}
if (_v != null && _v != '') {
_v = encodeURIComponent(_v);
}
switch (this.operator) {
case Comparator.Equals: {
return _f + ' eq ' + _v;
}
case Comparator.NotEquals: {
return _f + ' ne ' + _v;
}
case Comparator.GreaterThan: {
return _f + ' gt ' + _v;
}
case Comparator.GreaterThanOrEqual: {
return _f + ' ge ' + _v;
}
case Comparator.LessThan: {
return _f + ' lt ' + _v;
}
case Comparator.LessThanOrEQual: {
return _f + ' le ' + _v;
}
case Comparator.Contains: {
return "contains(" + _f + "," + _v + ")";
}
case Comparator.NotContains: {
return "not contains(" + _f + "," + _v + ")";
}
case Comparator.StartsWith: {
return "startswith(" + _f + "," + _v + ")";
}
case Comparator.NotStartsWith: {
return "not startswith(" + _f + "," + _v + ")";
}
case Comparator.EndsWith: {
return "endswith(" + _f + "," + _v + ")";
}
case Comparator.NotEndsWith: {
return "not endswith(" + _f + "," + _v + ")";
}
}
return result;
}
toFetcmXml() {
if (this.operator == 100) {
return '<condition attribute="ownerid" operator="eq-useroruserhierarchy" />';
}
if (this.operator == 101) {
return '<condition attribute="ownerid" operator="eq-userteams" />';
}
var result = "";
let _v = this.value;
if (this.value != null) {
if (typeof this.value == 'number') {
_v = this.value.toString();
}
if (typeof this.value == 'boolean') {
_v = this.value.valueOf() ? 'true' : 'false';
}
if (this.value != null && this.value.hasOwnProperty('value')) {
_v = this.value.value;
}
}
if (_v == null) {
this.adjustOperation();
}
var op = "eq";
switch (this.operator) {
case Comparator.Contains:
op = "like";
_v = "%" + _v + "%";
break;
case Comparator.ContainsData:
op = "not-null";
break;
case Comparator.DoesNotContainsData:
op = "null";
break;
case Comparator.EndsWith:
op = "like";
_v = "%" + _v;
null;
break;
case Comparator.Equals:
op = "eq";
break;
case Comparator.GreaterThan:
op = "gt";
break;
case Comparator.GreaterThanOrEqual:
op = "ge";
break;
case Comparator.LessThan:
op = "lt";
break;
case Comparator.LessThanOrEQual:
op = "le";
break;
case Comparator.NotContains:
op = "not-like";
"%" + _v + "%";
break;
case Comparator.NotEndsWith:
op = "not-like";
"&" + _v;
break;
case Comparator.NotEquals:
op = "ne";
break;
case Comparator.NotStartsWith:
op = "not-like";
_v = _v + "%";
break;
case Comparator.StartsWith:
op = "like";
_v = _v + "%";
break;
default: throw "condition type not supported in fetch xml " + this.operator;
}
var aliasString = "";
if (this.alias != null) {
aliasString = " entityname='" + this.alias + "'";
}
if (_v != null) {
result += "<condition" + aliasString + " attribute='" + this.field + "' operator='" + op + "' value='" + _v + "' />";
}
else {
result += "<condition" + aliasString + " attribute='" + this.field + "' operator='" + op + "' />";
}
return result;
}
adjustOperation() {
if (this.operator == Comparator.Equals) {
this.operator = Comparator.DoesNotContainsData;
}
else if (this.operator == Comparator.NotEquals) {
this.operator = Comparator.ContainsData;
}
if (this.operator != Comparator.DoesNotContainsData && this.operator != Comparator.ContainsData) {
throw "null in condition value is only supported for containsdata and doesnotdontainsdata:" + this.field + "/" + (this.alias != null ? "alias:" + this.alias : "") + "/" + this.operator;
}
}
}
export class Condition {
constructor(operator = Operator.And) {
this.operator = Operator.And;
this.operator = operator;
this.filter = [];
this.children = [];
}
where(field, opr, value = null) {
let f = new Filter();
f.field = field;
f.value = value;
f.operator = opr;
this.filter.push(f);
return this;
}
alias(alias, field, opr, value = null) {
let f = new Filter();
f.alias = alias;
f.field = field;
f.value = value;
f.operator = opr;
this.filter.push(f);
return this;
}
group(opr) {
let result = new Condition(opr);
result.parent = this;
this.children.push(result);
return result;
}
isActive() {
return this.where("statecode", Comparator.Equals, 0);
}
isInactive() {
return this.where("statecode", Comparator.Equals, 1);
}
owningUserIsCurrentUserOrHirachy() {
return this.where("ownerid", 100);
}
currentUserIsMemberOfOwningTeam() {
return this.where("ownerid", 101);
}
raw(filter) {
let result = new Filter();
result.field = filter;
result["raw"] = true;
this.filter.push(result);
return this;
}
toQueryString(prototype) {
if ((this.children == null || this.children.length == 0) && (this.filter == null || this.filter.length == 0)) {
return null;
}
let me = this;
let result = '';
let opr = '';
if (this.filter != null && this.filter.length > 0) {
this.filter.forEach(r => {
result += opr + r.toQueryString(prototype);
if (me.operator == Operator.And) {
opr = ' and ';
}
else {
opr = ' or ';
}
});
}
if (this.children != null && this.children.length > 0) {
this.children.forEach(c => {
result += opr + "(" + c.toQueryString(prototype) + ")";
if (me.operator == Operator.And) {
opr = ' and ';
}
else {
opr = ' or ';
}
});
}
return result;
}
toFetchXml() {
let result = "";
result += "<filter type='" + (this.operator == Operator.And ? "and" : "or") + "'>";
if (this.filter != null && this.filter.length > 0)
this.filter.forEach(f => {
result += f.toFetcmXml();
});
if (this.children != null && this.children.length > 0) {
this.children.forEach(c => {
result += c.toFetchXml();
});
}
result += "</filter>";
return result;
}
}
class XrmTransactionItem {
constructor(type, prototype, instance, field = null, value = null) {
this.id = null;
this.type = type;
this.prototype = prototype;
this.instance = instance;
this.field = field;
this.value = value;
}
}
export class XrmTransaction {
constructor() {
this.oprs = [];
}
put(prototype, instance, field, value) {
this.oprs.push(new XrmTransactionItem("put", prototype, instance, field, value));
}
delete(instance) {
this.oprs.push(new XrmTransactionItem("delete", null, instance));
}
create(prototype, instance) {
this.oprs.push(new XrmTransactionItem("create", prototype, instance));
}
update(prototype, instance) {
this.oprs.push(new XrmTransactionItem("update", prototype, instance));
}
}
export class XrmAccess {
constructor(lazy = false) {
this.lazy = null;
this.resolved = null;
this.lazy = lazy;
}
}
class ExpandProperty {
}
export class XrmContextService {
constructor(http, xrmService) {
this.http = http;
this.xrmService = xrmService;
this.context = {};
this.changemanager = {};
this.tick = new Date().valueOf();
this.includeOriginalPayload$ = false;
this.pageCookieMatch = /[-pagingcookie=\"][a-z,A-Z,0-9,%-_.~]+/g;
}
setVersion(v) {
this.xrmService.setVersion(v);
}
includeOroginalPayload(v) {
this.includeOriginalPayload$ = v;
}
getContext() {
return this.xrmService.getContext();
}
getCurrentKey(repeatForCreateForm = false) {
return this.xrmService.getCurrentKey(repeatForCreateForm);
}
getServiceUrl() {
return this.getContext().getClientUrl() + this.xrmService.apiUrl;
}
getCurrentUserId() {
return this.xrmService.getCurrentUserId();
}
get(prototype, id) {
let me = this;
let columnDef = this.columnBuilder(prototype);
let expand = null;
let eps = this.getExpandProperties(prototype);
if (eps != null && eps.length > 0) {
let comma = ",";
eps.forEach(ep => {
if (expand == null) {
expand = this.$expandToExpand(ep);
}
else {
if (expand.additional == null) {
expand.additional = [];
}
expand.additional.push(this.$expandToExpand(ep));
}
});
}
return this.xrmService.get(prototype._pluralName, id, columnDef.columns, expand).pipe(map(r => {
return me.resolve(prototype, r, prototype._updateable, null);
}));
}
debug(setting) {
this.xrmService.debug = setting;
}
count(xml) {
let me = this;
let headers = new HttpHeaders({ 'Accept': 'application/json' });
if (this.xrmService.token != null) {
headers = headers.append("Authorization", "Bearer " + this.xrmService.token);
}
headers = headers.append("OData-MaxVersion", "4.0");
headers = headers.append("OData-Version", "4.0");
headers = headers.append("Content-Type", "application/json; charset=utf-8");
headers = headers.append("Prefer", "odata.include-annotations=\"*\"");
headers = headers.append("Cache-Control", "no-cache");
let options = {
headers: headers
};
let fetchxml = xml.toCountFetchXml();
let url = this.getContext().getClientUrl() + this.xrmService.apiUrl + xml.entity().entityPrototype._pluralName + "?fetchXml=" + encodeURIComponent(fetchxml);
this.xrmService.log(fetchxml);
this.xrmService.log(url);
return new Observable(obs => {
this.http.get(this.forceHTTPS(url), options).toPromise()
.then(response => {
if (response.value && response.value.length && response.value.length == 1) {
obs.next(response.value[0].count);
}
else {
obs.next(0);
}
})
.catch(e => {
if (e["error"] && e["error"]["error"] && e["error"]["error"]["code"]) {
var val = e["error"]["error"]["code"];
if (val != null && val.toString() == "0x8004e023") {
obs.next(50000);
return;
}
}
throwError(e);
});
});
}
fetch(xml) {
let me = this;
let headers = new HttpHeaders({ 'Accept': 'application/json' });
if (this.xrmService.token != null) {
headers = headers.append("Authorization", "Bearer " + this.xrmService.token);
}
headers = headers.append("OData-MaxVersion", "4.0");
headers = headers.append("OData-Version", "4.0");
headers = headers.append("Content-Type", "application/json; charset=utf-8");
headers = headers.append("Prefer", "odata.include-annotations=\"*\"");
headers = headers.append("Cache-Control", "no-cache");
let options = {
headers: headers
};
let fetchxml = xml.toFetchXml();
let url = this.getContext().getClientUrl() + this.xrmService.apiUrl + xml.entity().entityPrototype._pluralName + "?fetchXml=" + encodeURIComponent(fetchxml);
this.xrmService.log(fetchxml);
this.xrmService.log(url);
let localPrototype = xml.entity().entityPrototype;
return this.http.get(this.forceHTTPS(url), options).pipe(map(response => {
let result = me.resolveFetchResult(localPrototype, response, xml.count, [url], 0, xml);
return result;
}));
}
fetchxml(prototype, fetchxml) {
let me = this;
let headers = new HttpHeaders({ 'Accept': 'application/json' });
if (this.xrmService.token != null) {
headers = headers.append("Authorization", "Bearer " + this.xrmService.token);
}
headers = headers.append("OData-MaxVersion", "4.0");
headers = headers.append("OData-Version", "4.0");
headers = headers.append("Content-Type", "application/json; charset=utf-8");
headers = headers.append("Prefer", "odata.include-annotations=\"*\"");
headers = headers.append("Cache-Control", "no-cache");
let options = {
headers: headers
};
let url = this.getContext().getClientUrl() + this.xrmService.apiUrl + prototype._pluralName + "?fetchXml=" + encodeURIComponent(fetchxml);
this.xrmService.log(fetchxml);
this.xrmService.log(url);
return this.http.get(this.forceHTTPS(url), options).pipe(map(response => {
let result = me.resolveQueryResult(prototype, response, 0, [url], 0, null);
return result;
}));
}
query(prototype, condition, orderBy = null, top = 0, count = false) {
let me = this;
let fields = this.columnBuilder(prototype).columns;
let con = condition;
let filter = null;
if (condition != null) {
while (con.parent != null) {
con = con.parent;
}
;
filter = con.toQueryString(prototype);
}
let headers = new HttpHeaders({ 'Accept': 'application/json' });
if (this.xrmService.token != null) {
headers = headers.append("Authorization", "Bearer " + this.xrmService.token);
}
headers = headers.append("OData-MaxVersion", "4.0");
headers = headers.append("OData-Version", "4.0");
headers = headers.append("Content-Type", "application/json; charset=utf-8");
headers = headers.append("Prefer", "odata.include-annotations=\"*\"");
if (top > 0) {
headers = headers.append("Prefer", "odata.maxpagesize=" + top.toString());
}
headers = headers.append("Cache-Control", "no-cache");
let options = {
headers: headers
};
let url = this.getContext().getClientUrl() + this.xrmService.apiUrl + prototype._pluralName;
if ((fields != null && fields != '') || (filter != null && filter != '') || (orderBy != null && orderBy != '') || top > 0) {
url += "?";
}
let sep = '';
if (fields != null && fields != '') {
url += '$select=' + fields;
sep = '&';
}
if (filter != null && filter != '') {
url += sep + '$filter=' + filter;
sep = '&';
}
if (orderBy != null && orderBy != '') {
url += sep + '$orderby=' + orderBy;
sep = '&';
}
if (count) {
url += sep + '$count=true';
sep = '&';
}
this.xrmService.log(url);
return this.http.get(this.forceHTTPS(url), options).pipe(map(response => {
let result = me.resolveQueryResult(prototype, response, top, [url], 0, null);
return result;
}));
}
create(prototype, instance) {
let newr = this.prepareNewInstance(prototype, instance);
this.xrmService.log(newr);
return this.xrmService.create(prototype._pluralName, newr).pipe(map(_response => {
let response = _response;
this.xrmService.log(response);
if (response != null) {
if (response.hasOwnProperty('$keyonly')) {
instance._pluralName = prototype._pluralName;
instance._logicalName = prototype._logicalName;
instance._keyName = prototype._keyName;
instance.id = _response.id;
instance._updateable = true;
let key = response._pluralName + ':' + response.id;
for (let prop in prototype) {
if (typeof prototype[prop] === 'function') {
response[prop] = prototype[prop];
continue;
}
}
this.context[key] = instance;
this.updateCM(prototype, instance);
return instance;
}
else {
this.resolveNewInstance(prototype, instance, response);
return this.resolve(prototype, response, true, null);
}
}
return null;
}));
}
createAll(prototype, instances) {
if (prototype != null && instances != null && instances.length > 0) {
let trans = new XrmTransaction();
instances.forEach(r => {
trans.create(prototype, r);
});
return this.commit(trans);
}
throw "You must parse a prototype and at least one instance to be created";
}
update(prototype, instance, deleteReferenceAsEmptyGuid = false) {
let me = this;
let upd = this.prepareUpdate(prototype, instance, deleteReferenceAsEmptyGuid);
if (upd == null) {
upd = {};
}
this.xrmService.log(upd);
let fields = this.columnBuilder(prototype).columns;
return this.xrmService.update(prototype._pluralName, upd, instance.id, fields).pipe(map(response => {
var wasnull = response[prototype._keyName] == null || response[prototype._keyName] == '';
if (wasnull || this.getContext().getVersion().startsWith("8.0") || this.getContext().getVersion().startsWith("8.1")) {
this.xrmService.log('version 8.0 update');
this.updateCM(prototype, instance);
return instance;
}
this.xrmService.log('version 9.0 or higher update');
return me.resolve(prototype, response, true, null);
}));
}
put(prototype, instance, field, value = 'ko-@value-not-parsed!') {
let v = value;
if (value == 'ko-@value-not-parsed!') {
v = instance[field];
}
let pvx = this.preparePutValue(prototype, field, v);
return this.xrmService.put(prototype._pluralName, instance.id, pvx.field, pvx.value, pvx.propertyAs).pipe(map(response => {
if (value != 'ko-@value-not-parsed!') {
this.assignValue(prototype, instance, field, value);
return null;
}
}));
}
putAll(prototype, instances, field, value) {
if (instances != null && instances.length == 1) {
return this.put(prototype, instances[0], field, value);
}
if (instances != null && instances.length > 1) {
let trans = new XrmTransaction();
let pvx = this.preparePutValue(prototype, field, value);
instances.forEach(r => {
trans.put(prototype, r, field, pvx);
});
return this.commit(trans);
}
throw 'you must parse at least one instance in the instances array';
}
delete(t) {
let me = this;
return this.xrmService.delete(t._pluralName, t.id).pipe(map(r => {
let key = t._pluralName + ":" + t.id;
if (me.context.hasOwnProperty(key)) {
delete me.context[key];
}
return null;
}));
}
deleteAll(instances) {
if (instances != null && instances.length == 1) {
return this.delete(instances[0]);
}
if (instances != null && instances.length > 0) {
let trans = new XrmTransaction();
instances.forEach(r => {
trans.delete(r);
});
return this.commit(trans);
}
throw 'you must parse at least one instance in the instances array';
}
markChangesCommitted(prototype, instance) {
this.updateCM(prototype, instance);
}
func(name, data, entity = null) {
var parameters = data;
if (parameters != null) {
parameters = this.toFuncParameterString(data);
}
if (entity == null) {
return this.xrmService.func(name, parameters);
}
else {
return this.xrmService.func(name, parameters, entity._pluralName, entity.id);
}
}
action(name, data, entity = null) {
if (entity != null) {
return this.xrmService.action(name, data, entity._pluralName, entity.id);
}
else {
return this.xrmService.action(name, data);
}
}
commit(transaction) {
let oprs = transaction["oprs"];
if (oprs != null && oprs.length > 0) {
this.tick++;
let batch = 'batch_KO' + new Date().valueOf() + "$" + this.tick.toString();
let change = 'changeset_KO' + new Date().valueOf() + "!" + this.tick.toString();
let headers = new HttpHeaders({ 'Accept': 'application/json' });
if (this.xrmService.token != null) {
headers = headers.append("Authorization", "Bearer " + this.xrmService.token);
}
headers = headers.append("Content-Type", "multipart/mixed;boundary=" + batch);
headers = headers.append("OData-MaxVersion", "4.0");
headers = headers.append("OData-Version", "4.0");
let body = '--' + batch + "\n";
body += "Content-Type: multipart/mixed;boundary=" + change + "\n";
body += "\n";
let count = 1;
oprs.forEach(r => {
r.id = count;
if (r.type == "put") {
let nextV = this.preparePutValue(r.prototype, r.field, r.value);
body += '--' + change + "\n";
body += "Content-Type: application/http\n";
body += "Content-Transfer-Encoding:binary\n";
body += "Content-ID: " + count.toString() + "\n";
body += "\n";
if (nextV.value != null) {
body += "PUT " + this.getContext().$devClientUrl() + r.prototype._pluralName + "(" + r.instance.id + ")/" + nextV.field + " HTTP/1.1\n";
}
else {
body += "DELETE " + this.getContext().$devClientUrl() + r.instance._pluralName + "(" + r.instance.id + ")/" + nextV.field + " HTTP/1.1\n";
}
let xr = {};
if (nextV.value != null) {
xr[nextV.propertyAs] = nextV.value;
}
body += "Content-Type: application/json;type=entry\n";
body += "\n";
body += JSON.stringify(xr) + "\n";
}
if (r.type == "delete") {
body += '--' + change + "\n";
body += "Content-Type: application/http\n";
body += "Content-Transfer-Encoding:binary\n";
body += "Content-ID: " + count.toString() + "\n";
body += "\n";
body += "DELETE " + this.getContext().$devClientUrl() + r.instance._pluralName + "(" + r.instance.id + ")" + " HTTP/1.1\n";
body += "Content-Type: application/json;type=entry\n";
body += "\n";
body += "{ }\n";
}
if (r.type == "create") {
let nextI = this.prepareNewInstance(r.prototype, r.instance);
body += '--' + change + "\n";
body += "Content-Type: application/http\n";
body += "Content-Transfer-Encoding:binary\n";
body += "Content-ID: " + count.toString() + "\n";
body += "\n";
body += "POST " + this.getContext().$devClientUrl() + r.prototype._pluralName + " HTTP/1.1\n";
body += "Content-Type: application/json;type=entry\n";
body += "\n";
body += JSON.stringify(nextI) + "\n";
}
if (r.type == "update") {
let nextU = this.prepareUpdate(r.prototype, r.instance, false);
if (nextU != null) {
let fields = "?$select=" + this.columnBuilder(r.prototype).columns;
body += '--' + change + "\n";
body += "Content-Type: application/http\n";
body += "Content-Transfer-Encoding:binary\n";
body += "Content-ID: " + count.toString() + "\n";
body += "\n";
body += "PATCH " + this.getContext().$devClientUrl() + r.prototype._pluralName + "(" + r.instance.id + ")" + fields + " HTTP/1.1\n";
body += "Content-Type: application/json;type=entry\n";
body += "\n";
body += JSON.stringify(nextU) + "\n";
}
}
count++;
});
body += '--' + change + '--\n';
body += "\n";
body += "--" + batch + "--\n";
this.xrmService.log(body);
let url = this.getContext().getClientUrl() + this.xrmService.apiUrl + "$batch";
this.xrmService.log(url);
return this.http.post(this.forceHTTPS(url), body, { headers: headers, responseType: "text" }).pipe(map(_txt => {
let txt = _txt;
this.xrmService.log(txt);
oprs.forEach(r => {
if (r.type == 'put') {
this.assignValue(r.prototype, r.instance, r.field, r.value);
this.updateCM(r.prototype, r.instance);
}
if (r.type == 'update') {
this.updateCM(r.prototype, r.instance);
}
if (r.type == 'delete') {
let key = r.instance._pluralName + ':' + r.instance.id;
delete this.context[key];
delete this.changemanager[key];
}
});
let index = 0;
txt.split('\n').forEach(l => {
if (l.startsWith('Content-ID:')) {
index = Number(l.split(':')[1].trim());
return true;
}
if (l.startsWith('OData-EntityId:')) {
let opr = oprs.find(o => o.id == index);
if (opr != null && opr.type == 'create') {
let id = l.split('OData-EntityId:')[1].split('/' + opr.prototype._pluralName + '(')[1].replace(')', '').trim();
opr.instance.id = id;
let key = opr.prototype._pluralName + ':' + opr.instance.id;
this.context[key] = opr.instance;
opr.instance._updateable = true;
this.updateCM(opr.prototype, opr.instance);
}
}
return true;
});
return null;
}));
}
throw 'you must parse at least one operation to the transaction by calling put, create, update or delete';
}
log(type) {
if (type == 'context') {
this.xrmService.log(this.context);
return;
}
if (type == 'xrmcontext') {
this.xrmService.log(this.getContext());
return;
}
if (type == 'url') {
this.xrmService.log(this.getContext().getClientUrl());
}
if (type == 'version') {
this.xrmService.log(this.getContext().getVersion());
}
this.xrmService.log('xrmContextService supported the current log types: context, xrmcontext, url, version');
}
clone(prototype, instance) {
let r = new Entity(prototype._pluralName, prototype._keyName);
for (let prop in prototype) {
if (prototype.ignoreColumn(prop))
continue;
let pv = prototype[prop];
if (typeof pv == 'function') {
r[prop] = pv;
continue;
}
let v = instance[prop];
if (v == null) {
r[prop] = null;
continue;
}
if (v instanceof Date) {
r[prop] = new Date(v.valueOf());
continue;
}
if (v instanceof EntityReference) {
r[prop] = v.clone();
continue;
}
if (v instanceof OptionSetValue) {
r[prop] = v.clone();
continue;
}
if (v != null) {
r[prop] = v;
}
}
r.id = null;
this.xrmService.log('clone');
this.xrmService.log(r);
return r;
}
applyAccess(prototype, instance) {
if (!prototype.hasOwnProperty('access'))
throw 'The metadata must define a property "access" of type XrmAccess';
if (instance.hasOwnProperty('access') && instance['access']['resolved']) {
return Observable.create(obs => obs.next(instance));
}
return this.mapAccess(prototype, instance);
}
prepareUpdate(prototype, instance, deletedReferenceAsEmptyGuid) {
let me = this;
let upd = {};
let countFields = 0;
let key = instance._pluralName + ':' + instance.id;
let cm = this.changemanager[key];
if (typeof cm === 'undefined' || cm === null) {
throw 'the object is not under change control and cannot be updated within this context';
}
for (let prop in prototype) {
if (prototype.hasOwnProperty(prop) && typeof prototype[prop] != 'function') {
if (prototype.ignoreColumn(prop))
continue;
let prevValue = cm[prop];
let newValue = instance[prop];
if ((prevValue === 'undefined' || prevValue === null) && (newValue === 'undefined' || newValue === null))
continue;
if (instance[prop] instanceof EntityReference) {
if (instance[prop].associatednavigationproperty != null && instance[prop].associatednavigationproperty != '' && instance[prop]['pluralName'] != null && instance[prop]['pluralName'] != '') {
if (!EntityReference.same(prevValue, newValue)) {
if (deletedReferenceAsEmptyGuid && newValue != null && (newValue["id"] == null || newValue["id"] == '')) {
newValue["id"] = XRMCONTEXTSERVICE_EMPTY_GUID;
}
if (newValue != null && newValue["id"] != null && newValue["id"] != '') {
let x = newValue["id"];
x = x.replace('{', '').replace('}', '');
upd[instance[prop]['associatednavigationpropertyname']()] = '/' + instance[prop]['pluralName'] + '(' + x + ')';
}
else {
// this does not work, navigation properties can only be removed with SDK deleted method
upd["_" + instance[prop]['logicalname'].toLowerCase() + "_value"] = null;
}
countFields++;
}
continue;
}
}
if (prototype[prop] instanceof EntityReference) {
if (!EntityReference.same(prevValue, newValue)) {
if ((newValue == null || newValue["id"] == null || newValue["id"] == "") && deletedReferenceAsEmptyGuid) {
newValue["id"] = XRMCONTEXTSERVICE_EMPTY_GUID;
}
if (newValue != null && newValue["id"] != null && newValue["id"] != '') {
let x = newValue["id"];
x = x.replace('{', '').replace('}', '');
upd[prototype[prop]['associatednavigationpropertyname']()] = '/' + prototype[prop]['pluralName'] + '(' + x + ')';
}
else {
upd["_" + instance[prop]['logicalname'].toLowerCase() + "_value"] = null;
}
countFields++;
}
continue;
}
if (prototype[prop] instanceof OptionSetValue) {
if (!OptionSetValue.same(prevValue, newValue)) {
let o = newValue;
if (o == null || o.value == null) {
upd[prop.toString()] = null;
}
else {
upd[prop.toString()] = o.value;
}
countFields++;
}
continue;
}
if (prototype[prop] instanceof Date) {
if (prevValue instanceof Date && newValue instanceof Date) {
var d1 = prevValue.toISOString();
var d2 = newValue.toISOString();
if (d1 == d2) {
continue;
}
}
if (prevValue != newValue) {
this.xrmService.log('pre-value-update-date:[' + prevValue + ']/[' + newValue + ']');
if (newValue == null) {
upd[prop.toString()] = null;