Skip to content

Instantly share code, notes, and snippets.

@iwannabebot
Last active February 8, 2019 13:56

Revisions

  1. iwannabebot revised this gist Feb 8, 2019. 1 changed file with 1704 additions and 616 deletions.
    2,320 changes: 1,704 additions & 616 deletions xrm-meta-sdk.ts
    Original file line number Diff line number Diff line change
    @@ -1,21 +1,8 @@
    import {
    XrmMetaOneToManyRelation,
    XrmMetaManyToManyRelation,
    IQueryRelation,
    QueryColumnType,
    QueryRelationType,
    IQueryTable,
    XrmEntityFilter,
    XrmMetaEntity,
    DisplayName
    XrmMetaAttribute } from "xrm-meta-models";

    import { Observable } from "rxjs";
    import { HttpClient, HttpHeaders } from "@angular/common/http";

    // tslint:disable:no-bitwise
    // tslint:disable:max-line-length

    export class XrmMetaSdk {
    private _arrayElements = [
    "Attributes",
    @@ -123,23 +110,23 @@ export class XrmMetaSdk {
    } else {
    const __this = _this;
    __this.RetrieveRelations(
    table.Id, null,
    (entity) => {
    table.Id,
    entity => {
    const o2ms = entity.OneToManyRelationships;
    const m2os = entity.ManyToOneRelationships;
    const m2ms = entity.ManyToManyRelationships;
    subscriber.next(this.ParseRelations(table.Id, o2ms, m2os, m2ms));
    subscriber.complete();
    }, (err) => {
    },
    err => {
    subscriber.error(err);
    }
    );
    }
    });
    }

    private ParseTables(
    entities: XrmMetaEntity[]): IQueryTable[] {
    private ParseTables(entities: XrmMetaEntity[]): IQueryTable[] {
    const tables = entities.map(entity => {
    return {
    Alias: "",
    @@ -163,12 +150,7 @@ export class XrmMetaSdk {
    Id: att.LogicalName,
    Name: this.readLabel(att),
    Table: tableId,
    OptionSet: att.OptionsSet.Options.map(op => {
    return {
    Key: this.readOptionLabel(op),
    Value: op.Value
    } as IQueryOption;
    }),
    OptionSet: this.readOption(att),
    Type: this.readAttributeType(att.AttributeType),
    Targets: att.Targets
    } as IQueryColumn;
    @@ -180,7 +162,7 @@ export class XrmMetaSdk {
    tableId: string,
    o2ms: XrmMetaOneToManyRelation[],
    m2os: XrmMetaOneToManyRelation[],
    m2ms: XrmMetaManyToManyRelation[],
    m2ms: XrmMetaManyToManyRelation[]
    ): IQueryRelation[] {
    const relations: IQueryRelation[] = [];
    for (let i = 0; i < o2ms.length; i++) {
    @@ -239,82 +221,1316 @@ export class XrmMetaSdk {
    }
    return relations;
    }
    //#region internal helpers

    private readLabel(metaRecord: any): string {
    try {
    const disp = metaRecord.Label as DisplayName;
    return disp.UserLocalizedLabel.Label;
    } catch {
    let label = null;
    public GetOperatorsOfColumn(column: IQueryColumn): OperatorMap {
    const operators: OperatorMap = new OperatorMap();
    switch (column.Type) {
    case QueryColumnType.Boolean: {
    operators.push(
    QueryOperatorType.Equal,
    QueryOperandInputType.TwoOptions
    );
    operators.push(
    QueryOperatorType.NotEqual,
    QueryOperandInputType.TwoOptions
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);
    operators.push(
    QueryOperatorType.BeginsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotBeginWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.Contains,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotContain,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.EndsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotEndWith,
    QueryOperandInputType.String
    );
    operators.push(QueryOperatorType.Like, QueryOperandInputType.String);
    operators.push(QueryOperatorType.NotLike, QueryOperandInputType.String);

    for (let i = 0; i < metaRecord.DisplayName.LocalizedLabels.length; i++) {
    if (metaRecord.DisplayName.LocalizedLabels[i].LanguageCode === 1033) {
    label = metaRecord.DisplayName.LocalizedLabels[i].Label;
    }
    break;
    }

    if (label === null) {
    if (metaRecord.DisplayName.LocalizedLabels.length > 0) {
    label = metaRecord.DisplayName.LocalizedLabels[0].Label;
    } else {
    label = metaRecord.LogicalName;
    }
    case QueryColumnType.Customer: {
    operators.push(QueryOperatorType.Equal, QueryOperandInputType.Customer);
    operators.push(
    QueryOperatorType.NotEqual,
    QueryOperandInputType.Customer
    );
    operators.push(
    QueryOperatorType.In,
    QueryOperandInputType.CustomerMultiple
    );
    operators.push(
    QueryOperatorType.NotIn,
    QueryOperandInputType.CustomerMultiple
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);
    operators.push(
    QueryOperatorType.BeginsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotBeginWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.Contains,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotContain,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.EndsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotEndWith,
    QueryOperandInputType.String
    );
    operators.push(QueryOperatorType.Like, QueryOperandInputType.String);
    operators.push(QueryOperatorType.NotLike, QueryOperandInputType.String);
    break;
    }
    return label;
    }
    }
    private readOptionLabel(metaRecord: any): string {
    try {
    const disp = metaRecord.Label as DisplayName;
    return disp.UserLocalizedLabel.Label;
    } catch {
    let label = null;
    case QueryColumnType.DateTime: {
    operators.push(QueryOperatorType.Equal, QueryOperandInputType.DateTime);
    operators.push(
    QueryOperatorType.NotEqual,
    QueryOperandInputType.DateTime
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);

    for (let i = 0; i < metaRecord.Label.LocalizedLabels.length; i++) {
    if (metaRecord.Label.LocalizedLabels[i].LanguageCode === 1033) {
    label = metaRecord.Label.LocalizedLabels[i].Label;
    }
    operators.push(
    QueryOperatorType.InFiscalPeriod,
    QueryOperandInputType.DateTimePe
    );
    operators.push(
    QueryOperatorType.InFiscalPeriodAndYear,
    QueryOperandInputType.DateTimeFyPe
    );
    operators.push(
    QueryOperatorType.InFiscalYear,
    QueryOperandInputType.DateTimeFy
    );
    operators.push(
    QueryOperatorType.InOrAfterFiscalPeriodAndYear,
    QueryOperandInputType.DateTimeFyPe
    );
    operators.push(
    QueryOperatorType.InOrBeforeFiscalPeriodAndYear,
    QueryOperandInputType.DateTimeFyPe
    );

    operators.push(QueryOperatorType.Last7Days, QueryOperandInputType.None);
    operators.push(
    QueryOperatorType.LastFiscalPeriod,
    QueryOperandInputType.None
    );
    operators.push(
    QueryOperatorType.LastFiscalYear,
    QueryOperandInputType.None
    );
    operators.push(QueryOperatorType.LastMonth, QueryOperandInputType.None);
    operators.push(QueryOperatorType.LastWeek, QueryOperandInputType.None);

    operators.push(
    QueryOperatorType.LastXDays,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.LastXFiscalPeriods,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.LastXFiscalYears,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.LastXHours,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.LastXMonths,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.LastXWeeks,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.LastXYears,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.LastYear,
    QueryOperandInputType.Integer
    );

    operators.push(QueryOperatorType.Next7Days, QueryOperandInputType.None);
    operators.push(
    QueryOperatorType.NextFiscalPeriod,
    QueryOperandInputType.None
    );
    operators.push(
    QueryOperatorType.NextFiscalYear,
    QueryOperandInputType.None
    );
    operators.push(QueryOperatorType.NextMonth, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NextWeek, QueryOperandInputType.None);
    operators.push(
    QueryOperatorType.NextXDays,
    QueryOperandInputType.Integer
    );

    operators.push(
    QueryOperatorType.NextXFiscalPeriods,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.NextXFiscalYears,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.NextXHours,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.NextXMonths,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.NextXWeeks,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.NextXYears,
    QueryOperandInputType.Integer
    );
    operators.push(QueryOperatorType.NextYear, QueryOperandInputType.None);

    operators.push(
    QueryOperatorType.OlderThanXYears,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.OlderThanXMonths,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.OlderThanXWeeks,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.OlderThanXDays,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.OlderThanXHours,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.OlderThanXMinutes,
    QueryOperandInputType.Integer
    );
    operators.push(QueryOperatorType.On, QueryOperandInputType.DateTime);
    operators.push(
    QueryOperatorType.OnOrAfter,
    QueryOperandInputType.DateTime
    );
    operators.push(
    QueryOperatorType.OnOrBefore,
    QueryOperandInputType.DateTime
    );

    operators.push(
    QueryOperatorType.ThisFiscalPeriod,
    QueryOperandInputType.None
    );
    operators.push(
    QueryOperatorType.ThisFiscalYear,
    QueryOperandInputType.None
    );
    operators.push(QueryOperatorType.ThisMonth, QueryOperandInputType.None);
    operators.push(QueryOperatorType.ThisWeek, QueryOperandInputType.None);
    operators.push(QueryOperatorType.ThisYear, QueryOperandInputType.None);

    operators.push(QueryOperatorType.Today, QueryOperandInputType.None);
    operators.push(QueryOperatorType.Tomorrow, QueryOperandInputType.None);
    operators.push(QueryOperatorType.Yesterday, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotOn, QueryOperandInputType.DateTime);

    operators.push(
    QueryOperatorType.BeginsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotBeginWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.Contains,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotContain,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.EndsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotEndWith,
    QueryOperandInputType.String
    );
    operators.push(QueryOperatorType.Like, QueryOperandInputType.String);
    operators.push(QueryOperatorType.NotLike, QueryOperandInputType.String);

    operators.push(
    QueryOperatorType.GreaterThan,
    QueryOperandInputType.DateTime
    );
    operators.push(
    QueryOperatorType.GreaterEqual,
    QueryOperandInputType.DateTime
    );
    operators.push(
    QueryOperatorType.LessThan,
    QueryOperandInputType.DateTime
    );
    operators.push(
    QueryOperatorType.LessEqual,
    QueryOperandInputType.DateTime
    );
    break;
    }
    case QueryColumnType.Decimal: {
    operators.push(QueryOperatorType.Equal, QueryOperandInputType.Decimal);
    operators.push(
    QueryOperatorType.NotEqual,
    QueryOperandInputType.Decimal
    );
    operators.push(
    QueryOperatorType.In,
    QueryOperandInputType.DecimalMultiple
    );
    operators.push(
    QueryOperatorType.NotIn,
    QueryOperandInputType.DecimalMultiple
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);

    if (label === null) {
    if (metaRecord.Label.LocalizedLabels.length > 0) {
    label = metaRecord.Label.LocalizedLabels[0].Label;
    } else {
    label = "Option #";
    }
    operators.push(
    QueryOperatorType.BeginsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotBeginWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.Contains,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotContain,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.EndsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotEndWith,
    QueryOperandInputType.String
    );
    operators.push(QueryOperatorType.Like, QueryOperandInputType.String);
    operators.push(QueryOperatorType.NotLike, QueryOperandInputType.String);

    operators.push(
    QueryOperatorType.Between,
    QueryOperandInputType.DecimalRange
    );
    operators.push(
    QueryOperatorType.NotBetween,
    QueryOperandInputType.DecimalRange
    );
    operators.push(
    QueryOperatorType.GreaterThan,
    QueryOperandInputType.Decimal
    );
    operators.push(
    QueryOperatorType.GreaterEqual,
    QueryOperandInputType.Decimal
    );
    operators.push(
    QueryOperatorType.LessThan,
    QueryOperandInputType.Decimal
    );
    operators.push(
    QueryOperatorType.LessEqual,
    QueryOperandInputType.Decimal
    );
    break;
    }
    return label;
    }
    }
    private readAttributeType(type: string): QueryColumnType {
    switch (type) {
    case "Lookup": {
    return QueryColumnType.Lookup;
    case QueryColumnType.Double: {
    operators.push(QueryOperatorType.Equal, QueryOperandInputType.Double);
    operators.push(
    QueryOperatorType.NotEqual,
    QueryOperandInputType.Double
    );
    operators.push(
    QueryOperatorType.In,
    QueryOperandInputType.DoubleMultiple
    );
    operators.push(
    QueryOperatorType.NotIn,
    QueryOperandInputType.DoubleMultiple
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);

    operators.push(
    QueryOperatorType.BeginsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotBeginWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.Contains,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotContain,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.EndsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotEndWith,
    QueryOperandInputType.String
    );
    operators.push(QueryOperatorType.Like, QueryOperandInputType.String);
    operators.push(QueryOperatorType.NotLike, QueryOperandInputType.String);

    operators.push(
    QueryOperatorType.Between,
    QueryOperandInputType.DoubleRange
    );
    operators.push(
    QueryOperatorType.NotBetween,
    QueryOperandInputType.DoubleRange
    );
    operators.push(
    QueryOperatorType.GreaterThan,
    QueryOperandInputType.Double
    );
    operators.push(
    QueryOperatorType.GreaterEqual,
    QueryOperandInputType.Double
    );
    operators.push(
    QueryOperatorType.LessThan,
    QueryOperandInputType.Double
    );
    operators.push(
    QueryOperatorType.LessEqual,
    QueryOperandInputType.Double
    );
    break;
    }
    case "State": {
    return QueryColumnType.State;
    case QueryColumnType.Integer: {
    operators.push(QueryOperatorType.Equal, QueryOperandInputType.Integer);
    operators.push(
    QueryOperatorType.NotEqual,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.In,
    QueryOperandInputType.IntegerMultiple
    );
    operators.push(
    QueryOperatorType.NotIn,
    QueryOperandInputType.IntegerMultiple
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);

    operators.push(
    QueryOperatorType.BeginsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotBeginWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.Contains,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotContain,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.EndsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotEndWith,
    QueryOperandInputType.String
    );
    operators.push(QueryOperatorType.Like, QueryOperandInputType.String);
    operators.push(QueryOperatorType.NotLike, QueryOperandInputType.String);

    operators.push(
    QueryOperatorType.Between,
    QueryOperandInputType.IntegerRange
    );
    operators.push(
    QueryOperatorType.NotBetween,
    QueryOperandInputType.IntegerRange
    );
    operators.push(
    QueryOperatorType.GreaterThan,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.GreaterEqual,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.LessThan,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.LessEqual,
    QueryOperandInputType.Integer
    );
    break;
    }
    case "Memo": {
    return QueryColumnType.Memo;
    case QueryColumnType.Lookup: {
    operators.push(QueryOperatorType.Equal, QueryOperandInputType.Lookup);
    operators.push(
    QueryOperatorType.NotEqual,
    QueryOperandInputType.Lookup
    );
    operators.push(
    QueryOperatorType.In,
    QueryOperandInputType.LookupMultiple
    );
    operators.push(
    QueryOperatorType.NotIn,
    QueryOperandInputType.LookupMultiple
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);

    operators.push(
    QueryOperatorType.BeginsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotBeginWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.Contains,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotContain,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.EndsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotEndWith,
    QueryOperandInputType.String
    );
    operators.push(QueryOperatorType.Like, QueryOperandInputType.String);
    operators.push(QueryOperatorType.NotLike, QueryOperandInputType.String);
    break;
    }
    case "Boolean": {
    return QueryColumnType.Boolean;
    case QueryColumnType.Memo: {
    operators.push(
    QueryOperatorType.Equal,
    QueryOperandInputType.Paragraph
    );
    operators.push(
    QueryOperatorType.NotEqual,
    QueryOperandInputType.Paragraph
    );
    operators.push(
    QueryOperatorType.In,
    QueryOperandInputType.StringMultiple
    );
    operators.push(
    QueryOperatorType.NotIn,
    QueryOperandInputType.StringMultiple
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);

    operators.push(
    QueryOperatorType.BeginsWith,
    QueryOperandInputType.Paragraph
    );
    operators.push(
    QueryOperatorType.DoesNotBeginWith,
    QueryOperandInputType.Paragraph
    );
    operators.push(
    QueryOperatorType.Contains,
    QueryOperandInputType.Paragraph
    );
    operators.push(
    QueryOperatorType.DoesNotContain,
    QueryOperandInputType.Paragraph
    );
    operators.push(
    QueryOperatorType.EndsWith,
    QueryOperandInputType.Paragraph
    );
    operators.push(
    QueryOperatorType.DoesNotEndWith,
    QueryOperandInputType.Paragraph
    );
    operators.push(QueryOperatorType.Like, QueryOperandInputType.Paragraph);
    operators.push(
    QueryOperatorType.NotLike,
    QueryOperandInputType.Paragraph
    );
    break;
    }
    case "String": {
    return QueryColumnType.String;
    case QueryColumnType.Money: {
    operators.push(QueryOperatorType.Equal, QueryOperandInputType.Decimal);
    operators.push(QueryOperatorType.NotEqual, QueryOperandInputType.Decimal);
    operators.push(
    QueryOperatorType.In,
    QueryOperandInputType.DecimalMultiple
    );
    operators.push(
    QueryOperatorType.NotIn,
    QueryOperandInputType.DecimalMultiple
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);

    operators.push(
    QueryOperatorType.BeginsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotBeginWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.Contains,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotContain,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.EndsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotEndWith,
    QueryOperandInputType.String
    );
    operators.push(QueryOperatorType.Like, QueryOperandInputType.String);
    operators.push(QueryOperatorType.NotLike, QueryOperandInputType.String);

    operators.push(
    QueryOperatorType.Between,
    QueryOperandInputType.DecimalRange
    );
    operators.push(
    QueryOperatorType.NotBetween,
    QueryOperandInputType.DecimalRange
    );
    operators.push(
    QueryOperatorType.GreaterThan,
    QueryOperandInputType.Decimal
    );
    operators.push(
    QueryOperatorType.GreaterEqual,
    QueryOperandInputType.Decimal
    );
    operators.push(
    QueryOperatorType.LessThan,
    QueryOperandInputType.Decimal
    );
    operators.push(
    QueryOperatorType.LessEqual,
    QueryOperandInputType.Decimal
    );
    break;
    }
    case "Integer": {
    return QueryColumnType.Integer;
    case QueryColumnType.Owner: {
    operators.push(QueryOperatorType.Equal, QueryOperandInputType.Owner);
    operators.push(QueryOperatorType.NotEqual, QueryOperandInputType.Owner);
    operators.push(
    QueryOperatorType.In,
    QueryOperandInputType.OwnerMultiple
    );
    operators.push(
    QueryOperatorType.NotIn,
    QueryOperandInputType.OwnerMultiple
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);

    operators.push(
    QueryOperatorType.EqualUserId,
    QueryOperandInputType.User
    );
    operators.push(
    QueryOperatorType.NotEqualUserId,
    QueryOperandInputType.User
    );
    operators.push(
    QueryOperatorType.EqualUserOrUserHierarchy,
    QueryOperandInputType.User
    );
    operators.push(
    QueryOperatorType.EqualUserOrUserHierarchyAndTeams,
    QueryOperandInputType.UserTeamMultiple
    );
    operators.push(
    QueryOperatorType.EqualUserOrUserTeams,
    QueryOperandInputType.UserTeamMultiple
    );
    operators.push(
    QueryOperatorType.BeginsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotBeginWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.Contains,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotContain,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.EndsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotEndWith,
    QueryOperandInputType.String
    );
    operators.push(QueryOperatorType.Like, QueryOperandInputType.String);
    operators.push(QueryOperatorType.NotLike, QueryOperandInputType.String);
    break;
    }
    case "DateTime": {
    return QueryColumnType.DateTime;
    case QueryColumnType.PartyList: {
    operators.push(
    QueryOperatorType.Equal,
    QueryOperandInputType.PartyList
    );
    operators.push(
    QueryOperatorType.NotEqual,
    QueryOperandInputType.PartyList
    );
    operators.push(
    QueryOperatorType.In,
    QueryOperandInputType.PartyListMultiple
    );
    operators.push(
    QueryOperatorType.NotIn,
    QueryOperandInputType.PartyListMultiple
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);
    break;
    }
    case "Status": {
    return QueryColumnType.Status;
    case QueryColumnType.Picklist: {
    operators.push(QueryOperatorType.Equal, QueryOperandInputType.Options);
    operators.push(
    QueryOperatorType.NotEqual,
    QueryOperandInputType.Options
    );
    operators.push(
    QueryOperatorType.In,
    QueryOperandInputType.OptionsMultiple
    );
    operators.push(
    QueryOperatorType.NotIn,
    QueryOperandInputType.OptionsMultiple
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);

    operators.push(
    QueryOperatorType.BeginsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotBeginWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.Contains,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotContain,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.EndsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotEndWith,
    QueryOperandInputType.String
    );
    operators.push(QueryOperatorType.Like, QueryOperandInputType.String);
    operators.push(QueryOperatorType.NotLike, QueryOperandInputType.String);

    operators.push(
    QueryOperatorType.Between,
    QueryOperandInputType.IntegerRange
    );
    operators.push(
    QueryOperatorType.NotBetween,
    QueryOperandInputType.IntegerRange
    );
    operators.push(
    QueryOperatorType.GreaterThan,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.GreaterEqual,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.LessThan,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.LessEqual,
    QueryOperandInputType.Integer
    );
    break;
    }
    case "Uniqueidentifier": {
    return QueryColumnType.Uniqueidentifier;
    case QueryColumnType.State: {
    operators.push(QueryOperatorType.Equal, QueryOperandInputType.Options);
    operators.push(
    QueryOperatorType.NotEqual,
    QueryOperandInputType.Options
    );
    operators.push(
    QueryOperatorType.In,
    QueryOperandInputType.OptionsMultiple
    );
    operators.push(
    QueryOperatorType.NotIn,
    QueryOperandInputType.OptionsMultiple
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);

    operators.push(
    QueryOperatorType.BeginsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotBeginWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.Contains,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotContain,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.EndsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotEndWith,
    QueryOperandInputType.String
    );
    operators.push(QueryOperatorType.Like, QueryOperandInputType.String);
    operators.push(QueryOperatorType.NotLike, QueryOperandInputType.String);

    operators.push(
    QueryOperatorType.Between,
    QueryOperandInputType.IntegerRange
    );
    operators.push(
    QueryOperatorType.NotBetween,
    QueryOperandInputType.IntegerRange
    );
    operators.push(
    QueryOperatorType.GreaterThan,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.GreaterEqual,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.LessThan,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.LessEqual,
    QueryOperandInputType.Integer
    );
    break;
    }
    case QueryColumnType.Status: {
    operators.push(QueryOperatorType.Equal, QueryOperandInputType.Options);
    operators.push(
    QueryOperatorType.NotEqual,
    QueryOperandInputType.Options
    );
    operators.push(
    QueryOperatorType.In,
    QueryOperandInputType.OptionsMultiple
    );
    operators.push(
    QueryOperatorType.NotIn,
    QueryOperandInputType.OptionsMultiple
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);

    operators.push(
    QueryOperatorType.BeginsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotBeginWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.Contains,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotContain,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.EndsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotEndWith,
    QueryOperandInputType.String
    );
    operators.push(QueryOperatorType.Like, QueryOperandInputType.String);
    operators.push(QueryOperatorType.NotLike, QueryOperandInputType.String);

    operators.push(
    QueryOperatorType.Between,
    QueryOperandInputType.IntegerRange
    );
    operators.push(
    QueryOperatorType.NotBetween,
    QueryOperandInputType.IntegerRange
    );
    operators.push(
    QueryOperatorType.GreaterThan,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.GreaterEqual,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.LessThan,
    QueryOperandInputType.Integer
    );
    operators.push(
    QueryOperatorType.LessEqual,
    QueryOperandInputType.Integer
    );
    break;
    }
    case QueryColumnType.String: {
    operators.push(QueryOperatorType.Equal, QueryOperandInputType.String);
    operators.push(
    QueryOperatorType.NotEqual,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.In,
    QueryOperandInputType.StringMultiple
    );
    operators.push(
    QueryOperatorType.NotIn,
    QueryOperandInputType.StringMultiple
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);

    operators.push(
    QueryOperatorType.BeginsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotBeginWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.Contains,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotContain,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.EndsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotEndWith,
    QueryOperandInputType.String
    );
    operators.push(QueryOperatorType.Like, QueryOperandInputType.String);
    operators.push(QueryOperatorType.NotLike, QueryOperandInputType.String);
    break;
    }
    case QueryColumnType.Uniqueidentifier: {
    operators.push(QueryOperatorType.Equal, QueryOperandInputType.Lookup);
    operators.push(
    QueryOperatorType.NotEqual,
    QueryOperandInputType.Lookup
    );
    operators.push(
    QueryOperatorType.In,
    QueryOperandInputType.LookupMultiple
    );
    operators.push(
    QueryOperatorType.NotIn,
    QueryOperandInputType.LookupMultiple
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);

    operators.push(QueryOperatorType.Above, QueryOperandInputType.Lookup);
    operators.push(
    QueryOperatorType.AboveOrEqual,
    QueryOperandInputType.Lookup
    );
    operators.push(QueryOperatorType.Under, QueryOperandInputType.Lookup);
    operators.push(
    QueryOperatorType.UnderOrEqual,
    QueryOperandInputType.Lookup
    );
    operators.push(
    QueryOperatorType.NotUnder,
    QueryOperandInputType.Lookup
    );
    operators.push(
    QueryOperatorType.EqualBusinessId,
    QueryOperandInputType.Lookup
    );
    operators.push(
    QueryOperatorType.EqualUserId,
    QueryOperandInputType.Lookup
    );

    operators.push(
    QueryOperatorType.BeginsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotBeginWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.Contains,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotContain,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.EndsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotEndWith,
    QueryOperandInputType.String
    );
    operators.push(QueryOperatorType.Like, QueryOperandInputType.String);
    operators.push(QueryOperatorType.NotLike, QueryOperandInputType.String);
    break;
    }
    case QueryColumnType.CalendarRules: {
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);
    break;
    }
    case QueryColumnType.Virtual: {
    break;
    }
    case QueryColumnType.BigInt: {
    operators.push(
    QueryOperatorType.Equal,
    QueryOperandInputType.BigInteger
    );
    operators.push(
    QueryOperatorType.NotEqual,
    QueryOperandInputType.BigInteger
    );
    operators.push(
    QueryOperatorType.In,
    QueryOperandInputType.BigIntegerMultiple
    );
    operators.push(
    QueryOperatorType.NotIn,
    QueryOperandInputType.BigIntegerMultiple
    );
    operators.push(QueryOperatorType.Null, QueryOperandInputType.None);
    operators.push(QueryOperatorType.NotNull, QueryOperandInputType.None);

    operators.push(
    QueryOperatorType.BeginsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotBeginWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.Contains,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotContain,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.EndsWith,
    QueryOperandInputType.String
    );
    operators.push(
    QueryOperatorType.DoesNotEndWith,
    QueryOperandInputType.String
    );
    operators.push(QueryOperatorType.Like, QueryOperandInputType.String);
    operators.push(QueryOperatorType.NotLike, QueryOperandInputType.String);

    operators.push(
    QueryOperatorType.Between,
    QueryOperandInputType.BigIntegerRange
    );
    operators.push(
    QueryOperatorType.NotBetween,
    QueryOperandInputType.BigIntegerRange
    );
    operators.push(
    QueryOperatorType.GreaterThan,
    QueryOperandInputType.BigInteger
    );
    operators.push(
    QueryOperatorType.GreaterEqual,
    QueryOperandInputType.BigInteger
    );
    operators.push(
    QueryOperatorType.LessThan,
    QueryOperandInputType.BigInteger
    );
    operators.push(
    QueryOperatorType.LessEqual,
    QueryOperandInputType.BigInteger
    );
    break;
    }
    case QueryColumnType.ManagedProperty: {
    break;
    }
    case QueryColumnType.EntityName: {
    break;
    }
    }
    return operators;
    }

    public GetInputParentType(inputType: QueryOperandInputType): QueryOperandType {
    switch (inputType) {
    case QueryOperandInputType.Integer:
    case QueryOperandInputType.IntegerMultiple:
    case QueryOperandInputType.IntegerRange:
    case QueryOperandInputType.BigInteger:
    case QueryOperandInputType.BigIntegerMultiple:
    case QueryOperandInputType.BigIntegerRange:
    case QueryOperandInputType.Decimal:
    case QueryOperandInputType.DecimalMultiple:
    case QueryOperandInputType.DecimalRange:
    case QueryOperandInputType.Double:
    case QueryOperandInputType.DoubleMultiple:
    case QueryOperandInputType.DoubleRange:
    return QueryOperandType.Number;

    case QueryOperandInputType.String:
    case QueryOperandInputType.StringMultiple:
    case QueryOperandInputType.Paragraph:
    return QueryOperandType.String;

    case QueryOperandInputType.DateTime:
    case QueryOperandInputType.DateTimeFy:
    case QueryOperandInputType.DateTimePe:
    case QueryOperandInputType.DateTimeFyPe:
    return QueryOperandType.DateTime;
    case QueryOperandInputType.Lookup:
    case QueryOperandInputType.LookupMultiple:
    case QueryOperandInputType.Customer:
    case QueryOperandInputType.CustomerMultiple:
    case QueryOperandInputType.Owner:
    case QueryOperandInputType.OwnerMultiple:
    case QueryOperandInputType.PartyList:
    case QueryOperandInputType.PartyListMultiple:
    case QueryOperandInputType.User:
    case QueryOperandInputType.UserTeam:
    case QueryOperandInputType.UserTeamMultiple:
    return QueryOperandType.Lookup;

    case QueryOperandInputType.TwoOptions: {
    return QueryOperandType.TwoOptions;
    }
    case QueryOperandInputType.Options:
    case QueryOperandInputType.OptionsMultiple:
    return QueryOperandType.Options;
    default: {
    return QueryOperandType.None;
    }
    }
    }
    //#region helpers

    private readLabel(metaRecord: any): string {
    try {
    const disp = metaRecord.DisplayName as DisplayName;
    return disp.UserLocalizedLabel.Label;
    } catch {
    let label = null;

    for (let i = 0; i < metaRecord.DisplayName.LocalizedLabels.length; i++) {
    if (metaRecord.DisplayName.LocalizedLabels[i].LanguageCode === 1033) {
    label = metaRecord.DisplayName.LocalizedLabels[i].Label;
    }
    }

    if (label === null) {
    if (metaRecord.DisplayName.LocalizedLabels.length > 0) {
    label = metaRecord.DisplayName.LocalizedLabels[0].Label;
    } else {
    label = metaRecord.LogicalName;
    }
    }
    return label;
    }
    }

    private readOption(att: XrmMetaAttribute): IQueryOption[] {
    try {
    return att.OptionSet.Options.map(op => {
    return {
    Key: this.readOptionLabel(op),
    Value: op.Value
    } as IQueryOption;
    });
    } catch {
    return [];
    }
    }
    private readOptionLabel(metaRecord: any): string {
    try {
    const disp = metaRecord.DisplayName as DisplayName;
    return disp.UserLocalizedLabel.Label;
    } catch {
    let label = null;

    for (let i = 0; i < metaRecord.Label.LocalizedLabels.length; i++) {
    if (metaRecord.Label.LocalizedLabels[i].LanguageCode === 1033) {
    label = metaRecord.Label.LocalizedLabels[i].Label;
    }
    }

    if (label === null) {
    if (metaRecord.Label.LocalizedLabels.length > 0) {
    label = metaRecord.Label.LocalizedLabels[0].Label;
    } else {
    label = "Option #";
    }
    }
    return label;
    }
    }
    private readAttributeType(type: string): QueryColumnType {
    switch (type) {
    case "Lookup": {
    return QueryColumnType.Lookup;
    }
    case "State": {
    return QueryColumnType.State;
    }
    case "Memo": {
    return QueryColumnType.Memo;
    }
    case "Boolean": {
    return QueryColumnType.Boolean;
    }
    case "String": {
    return QueryColumnType.String;
    }
    case "Integer": {
    return QueryColumnType.Integer;
    }
    case "DateTime": {
    return QueryColumnType.DateTime;
    }
    case "Status": {
    return QueryColumnType.Status;
    }
    case "Uniqueidentifier": {
    return QueryColumnType.Uniqueidentifier;
    }
    case "Picklist": {
    return QueryColumnType.Picklist;
    @@ -369,371 +1585,31 @@ export class XrmMetaSdk {
    return this.clientUrl || window.location.hostname;
    }

    private getError(resp) {
    if (resp.status === 12029) {
    return new Error("The attempt to connect to the server failed.");
    }
    if (resp.status === 12007) {
    return new Error("The server name could not be resolved.");
    }
    const faultXml = resp.responseXML;
    let errorMessage = "Unknown (unable to parse the fault)";
    let faultstring = null;
    let ErrorCode = null;
    if (typeof faultXml === "object") {
    const bodyNode = faultXml.firstChild.firstChild;
    //#endregion

    // Retrieve the fault node
    for (let i = 0; i < bodyNode.childNodes.length; i++) {
    const node = bodyNode.childNodes[i];
    // NOTE: This comparison does not handle the case where the XML namespace changes
    if ("s:Fault" === node.nodeName) {
    for (let j = 0; j < node.childNodes.length; j++) {
    const testNode = node.childNodes[j];
    if ("faultstring" === testNode.nodeName) {
    faultstring = this.getNodeText(testNode);
    }
    if ("detail" === testNode.nodeName) {
    for (let k = 0; k < testNode.childNodes.length; k++) {
    const orgServiceFault = testNode.childNodes[k];
    if ("OrganizationServiceFault" === orgServiceFault.nodeName) {
    for (let l = 0; l < orgServiceFault.childNodes.length; l++) {
    const ErrorCodeNode = orgServiceFault.childNodes[l];
    if ("ErrorCode" === ErrorCodeNode.nodeName) {
    ErrorCode = this.getNodeText(ErrorCodeNode);
    break;
    }
    }
    }
    }
    }
    }
    break;
    }
    }
    //#region internals
    private RetrieveAllEntities(
    successCallBack,
    errorCallBack
    ) {
    const _this = this;

    if (typeof successCallBack !== "function") {
    throw new Error(
    "SDK.Metadata.RetrieveAllEntities successCallBack must be a function."
    );
    }
    if (ErrorCode !== null && faultstring !== null) {
    errorMessage = "Error Code:" + ErrorCode + " Message: " + faultstring;
    } else {
    if (faultstring !== null) {
    errorMessage = faultstring;
    }
    if (typeof errorCallBack !== "function") {
    throw new Error(
    "SDK.Metadata.RetrieveAllEntities errorCallBack must be a function."
    );
    }
    return new Error(errorMessage);
    }

    private getRequest(entityFiltersValue: string): string {
    return null;
    }

    private xmlEncode(strInput) {
    let c: number;
    let XmlEncode = "";
    if (strInput === null) {
    return null;
    }
    if (strInput === "") {
    return "";
    }
    for (let cnt = 0; cnt < strInput.length; cnt++) {
    c = strInput.charCodeAt(cnt);
    if (
    (c > 96 && c < 123) ||
    (c > 64 && c < 91) ||
    c === 32 ||
    (c > 47 && c < 58) ||
    c === 46 ||
    c === 44 ||
    c === 45 ||
    c === 95
    ) {
    XmlEncode = XmlEncode + String.fromCharCode(c);
    } else {
    XmlEncode = XmlEncode + "&#" + c + ";";
    }
    }
    return XmlEncode;
    }

    private objectifyNode(node) {
    // Check for null
    if (node.attributes != null && node.attributes.length === 1) {
    if (
    node.attributes.getNamedItem("i:nil") != null &&
    node.attributes.getNamedItem("i:nil").nodeValue === "true"
    ) {
    return null;
    }
    }

    // Check if it is a value
    if (node.firstChild != null && node.firstChild.nodeType === 3) {
    const nodeName = this.getNodeName(node);

    switch (nodeName) {
    // Integer Values
    case "ActivityTypeMask":
    case "ObjectTypeCode":
    case "ColumnNumber":
    case "DefaultFormValue":
    case "MaxValue":
    case "MinValue":
    case "MaxLength":
    case "Order":
    case "Precision":
    case "PrecisionSource":
    case "LanguageCode":
    return parseInt(node.firstChild.nodeValue, 10);
    // Boolean values
    case "AutoRouteToOwnerQueue":
    case "CanBeChanged":
    case "CanTriggerWorkflow":
    case "IsActivity":
    case "IsAIRUpdated":
    case "IsActivityParty":
    case "IsAvailableOffline":
    case "IsChildEntity":
    case "IsCustomEntity":
    case "IsCustomOptionSet":
    case "IsDocumentManagementEnabled":
    case "IsEnabledForCharts":
    case "IsGlobal":
    case "IsImportable":
    case "IsIntersect":
    case "IsManaged":
    case "IsReadingPaneEnabled":
    case "IsValidForAdvancedFind":
    case "CanBeSecuredForCreate":
    case "CanBeSecuredForRead":
    case "CanBeSecuredForUpdate":
    case "IsCustomAttribute":
    case "IsManaged":
    case "IsPrimaryId":
    case "IsPrimaryName":
    case "IsSecured":
    case "IsValidForCreate":
    case "IsValidForRead":
    case "IsValidForUpdate":
    case "IsCustomRelationship":
    case "CanBeBasic":
    case "CanBeDeep":
    case "CanBeGlobal":
    case "CanBeLocal":
    return node.firstChild.nodeValue === "true" ? true : false;
    // OptionMetadata.Value and BooleanManagedProperty.Value and AttributeRequiredLevelManagedProperty.Value
    case "Value":
    // BooleanManagedProperty.Value
    if (
    node.firstChild.nodeValue === "true" ||
    node.firstChild.nodeValue === "false"
    ) {
    return node.firstChild.nodeValue === "true" ? true : false;
    }
    // AttributeRequiredLevelManagedProperty.Value
    if (
    node.firstChild.nodeValue === "ApplicationRequired" ||
    node.firstChild.nodeValue === "None" ||
    node.firstChild.nodeValue === "Recommended" ||
    node.firstChild.nodeValue === "SystemRequired"
    ) {
    return node.firstChild.nodeValue;
    }
    const numberValue = parseInt(node.firstChild.nodeValue, 10);
    if (isNaN(numberValue)) {
    // FormatName.Value
    return node.firstChild.nodeValue;
    } else {
    // OptionMetadata.Value
    return numberValue;
    }
    break;
    // String values
    default:
    return node.firstChild.nodeValue;
    }
    }

    // Check if it is a known array
    if (this.isMetadataArray(this.getNodeName(node))) {
    const arrayValue = [];

    for (let i = 0; i < node.childNodes.length; i++) {
    let objectTypeName;
    if (
    node.childNodes[i].attributes != null &&
    node.childNodes[i].attributes.getNamedItem("i:type") != null
    ) {
    objectTypeName = node.childNodes[i].attributes
    .getNamedItem("i:type")
    .nodeValue.split(":")[1];
    } else {
    objectTypeName = this.getNodeName(node.childNodes[i]);
    }

    const b = this.objectifyNode(node.childNodes[i]);
    b._type = objectTypeName;
    arrayValue.push(b);
    }

    return arrayValue;
    }

    // Null entity description labels are returned as <label/> - not using i:nil = true;
    if (node.childNodes.length === 0) {
    return null;
    }

    // Otherwise return an object
    const c: any = {};
    if (node.attributes.getNamedItem("i:type") != null) {
    c._type = node.attributes.getNamedItem("i:type").nodeValue.split(":")[1];
    }
    for (let i = 0; i < node.childNodes.length; i++) {
    if (node.childNodes[i].nodeType === 3) {
    c[this.getNodeName(node.childNodes[i])] = node.childNodes[i].nodeValue;
    } else {
    c[this.getNodeName(node.childNodes[i])] = this.objectifyNode(
    node.childNodes[i]
    );
    }
    }
    return c;
    }

    private isMetadataArray(elementName) {
    for (let i = 0; i < this._arrayElements.length; i++) {
    if (elementName === this._arrayElements[i]) {
    return true;
    }
    }
    return false;
    }

    private selectNodes(node, XPathExpression): any[] {
    if (typeof node.selectNodes != null) {
    return node.selectNodes(XPathExpression);
    } else {
    const output = [];
    const XPathResults = node.evaluate(
    XPathExpression,
    node,
    this.NSResolver,
    XPathResult.ANY_TYPE,
    null
    );
    let result = XPathResults.iterateNext();
    while (result) {
    output.push(result);
    result = XPathResults.iterateNext();
    }
    return output;
    }
    return [];
    }

    private selectSingleNodeText(node, xpathExpr) {
    const x = this.selectSingleNode(node, xpathExpr);
    if (this.isNodeNull(x)) {
    return null;
    }
    if (typeof x.text != null) {
    return x.text;
    } else {
    return x.textContent;
    }
    }

    private selectSingleNode(node, xpathExpr) {
    if (typeof node.selectSingleNode != null) {
    return node.selectSingleNode(xpathExpr);
    } else {
    const xpe = new XPathEvaluator();
    const xPathNode = xpe.evaluate(
    xpathExpr,
    node,
    this.NSResolver,
    XPathResult.FIRST_ORDERED_NODE_TYPE,
    null
    );
    return xPathNode != null ? xPathNode.singleNodeValue : null;
    }
    }

    private getNodeText(node) {
    if (typeof node.text !== "undefined") {
    return node.text;
    } else {
    return node.textContent;
    }
    }

    private isNodeNull(node) {
    if (node == null) {
    return true;
    }
    if (
    node.attributes.getNamedItem("i:nil") != null &&
    node.attributes.getNamedItem("i:nil").value === "true"
    ) {
    return true;
    }
    return false;
    }

    private getNodeName(node: any) {
    if (typeof node.baseName != null) {
    return node.baseName;
    } else {
    return node.localName;
    }
    }

    private setSelectionNamespaces(doc) {
    const nameSpace = [
    'xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"',
    'xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts"',
    'xmlns:i="http://www.w3.org/2001/XMLSchema-instance"',
    'xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic"',
    'xmlns:c="http://schemas.microsoft.com/xrm/2011/Metadata"'
    ];
    doc.setProperty("SelectionNamespaces", nameSpace.join(" "));
    }

    private NSResolver(prefix) {
    const nameSpace = {
    s: "http://schemas.xmlsoap.org/soap/envelope/",
    a: "http://schemas.microsoft.com/xrm/2011/Contracts",
    i: "http://www.w3.org/2001/XMLSchema-instance",
    b: "http://schemas.datacontract.org/2004/07/System.Collections.Generic",
    c: "http://schemas.microsoft.com/xrm/2011/Metadata"
    };
    return nameSpace[prefix] || null;
    }

    private evaluateEntityFilters(xrmFilter: XrmEntityFilter): string {
    const entityFilterArray = [];
    if ((1 & xrmFilter) === 1) {
    entityFilterArray.push("Entity");
    }
    if ((2 & xrmFilter) === 2) {
    entityFilterArray.push("Attributes");
    }
    if ((4 & xrmFilter) === 4) {
    entityFilterArray.push("Privileges");
    }
    if ((8 & xrmFilter) === 8) {
    entityFilterArray.push("Relationships");
    }
    return entityFilterArray.join(" ");
    }
    //#endregion

    private RetrieveAllEntities(successCallBack, errorCallBack) {
    const _this = this;
    const entityFiltersValue = this.evaluateEntityFilters(1);
    const entityFiltersValue = this._evaluateEntityFilters(1);

    const request = [
    '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">',
    // Allows retrieval if ImageAttributeMetadata objects
    '<soapenv:Header><a:SdkClientVersion xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">7.0</a:SdkClientVersion></soapenv:Header>',
    "<soapenv:Body>",
    '<Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">',
    @@ -742,13 +1618,13 @@ export class XrmMetaSdk {
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>EntityFilters</b:key>",
    '<b:value i:type="c:EntityFilters" xmlns:c="http://schemas.microsoft.com/xrm/2011/Metadata">' +
    this.xmlEncode(entityFiltersValue) +
    _this._xmlEncode(entityFiltersValue) +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>RetrieveAsIfPublished</b:key>",
    '<b:value i:type="c:boolean" xmlns:c="http://www.w3.org/2001/XMLSchema">' +
    this.xmlEncode("true") +
    _this._xmlEncode(true.toString()) +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "</a:Parameters>",
    @@ -762,50 +1638,53 @@ export class XrmMetaSdk {
    const req = new XMLHttpRequest();
    req.open(
    "POST",
    this.getUrl() + "/XRMServices/2011/Organization.svc/web",
    _this.getUrl() + "/XRMServices/2011/Organization.svc/web",
    true
    );
    // try {
    // req.responseType = "msxml-document";
    // } catch (e) {}
    try {
    req.responseType = "msxml-document" as any;
    } catch (e) {}
    req.setRequestHeader("Accept", "application/xml, text/xml, */*");
    req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    req.setRequestHeader(
    "SOAPAction",
    "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute"
    );
    req.onreadystatechange = function() {
    const __this = _this;
    if (req.readyState === 4 /* complete */) {
    req.onreadystatechange = null; // Addresses potential memory leak issue with IE
    if (req.status === 200) {
    // Success
    const doc = req.responseXML;
    try {
    _this.setSelectionNamespaces(doc);
    __this._setSelectionNamespaces(doc);
    } catch (e) {}
    const entityMetadataNodes = _this.selectNodes(
    doc,
    "//c:EntityMetadata"
    );
    const entityMetadataNodes = __this._selectNodes(doc, "//c:EntityMetadata");
    const entityMetadataCollection: XrmMetaEntity[] = [];
    for (let i = 0; i < entityMetadataNodes.length; i++) {
    const a = _this.objectifyNode(entityMetadataNodes[i]);
    const a = __this._objectifyNode(entityMetadataNodes[i]);
    a._type = "EntityMetadata";
    entityMetadataCollection.push(a);
    }

    successCallBack(entityMetadataCollection);
    } else {
    errorCallBack(_this.getError(req));
    errorCallBack(__this._getError(req));
    }
    }
    };
    req.send(request);
    }

    private RetrieveAttributes(LogicalName, successCallBack, errorCallBack) {
    private RetrieveAttributes(
    LogicalName,
    successCallBack,
    errorCallBack
    ) {
    let MetadataId = null;
    const _this = this;
    if (LogicalName == null) {
    if (LogicalName == null && MetadataId == null) {
    throw new Error(
    "SDK.Metadata.RetrieveEntity requires either the LogicalName or MetadataId parameter not be null."
    );
    @@ -835,15 +1714,15 @@ export class XrmMetaSdk {
    "SDK.Metadata.RetrieveEntity errorCallBack must be a function."
    );
    }
    const entityFiltersValue = this.evaluateEntityFilters(2);
    const entityFiltersValue = _this._evaluateEntityFilters(2);

    let entityLogicalNameValueNode = "";
    if (LogicalName == null) {
    entityLogicalNameValueNode = '<b:value i:nil="true" />';
    } else {
    entityLogicalNameValueNode =
    '<b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">' +
    this.xmlEncode(LogicalName.toLowerCase()) +
    _this._xmlEncode(LogicalName.toLowerCase()) +
    "</b:value>";
    }
    const request = [
    @@ -857,19 +1736,19 @@ export class XrmMetaSdk {
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>EntityFilters</b:key>",
    '<b:value i:type="c:EntityFilters" xmlns:c="http://schemas.microsoft.com/xrm/2011/Metadata">' +
    this.xmlEncode(entityFiltersValue) +
    _this._xmlEncode(entityFiltersValue) +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>MetadataId</b:key>",
    '<b:value i:type="ser:guid" xmlns:ser="http://schemas.microsoft.com/2003/10/Serialization/">' +
    this.xmlEncode(MetadataId) +
    _this._xmlEncode(MetadataId) +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>RetrieveAsIfPublished</b:key>",
    '<b:value i:type="c:boolean" xmlns:c="http://www.w3.org/2001/XMLSchema">' +
    this.xmlEncode("true") +
    _this._xmlEncode(true.toString()) +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    @@ -887,34 +1766,33 @@ export class XrmMetaSdk {
    const req = new XMLHttpRequest();
    req.open(
    "POST",
    this.getUrl() + "/XRMServices/2011/Organization.svc/web",
    _this.getUrl() + "/XRMServices/2011/Organization.svc/web",
    true
    );
    // try {
    // req.responseType = "msxml-document";
    // } catch (e) {}
    try {
    req.responseType = "msxml-document" as any;
    } catch (e) {}
    req.setRequestHeader("Accept", "application/xml, text/xml, */*");
    req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    req.setRequestHeader(
    "SOAPAction",
    "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute"
    );
    req.onreadystatechange = function() {
    const __this = _this;
    if (req.readyState === 4 /* complete */) {
    req.onreadystatechange = null; // Addresses potential memory leak issue with IE
    if (req.status === 200) {
    const doc = req.responseXML;
    try {
    _this.setSelectionNamespaces(doc);
    __this._setSelectionNamespaces(doc);
    } catch (e) {}
    const a: XrmMetaEntity = _this.objectifyNode(
    _this.selectSingleNode(doc, "//b:value")
    );
    const a: XrmMetaEntity = __this._objectifyNode(__this._selectSingleNode(doc, "//b:value"));
    a._type = "EntityMetadata";
    successCallBack(a.Attributes);
    } else {
    // Failure
    errorCallBack(_this.getError(req));
    errorCallBack(__this._getError(req));
    }
    }
    };
    @@ -923,10 +1801,10 @@ export class XrmMetaSdk {

    private RetrieveRelations(
    LogicalName,
    MetadataId,
    successCallBack,
    errorCallBack
    ) {
    let MetadataId = null;
    const _this = this;
    if (LogicalName == null && MetadataId == null) {
    throw new Error(
    @@ -958,16 +1836,15 @@ export class XrmMetaSdk {
    "SDK.Metadata.RetrieveEntity errorCallBack must be a function."
    );
    }
    // Attributes and entities
    const entityFiltersValue = this.evaluateEntityFilters(8);
    const entityFiltersValue = _this._evaluateEntityFilters(8);

    let entityLogicalNameValueNode = "";
    if (LogicalName == null) {
    entityLogicalNameValueNode = '<b:value i:nil="true" />';
    } else {
    entityLogicalNameValueNode =
    '<b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">' +
    this.xmlEncode(LogicalName.toLowerCase()) +
    _this._xmlEncode(LogicalName.toLowerCase()) +
    "</b:value>";
    }
    const request = [
    @@ -981,19 +1858,19 @@ export class XrmMetaSdk {
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>EntityFilters</b:key>",
    '<b:value i:type="c:EntityFilters" xmlns:c="http://schemas.microsoft.com/xrm/2011/Metadata">' +
    this.xmlEncode(entityFiltersValue) +
    _this._xmlEncode(entityFiltersValue) +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>MetadataId</b:key>",
    '<b:value i:type="ser:guid" xmlns:ser="http://schemas.microsoft.com/2003/10/Serialization/">' +
    this.xmlEncode(MetadataId) +
    _this._xmlEncode(MetadataId) +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>RetrieveAsIfPublished</b:key>",
    '<b:value i:type="c:boolean" xmlns:c="http://www.w3.org/2001/XMLSchema">' +
    this.xmlEncode("true") +
    _this._xmlEncode(true.toString()) +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    @@ -1011,186 +1888,397 @@ export class XrmMetaSdk {
    const req = new XMLHttpRequest();
    req.open(
    "POST",
    this.getUrl() + "/XRMServices/2011/Organization.svc/web",
    _this.getUrl() + "/XRMServices/2011/Organization.svc/web",
    true
    );
    // try {
    // req.responseType = "msxml-document";
    // } catch (e) {}
    try {
    req.responseType = "msxml-document" as any;
    } catch (e) {}
    req.setRequestHeader("Accept", "application/xml, text/xml, */*");
    req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    req.setRequestHeader(
    "SOAPAction",
    "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute"
    );
    req.onreadystatechange = function() {
    const __this = _this;
    if (req.readyState === 4 /* complete */) {
    req.onreadystatechange = null; // Addresses potential memory leak issue with IE
    if (req.status === 200) {
    const doc = req.responseXML;
    try {
    _this.setSelectionNamespaces(doc);
    __this._setSelectionNamespaces(doc);
    } catch (e) {}
    const a: XrmMetaEntity = _this.objectifyNode(
    _this.selectSingleNode(doc, "//b:value")
    );
    const a: XrmMetaEntity = __this._objectifyNode(__this._selectSingleNode(doc, "//b:value"));
    a._type = "EntityMetadata";
    successCallBack(a);
    } else {
    // Failure
    errorCallBack(_this.getError(req));
    errorCallBack(__this._getError(req));
    }
    }
    };
    req.send(request);
    }
    private RetrieveAttribute(
    EntityLogicalName,
    LogicalName,
    MetadataId,
    successCallBack,
    errorCallBack
    ) {
    private _getError(resp) {
    const _this = this;
    if (
    EntityLogicalName == null &&
    LogicalName == null &&
    MetadataId == null
    ) {
    throw new Error(
    "SDK.Metadata.RetrieveAttribute requires either the EntityLogicalName and LogicalName parameters or the MetadataId parameter not be null."
    );
    if (resp.status === 12029) {
    return new Error("The attempt to connect to the server failed.");
    }
    if (
    MetadataId != null &&
    EntityLogicalName == null &&
    LogicalName == null
    ) {
    if (typeof MetadataId !== "string") {
    throw new Error(
    "SDK.Metadata.RetrieveEntity MetadataId must be a string value."
    );
    if (resp.status === 12007) {
    return new Error("The server name could not be resolved.");
    }
    const faultXml = resp.responseXML;
    let errorMessage = "Unknown (unable to parse the fault)";
    let faultstring = null;
    let ErrorCode = null;
    if (typeof faultXml === "object") {

    const bodyNode = faultXml.firstChild.firstChild;

    // Retrieve the fault node
    for (let i = 0; i < bodyNode.childNodes.length; i++) {
    const node = bodyNode.childNodes[i];

    // NOTE: This comparison does not handle the case where the XML namespace changes
    if ("s:Fault" === node.nodeName) {
    for (let j = 0; j < node.childNodes.length; j++) {
    const testNode = node.childNodes[j];
    if ("faultstring" === testNode.nodeName) {
    faultstring = _this._getNodeText(testNode);
    }
    if ("detail" === testNode.nodeName) {
    for (let k = 0; k < testNode.childNodes.length; k++) {
    const orgServiceFault = testNode.childNodes[k];
    if ("OrganizationServiceFault" === orgServiceFault.nodeName) {
    for (let l = 0; l < orgServiceFault.childNodes.length; l++) {
    const ErrorCodeNode = orgServiceFault.childNodes[l];
    if ("ErrorCode" === ErrorCodeNode.nodeName) {
    ErrorCode = _this._getNodeText(ErrorCodeNode);
    break;
    }
    }
    }
    }
    }
    }
    break;
    }
    }
    }
    if (ErrorCode != null && faultstring != null) {
    errorMessage = "Error Code:" + ErrorCode + " Message: " + faultstring;
    } else {
    MetadataId = "00000000-0000-0000-0000-000000000000";
    if (faultstring != null) {
    errorMessage = faultstring;
    }
    }
    if (EntityLogicalName != null) {
    if (typeof EntityLogicalName !== "string") {
    {
    throw new Error(
    "SDK.Metadata.RetrieveAttribute EntityLogicalName must be a string value."
    );
    }
    return new Error(errorMessage);
    }

    private _evaluateEntityFilters(EntityFilters) {
    const entityFilterArray = [];
    if ((1 & EntityFilters) === 1) {
    entityFilterArray.push("Entity");
    }
    if ((2 & EntityFilters) === 2) {
    entityFilterArray.push("Attributes");
    }
    if ((4 & EntityFilters) === 4) {
    entityFilterArray.push("Privileges");
    }
    if ((8 & EntityFilters) === 8) {
    entityFilterArray.push("Relationships");
    }
    return entityFilterArray.join(" ");
    }

    private _isMetadataArray(elementName) {
    const _this = this;
    for (let i = 0; i < _this._arrayElements.length; i++) {
    if (elementName === _this._arrayElements[i]) {
    return true;
    }
    }
    if (LogicalName != null) {
    if (typeof LogicalName !== "string") {
    {
    throw new Error(
    "SDK.Metadata.RetrieveAttribute LogicalName must be a string value."
    );
    return false;
    }

    private _objectifyNode(node) {
    const _this = this;
    // Check for null
    if (node.attributes != null && node.attributes.length === 1) {
    if (
    node.attributes.getNamedItem("i:nil") != null &&
    node.attributes.getNamedItem("i:nil").nodeValue === "true"
    ) {
    return null;
    }
    }

    // Check if it is a value
    if (node.firstChild != null && node.firstChild.nodeType === 3) {
    const nodeName = _this._getNodeName(node);

    switch (nodeName) {
    // Integer Values
    case "ActivityTypeMask":
    case "ObjectTypeCode":
    case "ColumnNumber":
    case "DefaultFormValue":
    case "MaxValue":
    case "MinValue":
    case "MaxLength":
    case "Order":
    case "Precision":
    case "PrecisionSource":
    case "LanguageCode":
    return parseInt(node.firstChild.nodeValue, 10);
    // Boolean values
    case "AutoRouteToOwnerQueue":
    case "CanBeChanged":
    case "CanTriggerWorkflow":
    case "IsActivity":
    case "IsAIRUpdated":
    case "IsActivityParty":
    case "IsAvailableOffline":
    case "IsChildEntity":
    case "IsCustomEntity":
    case "IsCustomOptionSet":
    case "IsDocumentManagementEnabled":
    case "IsEnabledForCharts":
    case "IsGlobal":
    case "IsImportable":
    case "IsIntersect":
    case "IsManaged":
    case "IsReadingPaneEnabled":
    case "IsValidForAdvancedFind":
    case "CanBeSecuredForCreate":
    case "CanBeSecuredForRead":
    case "CanBeSecuredForUpdate":
    case "IsCustomAttribute":
    case "IsManaged":
    case "IsPrimaryId":
    case "IsPrimaryName":
    case "IsSecured":
    case "IsValidForCreate":
    case "IsValidForRead":
    case "IsValidForUpdate":
    case "IsCustomRelationship":
    case "CanBeBasic":
    case "CanBeDeep":
    case "CanBeGlobal":
    case "CanBeLocal":
    return node.firstChild.nodeValue === "true" ? true : false;
    // OptionMetadata.Value and BooleanManagedProperty.Value and AttributeRequiredLevelManagedProperty.Value
    case "Value":
    // BooleanManagedProperty.Value
    if (
    node.firstChild.nodeValue === "true" ||
    node.firstChild.nodeValue === "false"
    ) {
    return node.firstChild.nodeValue === "true" ? true : false;
    }
    // AttributeRequiredLevelManagedProperty.Value
    if (
    node.firstChild.nodeValue === "ApplicationRequired" ||
    node.firstChild.nodeValue === "None" ||
    node.firstChild.nodeValue === "Recommended" ||
    node.firstChild.nodeValue === "SystemRequired"
    ) {
    return node.firstChild.nodeValue;
    }
    const numberValue = parseInt(node.firstChild.nodeValue, 10);
    if (isNaN(numberValue)) {
    // FormatName.Value
    return node.firstChild.nodeValue;
    } else {
    // OptionMetadata.Value
    return numberValue;
    }
    break;
    // String values
    default:
    return node.firstChild.nodeValue;
    }
    }

    // Check if it is a known array
    if (_this._isMetadataArray(_this._getNodeName(node))) {
    const arrayValue = [];

    for (let i = 0; i < node.childNodes.length; i++) {
    let objectTypeName;
    if (
    node.childNodes[i].attributes != null &&
    node.childNodes[i].attributes.getNamedItem("i:type") != null
    ) {
    objectTypeName = node.childNodes[i].attributes
    .getNamedItem("i:type")
    .nodeValue.split(":")[1];
    } else {
    objectTypeName = _this._getNodeName(node.childNodes[i]);
    }

    const b = _this._objectifyNode(node.childNodes[i]);
    b._type = objectTypeName;
    arrayValue.push(b);
    }

    return arrayValue;
    }
    if (typeof successCallBack !== "function") {
    throw new Error(
    "SDK.Metadata.RetrieveAttribute successCallBack must be a function."

    // Null entity description labels are returned as <label/> - not using i:nil = true;
    if (node.childNodes.length === 0) {
    return null;
    }

    // Otherwise return an object
    const c: any = {};
    if (node.attributes.getNamedItem("i:type") != null) {
    c._type = node.attributes.getNamedItem("i:type").nodeValue.split(":")[1];
    }
    for (let i = 0; i < node.childNodes.length; i++) {
    if (node.childNodes[i].nodeType === 3) {
    c[_this._getNodeName(node.childNodes[i])] = node.childNodes[i].nodeValue;
    } else {
    c[_this._getNodeName(node.childNodes[i])] = _this._objectifyNode(
    node.childNodes[i]
    );
    }
    }
    return c;
    }

    private _selectNodes(node, XPathExpression) {
    const _this = this;
    if (typeof node.selectNodes !== "undefined") {
    return node.selectNodes(XPathExpression);
    } else {
    const output = [];
    const XPathResults = node.evaluate(
    XPathExpression,
    node,
    _this._NSResolver,
    XPathResult.ANY_TYPE,
    null
    );
    let result = XPathResults.iterateNext();
    while (result) {
    output.push(result);
    result = XPathResults.iterateNext();
    }
    return output;
    }
    if (typeof errorCallBack !== "function") {
    throw new Error(
    "SDK.Metadata.RetrieveAttribute errorCallBack must be a function."
    }

    private _selectSingleNode(node, xpathExpr) {
    const _this = this;
    if (typeof node.selectSingleNode !== "undefined") {
    return node.selectSingleNode(xpathExpr);
    } else {
    const xpe = new XPathEvaluator();
    const xPathNode = xpe.evaluate(
    xpathExpr,
    node,
    _this._NSResolver,
    XPathResult.FIRST_ORDERED_NODE_TYPE,
    null
    );
    return xPathNode != null ? xPathNode.singleNodeValue : null;
    }
    }

    let entityLogicalNameValueNode;
    if (EntityLogicalName == null) {
    entityLogicalNameValueNode = '<b:value i:nil="true" />';
    private _selectSingleNodeText(node, xpathExpr) {
    const _this = this;
    const x = _this._selectSingleNode(node, xpathExpr);
    if (_this._isNodeNull(x)) {
    return null;
    }
    if (typeof x.text !== "undefined") {
    return x.text;
    } else {
    entityLogicalNameValueNode =
    '<b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">' +
    _this.xmlEncode(EntityLogicalName.toLowerCase()) +
    "</b:value>";
    return x.textContent;
    }
    let logicalNameValueNode;
    if (LogicalName == null) {
    logicalNameValueNode = '<b:value i:nil="true" />';
    }

    private _getNodeText(node) {
    if (typeof node.text !== "undefined") {
    return node.text;
    } else {
    logicalNameValueNode =
    '<b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">' +
    _this.xmlEncode(LogicalName.toLowerCase()) +
    "</b:value>";
    return node.textContent;
    }
    const request = [
    '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">',
    // Allows retrieval if ImageAttributeMetadata objects
    '<soapenv:Header><a:SdkClientVersion xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">6.0</a:SdkClientVersion></soapenv:Header>',
    "<soapenv:Body>",
    '<Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">',
    '<request i:type="a:RetrieveAttributeRequest" xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">',
    '<a:Parameters xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">',
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>EntityLogicalName</b:key>",
    entityLogicalNameValueNode,
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>MetadataId</b:key>",
    '<b:value i:type="ser:guid" xmlns:ser="http://schemas.microsoft.com/2003/10/Serialization/">' +
    _this.xmlEncode(MetadataId) +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>RetrieveAsIfPublished</b:key>",
    '<b:value i:type="c:boolean" xmlns:c="http://www.w3.org/2001/XMLSchema">' +
    _this.xmlEncode("true") +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>LogicalName</b:key>",
    logicalNameValueNode,
    "</a:KeyValuePairOfstringanyType>",
    "</a:Parameters>",
    '<a:RequestId i:nil="true" />',
    "<a:RequestName>RetrieveAttribute</a:RequestName>",
    "</request>",
    "</Execute>",
    "</soapenv:Body>",
    "</soapenv:Envelope>"
    ].join("");
    const req = new XMLHttpRequest();
    req.open(
    "POST",
    _this.getUrl() + "/XRMServices/2011/Organization.svc/web",
    true
    );
    // try {
    // req.responseType = "msxml-document";
    // } catch (e) {}
    req.setRequestHeader("Accept", "application/xml, text/xml, */*");
    req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    req.setRequestHeader(
    "SOAPAction",
    "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute"
    );
    req.onreadystatechange = function() {
    if (req.readyState === 4 /* complete */) {
    req.onreadystatechange = null; // Addresses potential memory leak issue with IE
    if (req.status === 200) {
    // Success
    const doc = req.responseXML;
    try {
    _this.setSelectionNamespaces(doc);
    } catch (e) {}
    const a = _this.objectifyNode(
    _this.selectSingleNode(doc, "//b:value")
    );
    successCallBack(a);
    } else {
    // Failure
    errorCallBack(_this.getError(req));
    }
    }
    }

    private _isNodeNull(node) {
    if (node == null) {
    return true;
    }
    if (
    node.attributes.getNamedItem("i:nil") != null &&
    node.attributes.getNamedItem("i:nil").value === "true"
    ) {
    return true;
    }
    return false;
    }

    private _getNodeName(node) {
    if (typeof node.baseName !== "undefined") {
    return node.baseName;
    } else {
    return node.localName;
    }
    }

    private _setSelectionNamespaces(doc) {
    const namespaces = [
    "xmlns:s='http://schemas.xmlsoap.org/soap/envelope/'",
    "xmlns:a='http://schemas.microsoft.com/xrm/2011/Contracts'",
    "xmlns:i='http://www.w3.org/2001/XMLSchema-instance'",
    "xmlns:b='http://schemas.datacontract.org/2004/07/System.Collections.Generic'",
    "xmlns:c='http://schemas.microsoft.com/xrm/2011/Metadata'"
    ];
    doc.setProperty("SelectionNamespaces", namespaces.join(" "));
    }

    private _NSResolver(prefix) {
    const ns = {
    s: "http://schemas.xmlsoap.org/soap/envelope/",
    a: "http://schemas.microsoft.com/xrm/2011/Contracts",
    i: "http://www.w3.org/2001/XMLSchema-instance",
    b: "http://schemas.datacontract.org/2004/07/System.Collections.Generic",
    c: "http://schemas.microsoft.com/xrm/2011/Metadata"
    };
    req.send(request);
    return ns[prefix] || null;
    }

    private _xmlEncode(strInput) {
    let c;
    let XmlEncode = "";
    if (strInput == null) {
    return null;
    }
    if (strInput === "") {
    return "";
    }
    for (let cnt = 0; cnt < strInput.length; cnt++) {
    c = strInput.charCodeAt(cnt);
    if (
    (c > 96 && c < 123) ||
    (c > 64 && c < 91) ||
    c === 32 ||
    (c > 47 && c < 58) ||
    c === 46 ||
    c === 44 ||
    c === 45 ||
    c === 95
    ) {
    XmlEncode = XmlEncode + String.fromCharCode(c);
    } else {
    XmlEncode = XmlEncode + "&#" + c + ";";
    }
    }
    return XmlEncode;
    }
    //#endregion
    }
  2. iwannabebot revised this gist Feb 8, 2019. No changes.
  3. iwannabebot created this gist Feb 8, 2019.
    217 changes: 217 additions & 0 deletions xrm-meta-models.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,217 @@
    export interface XrmMetaAttribute {
    LogicalName: string;
    MetadataId: string;
    DisplayName: DisplayName;
    Description: Description;
    IsCustomizable: IsCustomizable;
    IsAuditEnabled: IsAuditEnabled;
    OptionsSet: any;
    AttributeType: string;
    HasChanged?: any;
    AttributeOf?: any;
    ColumnNumber: number;
    DeprecatedVersion?: any;
    IntroducedVersion: string;
    EntityLogicalName: string;
    IsCustomAttribute: boolean;
    IsPrimaryId: boolean;
    IsPrimaryName: boolean;
    IsValidForCreate: boolean;
    IsValidForRead: boolean;
    IsValidForUpdate: boolean;
    CanBeSecuredForRead: boolean;
    CanBeSecuredForCreate: boolean;
    CanBeSecuredForUpdate: boolean;
    IsSecured: boolean;
    IsRetrievable: boolean;
    IsFilterable: boolean;
    IsSearchable: boolean;
    IsManaged: boolean;
    LinkedAttributeId?: any;
    IsValidForForm: boolean;
    IsRequiredForForm: boolean;
    IsValidForGrid: boolean;
    SchemaName: string;
    ExternalName?: any;
    IsLogical: boolean;
    IsDataSourceSecret: boolean;
    InheritsFrom?: any;
    SourceType: number;
    AutoNumberFormat: string;
    Format: string;
    ImeMode: string;
    MaxLength: number;
    YomiOf?: any;
    IsLocalizable: boolean;
    DatabaseLength: number;
    FormulaDefinition?: any;
    SourceTypeMask: number;
    AttributeTypeName: AttributeTypeName;
    IsValidForAdvancedFind: IsValidForAdvancedFind;
    RequiredLevel: RequiredLevel;
    FormatName: FormatName;
    Targets: string[];
    }

    export interface XrmMetaEntity {

    LogicalName: string;
    MetadataId: string;
    DisplayName: DisplayName;
    Description: Description;
    IsCustomizable: IsCustomizable;
    IsAuditEnabled: IsAuditEnabled;
    HasChanged: any;
    ActivityTypeMask: number;
    Attributes: XrmMetaAttribute[];
    DisplayCollectionName: DisplayCollectionName;
    IsActivity: boolean;
    IsActivityParty: boolean;
    IsBusinessProcessEnabled: string;
    IsChildEntity: boolean;
    IsCustomEntity: boolean;
    IsValidForAdvancedFind: boolean;
    IsValidForQueue: IsValidForQueue;
    ManyToManyRelationships: XrmMetaManyToManyRelation[];
    ManyToOneRelationships: XrmMetaOneToManyRelation[];
    ObjectTypeCode: number;
    OneToManyRelationships: XrmMetaOneToManyRelation[];
    OwnershipType: string;
    PrimaryIdAttribute: string;
    PrimaryNameAttribute: string;
    SchemaName: string;
    CollectionSchemaName: string;
    Keys: any;
    EntitySetName: string;
    _type: string;
    }

    export enum XrmEntityFilter {
    Default = 1,
    Entity = 1,
    Attributes = 2,
    Privileges = 4,
    Relationships = 8,
    All = 15
    }

    export interface AttributeTypeName {
    Value: string;
    }

    export interface FormatName {
    Value: string;
    }

    export interface RequiredLevel {
    Value: string;
    CanBeChanged: boolean;
    ManagedPropertyLogicalName: string;
    }

    export interface IsValidForAdvancedFind {
    Value: boolean;
    CanBeChanged: boolean;
    ManagedPropertyLogicalName: string;
    }

    export interface LocalizedLabel {
    MetadataId: string;
    HasChanged?: any;
    IsManaged: boolean;
    Label: any;
    LanguageCode: number;
    _type: string;
    }

    export interface UserLocalizedLabel {
    MetadataId: string;
    HasChanged?: any;
    IsManaged: boolean;
    Label?: any;
    LanguageCode: number;
    }

    export interface Description {
    LocalizedLabels: LocalizedLabel[];
    UserLocalizedLabel: UserLocalizedLabel;
    }

    export interface LocalizedLabel {
    Label: any;
    MetadataId: string;
    HasChanged?: any;
    IsManaged: boolean;
    LanguageCode: number;
    _type: string;
    }

    export interface DisplayCollectionName {
    LocalizedLabels: LocalizedLabel[];
    UserLocalizedLabel: UserLocalizedLabel;
    }

    export interface LocalizedLabel {
    MetadataId: string;
    HasChanged?: any;
    IsManaged: boolean;
    Label: any;
    LanguageCode: number;
    _type: string;
    }

    export interface DisplayName {
    LocalizedLabels: LocalizedLabel[];
    UserLocalizedLabel: UserLocalizedLabel;
    }

    export interface IsAuditEnabled {
    CanBeChanged: boolean;
    ManagedPropertyLogicalName: string;
    Value: boolean;
    }

    export interface IsCustomizable {
    CanBeChanged: boolean;
    ManagedPropertyLogicalName: string;
    Value: boolean;
    }

    export interface IsValidForQueue {
    CanBeChanged: boolean;
    ManagedPropertyLogicalName: string;
    Value: boolean;
    }

    export interface XrmMetaRelation {
    IsCustomRelationship?: boolean;
    IsValidForAdvancedFind?: boolean;
    SchemaName: string;
    RelationshipType?: XrmMetaRelationshipType;
    }

    export interface XrmMetaOneToManyRelation extends XrmMetaRelation {
    ReferencedAttribute: string;
    ReferencedEntity: string;
    ReferencingAttribute: string;
    ReferencingEntity: string;
    ReferencedEntityNavigationPropertyName: string;
    ReferencingEntityNavigationPropertyName: string;
    }

    export interface XrmMetaManyToManyRelation extends XrmMetaRelation {
    Entity1LogicalName: string;
    Entity2LogicalName: string;
    IntersectEntityName: string;
    Entity1IntersectAttribute: string;
    Entity2IntersectAttribute: string;
    Entity1NavigationPropertyName: string;
    Entity2NavigationPropertyName: string;
    }

    export enum XrmMetaRelationshipType {
    OneToManyRelationship = 0,
    Default = 0,
    ManyToManyRelationship = 1
    }

    1,196 changes: 1,196 additions & 0 deletions xrm-meta-sdk.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,1196 @@
    import {
    XrmMetaOneToManyRelation,
    XrmMetaManyToManyRelation,
    IQueryRelation,
    QueryColumnType,
    QueryRelationType,
    IQueryTable,
    XrmEntityFilter,
    XrmMetaEntity,
    DisplayName
    XrmMetaAttribute } from "xrm-meta-models";

    import { Observable } from "rxjs";
    import { HttpClient, HttpHeaders } from "@angular/common/http";

    // tslint:disable:no-bitwise
    // tslint:disable:max-line-length

    export class XrmMetaSdk {
    private _arrayElements = [
    "Attributes",
    "ManyToManyRelationships",
    "ManyToOneRelationships",
    "OneToManyRelationships",
    "Privileges",
    "LocalizedLabels",
    "Options",
    "Targets"
    ];

    constructor(private http: HttpClient, private clientUrl: string) {}

    public GetTables(): Observable<IQueryTable[]> {
    const _this = this;
    return new Observable<IQueryTable[]>(subscriber => {
    if (_this.IsLocal()) {
    this.http
    .get<XrmMetaEntity[]>("http://localhost:54566/GetEntity")
    .subscribe(
    entities => {
    subscriber.next(this.ParseTables(entities));
    },
    err => {
    subscriber.error(err);
    },
    () => {
    subscriber.complete();
    }
    );
    } else {
    _this.RetrieveAllEntities(
    (entities: XrmMetaEntity[]) => {
    subscriber.next(this.ParseTables(entities));
    subscriber.complete();
    },
    err => {
    subscriber.error(err);
    }
    );
    }
    });
    }

    public GetColumnsOfTable(table: IQueryTable): Observable<IQueryColumn[]> {
    const _this = this;
    return new Observable<IQueryColumn[]>(subscriber => {
    if (_this.IsLocal()) {
    this.http
    .get<XrmMetaAttribute[]>(
    "http://localhost:54566/GetAttribute/" + table.Id
    )
    .subscribe(
    attributes => {
    const columns = _this.ParseColumns(table.Id, attributes);
    subscriber.next(columns);
    },
    err => {
    subscriber.error(err);
    },
    () => {
    subscriber.complete();
    }
    );
    } else {
    const __this = _this;
    _this.RetrieveAttributes(
    table.Id,
    (attributes: XrmMetaAttribute[]) => {
    const columns = _this.ParseColumns(table.Id, attributes);
    subscriber.next(columns);
    subscriber.complete();
    },
    err => {
    subscriber.error(err);
    }
    );
    }
    });
    }

    public GetRelationsOfTable(table: IQueryTable): Observable<IQueryRelation[]> {
    const _this = this;
    return new Observable<IQueryRelation[]>(subscriber => {
    if (_this.IsLocal()) {
    _this.http
    .get<XrmMetaEntity>(
    "http://localhost:54566/GetRelationships/" + table.Id
    )
    .subscribe(
    entity => {
    const o2ms = entity.OneToManyRelationships;
    const m2os = entity.ManyToOneRelationships;
    const m2ms = entity.ManyToManyRelationships;
    subscriber.next(this.ParseRelations(table.Id, o2ms, m2os, m2ms));
    },
    err => {
    subscriber.error(err);
    },
    () => {
    subscriber.complete();
    }
    );
    } else {
    const __this = _this;
    __this.RetrieveRelations(
    table.Id, null,
    (entity) => {
    const o2ms = entity.OneToManyRelationships;
    const m2os = entity.ManyToOneRelationships;
    const m2ms = entity.ManyToManyRelationships;
    subscriber.next(this.ParseRelations(table.Id, o2ms, m2os, m2ms));
    subscriber.complete();
    }, (err) => {
    subscriber.error(err);
    }
    );
    }
    });
    }

    private ParseTables(
    entities: XrmMetaEntity[]): IQueryTable[] {
    const tables = entities.map(entity => {
    return {
    Alias: "",
    Id: entity.LogicalName,
    Name: this.readLabel(entity),
    PrimaryKeyName: entity.PrimaryIdAttribute,
    PrimaryName: entity.PrimaryNameAttribute
    } as IQueryTable;
    });

    return tables;
    }

    private ParseColumns(
    tableId: string,
    attributes: XrmMetaAttribute[]
    ): IQueryColumn[] {
    const columns = attributes.map(att => {
    return {
    Alias: "",
    Id: att.LogicalName,
    Name: this.readLabel(att),
    Table: tableId,
    OptionSet: att.OptionsSet.Options.map(op => {
    return {
    Key: this.readOptionLabel(op),
    Value: op.Value
    } as IQueryOption;
    }),
    Type: this.readAttributeType(att.AttributeType),
    Targets: att.Targets
    } as IQueryColumn;
    });
    return columns;
    }

    private ParseRelations(
    tableId: string,
    o2ms: XrmMetaOneToManyRelation[],
    m2os: XrmMetaOneToManyRelation[],
    m2ms: XrmMetaManyToManyRelation[],
    ): IQueryRelation[] {
    const relations: IQueryRelation[] = [];
    for (let i = 0; i < o2ms.length; i++) {
    const o2m = o2ms[i];
    const relation: IQueryRelation = ({
    ParentColumn: o2m.ReferencedAttribute,
    ParentTable: o2m.ReferencedEntity,
    ChildColumn: o2m.ReferencingAttribute,
    ChildTable: o2m.ReferencingEntity,
    Id: o2m.SchemaName,
    RelationType: QueryRelationType.OneToMany
    } as any) as IQueryRelation;
    relations.push(relation);
    }
    for (let i = 0; i < m2os.length; i++) {
    const m2o = m2os[i];
    const relation: IQueryRelation = ({
    ChildColumn: m2o.ReferencedAttribute,
    ChildTable: m2o.ReferencedEntity,
    ParentColumn: m2o.ReferencingAttribute,
    ParentTable: m2o.ReferencingEntity,
    Id: m2o.SchemaName,
    RelationType: QueryRelationType.ManyToOne
    } as any) as IQueryRelation;
    relations.push(relation);
    }
    for (let i = 0; i < m2ms.length; i++) {
    const m2m = m2ms[i];
    let pC, cC, pT, cT, iP, iC;
    if (m2m.Entity1LogicalName.toLowerCase() === tableId.toLowerCase()) {
    pT = m2m.Entity1LogicalName;
    cT = m2m.Entity2LogicalName;
    iP = m2m.Entity1IntersectAttribute;
    iC = m2m.Entity2IntersectAttribute;
    } else {
    pT = m2m.Entity2LogicalName;
    cT = m2m.Entity1LogicalName;
    iP = m2m.Entity2IntersectAttribute;
    iC = m2m.Entity1IntersectAttribute;
    }
    // TODO: Add proper table id here
    pC = pT + "id";
    cC = cT + "id";
    const relation: IQueryRelation = {
    ChildColumn: cC,
    ChildTable: cT,
    ParentColumn: pC,
    ParentTable: pT,
    Id: m2m.SchemaName,
    RelationType: QueryRelationType.ManyToMany,
    IntermediateChildColumn: iC,
    IntermediateParentColumn: iP,
    IntermediateTable: m2m.IntersectEntityName
    } as IQueryRelation;
    relations.push(relation);
    }
    return relations;
    }
    //#region internal helpers

    private readLabel(metaRecord: any): string {
    try {
    const disp = metaRecord.Label as DisplayName;
    return disp.UserLocalizedLabel.Label;
    } catch {
    let label = null;

    for (let i = 0; i < metaRecord.DisplayName.LocalizedLabels.length; i++) {
    if (metaRecord.DisplayName.LocalizedLabels[i].LanguageCode === 1033) {
    label = metaRecord.DisplayName.LocalizedLabels[i].Label;
    }
    }

    if (label === null) {
    if (metaRecord.DisplayName.LocalizedLabels.length > 0) {
    label = metaRecord.DisplayName.LocalizedLabels[0].Label;
    } else {
    label = metaRecord.LogicalName;
    }
    }
    return label;
    }
    }
    private readOptionLabel(metaRecord: any): string {
    try {
    const disp = metaRecord.Label as DisplayName;
    return disp.UserLocalizedLabel.Label;
    } catch {
    let label = null;

    for (let i = 0; i < metaRecord.Label.LocalizedLabels.length; i++) {
    if (metaRecord.Label.LocalizedLabels[i].LanguageCode === 1033) {
    label = metaRecord.Label.LocalizedLabels[i].Label;
    }
    }

    if (label === null) {
    if (metaRecord.Label.LocalizedLabels.length > 0) {
    label = metaRecord.Label.LocalizedLabels[0].Label;
    } else {
    label = "Option #";
    }
    }
    return label;
    }
    }
    private readAttributeType(type: string): QueryColumnType {
    switch (type) {
    case "Lookup": {
    return QueryColumnType.Lookup;
    }
    case "State": {
    return QueryColumnType.State;
    }
    case "Memo": {
    return QueryColumnType.Memo;
    }
    case "Boolean": {
    return QueryColumnType.Boolean;
    }
    case "String": {
    return QueryColumnType.String;
    }
    case "Integer": {
    return QueryColumnType.Integer;
    }
    case "DateTime": {
    return QueryColumnType.DateTime;
    }
    case "Status": {
    return QueryColumnType.Status;
    }
    case "Uniqueidentifier": {
    return QueryColumnType.Uniqueidentifier;
    }
    case "Picklist": {
    return QueryColumnType.Picklist;
    }
    case "Owner": {
    return QueryColumnType.Owner;
    }
    case "BigInt": {
    return QueryColumnType.BigInt;
    }
    case "EntityName": {
    return QueryColumnType.EntityName;
    }
    case "Virtual": {
    return QueryColumnType.Virtual;
    }
    case "Decimal": {
    return QueryColumnType.Decimal;
    }
    case "ManagedProperty": {
    return QueryColumnType.ManagedProperty;
    }
    case "Customer": {
    return QueryColumnType.Customer;
    }
    case "PartyList": {
    return QueryColumnType.PartyList;
    }
    case "Double": {
    return QueryColumnType.Double;
    }
    case "Money": {
    return QueryColumnType.Money;
    }
    case "CalendarRules": {
    return QueryColumnType.CalendarRules;
    }
    default: {
    return QueryColumnType.None;
    }
    }
    }

    private IsLocal(): boolean {
    return (
    window.location.hostname === "localhost" ||
    window.location.hostname === "127.0.01"
    );
    }

    private getUrl() {
    return this.clientUrl || window.location.hostname;
    }

    private getError(resp) {
    if (resp.status === 12029) {
    return new Error("The attempt to connect to the server failed.");
    }
    if (resp.status === 12007) {
    return new Error("The server name could not be resolved.");
    }
    const faultXml = resp.responseXML;
    let errorMessage = "Unknown (unable to parse the fault)";
    let faultstring = null;
    let ErrorCode = null;
    if (typeof faultXml === "object") {
    const bodyNode = faultXml.firstChild.firstChild;

    // Retrieve the fault node
    for (let i = 0; i < bodyNode.childNodes.length; i++) {
    const node = bodyNode.childNodes[i];
    // NOTE: This comparison does not handle the case where the XML namespace changes
    if ("s:Fault" === node.nodeName) {
    for (let j = 0; j < node.childNodes.length; j++) {
    const testNode = node.childNodes[j];
    if ("faultstring" === testNode.nodeName) {
    faultstring = this.getNodeText(testNode);
    }
    if ("detail" === testNode.nodeName) {
    for (let k = 0; k < testNode.childNodes.length; k++) {
    const orgServiceFault = testNode.childNodes[k];
    if ("OrganizationServiceFault" === orgServiceFault.nodeName) {
    for (let l = 0; l < orgServiceFault.childNodes.length; l++) {
    const ErrorCodeNode = orgServiceFault.childNodes[l];
    if ("ErrorCode" === ErrorCodeNode.nodeName) {
    ErrorCode = this.getNodeText(ErrorCodeNode);
    break;
    }
    }
    }
    }
    }
    }
    break;
    }
    }
    }
    if (ErrorCode !== null && faultstring !== null) {
    errorMessage = "Error Code:" + ErrorCode + " Message: " + faultstring;
    } else {
    if (faultstring !== null) {
    errorMessage = faultstring;
    }
    }
    return new Error(errorMessage);
    }

    private getRequest(entityFiltersValue: string): string {
    return null;
    }

    private xmlEncode(strInput) {
    let c: number;
    let XmlEncode = "";
    if (strInput === null) {
    return null;
    }
    if (strInput === "") {
    return "";
    }
    for (let cnt = 0; cnt < strInput.length; cnt++) {
    c = strInput.charCodeAt(cnt);
    if (
    (c > 96 && c < 123) ||
    (c > 64 && c < 91) ||
    c === 32 ||
    (c > 47 && c < 58) ||
    c === 46 ||
    c === 44 ||
    c === 45 ||
    c === 95
    ) {
    XmlEncode = XmlEncode + String.fromCharCode(c);
    } else {
    XmlEncode = XmlEncode + "&#" + c + ";";
    }
    }
    return XmlEncode;
    }

    private objectifyNode(node) {
    // Check for null
    if (node.attributes != null && node.attributes.length === 1) {
    if (
    node.attributes.getNamedItem("i:nil") != null &&
    node.attributes.getNamedItem("i:nil").nodeValue === "true"
    ) {
    return null;
    }
    }

    // Check if it is a value
    if (node.firstChild != null && node.firstChild.nodeType === 3) {
    const nodeName = this.getNodeName(node);

    switch (nodeName) {
    // Integer Values
    case "ActivityTypeMask":
    case "ObjectTypeCode":
    case "ColumnNumber":
    case "DefaultFormValue":
    case "MaxValue":
    case "MinValue":
    case "MaxLength":
    case "Order":
    case "Precision":
    case "PrecisionSource":
    case "LanguageCode":
    return parseInt(node.firstChild.nodeValue, 10);
    // Boolean values
    case "AutoRouteToOwnerQueue":
    case "CanBeChanged":
    case "CanTriggerWorkflow":
    case "IsActivity":
    case "IsAIRUpdated":
    case "IsActivityParty":
    case "IsAvailableOffline":
    case "IsChildEntity":
    case "IsCustomEntity":
    case "IsCustomOptionSet":
    case "IsDocumentManagementEnabled":
    case "IsEnabledForCharts":
    case "IsGlobal":
    case "IsImportable":
    case "IsIntersect":
    case "IsManaged":
    case "IsReadingPaneEnabled":
    case "IsValidForAdvancedFind":
    case "CanBeSecuredForCreate":
    case "CanBeSecuredForRead":
    case "CanBeSecuredForUpdate":
    case "IsCustomAttribute":
    case "IsManaged":
    case "IsPrimaryId":
    case "IsPrimaryName":
    case "IsSecured":
    case "IsValidForCreate":
    case "IsValidForRead":
    case "IsValidForUpdate":
    case "IsCustomRelationship":
    case "CanBeBasic":
    case "CanBeDeep":
    case "CanBeGlobal":
    case "CanBeLocal":
    return node.firstChild.nodeValue === "true" ? true : false;
    // OptionMetadata.Value and BooleanManagedProperty.Value and AttributeRequiredLevelManagedProperty.Value
    case "Value":
    // BooleanManagedProperty.Value
    if (
    node.firstChild.nodeValue === "true" ||
    node.firstChild.nodeValue === "false"
    ) {
    return node.firstChild.nodeValue === "true" ? true : false;
    }
    // AttributeRequiredLevelManagedProperty.Value
    if (
    node.firstChild.nodeValue === "ApplicationRequired" ||
    node.firstChild.nodeValue === "None" ||
    node.firstChild.nodeValue === "Recommended" ||
    node.firstChild.nodeValue === "SystemRequired"
    ) {
    return node.firstChild.nodeValue;
    }
    const numberValue = parseInt(node.firstChild.nodeValue, 10);
    if (isNaN(numberValue)) {
    // FormatName.Value
    return node.firstChild.nodeValue;
    } else {
    // OptionMetadata.Value
    return numberValue;
    }
    break;
    // String values
    default:
    return node.firstChild.nodeValue;
    }
    }

    // Check if it is a known array
    if (this.isMetadataArray(this.getNodeName(node))) {
    const arrayValue = [];

    for (let i = 0; i < node.childNodes.length; i++) {
    let objectTypeName;
    if (
    node.childNodes[i].attributes != null &&
    node.childNodes[i].attributes.getNamedItem("i:type") != null
    ) {
    objectTypeName = node.childNodes[i].attributes
    .getNamedItem("i:type")
    .nodeValue.split(":")[1];
    } else {
    objectTypeName = this.getNodeName(node.childNodes[i]);
    }

    const b = this.objectifyNode(node.childNodes[i]);
    b._type = objectTypeName;
    arrayValue.push(b);
    }

    return arrayValue;
    }

    // Null entity description labels are returned as <label/> - not using i:nil = true;
    if (node.childNodes.length === 0) {
    return null;
    }

    // Otherwise return an object
    const c: any = {};
    if (node.attributes.getNamedItem("i:type") != null) {
    c._type = node.attributes.getNamedItem("i:type").nodeValue.split(":")[1];
    }
    for (let i = 0; i < node.childNodes.length; i++) {
    if (node.childNodes[i].nodeType === 3) {
    c[this.getNodeName(node.childNodes[i])] = node.childNodes[i].nodeValue;
    } else {
    c[this.getNodeName(node.childNodes[i])] = this.objectifyNode(
    node.childNodes[i]
    );
    }
    }
    return c;
    }

    private isMetadataArray(elementName) {
    for (let i = 0; i < this._arrayElements.length; i++) {
    if (elementName === this._arrayElements[i]) {
    return true;
    }
    }
    return false;
    }

    private selectNodes(node, XPathExpression): any[] {
    if (typeof node.selectNodes != null) {
    return node.selectNodes(XPathExpression);
    } else {
    const output = [];
    const XPathResults = node.evaluate(
    XPathExpression,
    node,
    this.NSResolver,
    XPathResult.ANY_TYPE,
    null
    );
    let result = XPathResults.iterateNext();
    while (result) {
    output.push(result);
    result = XPathResults.iterateNext();
    }
    return output;
    }
    return [];
    }

    private selectSingleNodeText(node, xpathExpr) {
    const x = this.selectSingleNode(node, xpathExpr);
    if (this.isNodeNull(x)) {
    return null;
    }
    if (typeof x.text != null) {
    return x.text;
    } else {
    return x.textContent;
    }
    }

    private selectSingleNode(node, xpathExpr) {
    if (typeof node.selectSingleNode != null) {
    return node.selectSingleNode(xpathExpr);
    } else {
    const xpe = new XPathEvaluator();
    const xPathNode = xpe.evaluate(
    xpathExpr,
    node,
    this.NSResolver,
    XPathResult.FIRST_ORDERED_NODE_TYPE,
    null
    );
    return xPathNode != null ? xPathNode.singleNodeValue : null;
    }
    }

    private getNodeText(node) {
    if (typeof node.text !== "undefined") {
    return node.text;
    } else {
    return node.textContent;
    }
    }

    private isNodeNull(node) {
    if (node == null) {
    return true;
    }
    if (
    node.attributes.getNamedItem("i:nil") != null &&
    node.attributes.getNamedItem("i:nil").value === "true"
    ) {
    return true;
    }
    return false;
    }

    private getNodeName(node: any) {
    if (typeof node.baseName != null) {
    return node.baseName;
    } else {
    return node.localName;
    }
    }

    private setSelectionNamespaces(doc) {
    const nameSpace = [
    'xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"',
    'xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts"',
    'xmlns:i="http://www.w3.org/2001/XMLSchema-instance"',
    'xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic"',
    'xmlns:c="http://schemas.microsoft.com/xrm/2011/Metadata"'
    ];
    doc.setProperty("SelectionNamespaces", nameSpace.join(" "));
    }

    private NSResolver(prefix) {
    const nameSpace = {
    s: "http://schemas.xmlsoap.org/soap/envelope/",
    a: "http://schemas.microsoft.com/xrm/2011/Contracts",
    i: "http://www.w3.org/2001/XMLSchema-instance",
    b: "http://schemas.datacontract.org/2004/07/System.Collections.Generic",
    c: "http://schemas.microsoft.com/xrm/2011/Metadata"
    };
    return nameSpace[prefix] || null;
    }

    private evaluateEntityFilters(xrmFilter: XrmEntityFilter): string {
    const entityFilterArray = [];
    if ((1 & xrmFilter) === 1) {
    entityFilterArray.push("Entity");
    }
    if ((2 & xrmFilter) === 2) {
    entityFilterArray.push("Attributes");
    }
    if ((4 & xrmFilter) === 4) {
    entityFilterArray.push("Privileges");
    }
    if ((8 & xrmFilter) === 8) {
    entityFilterArray.push("Relationships");
    }
    return entityFilterArray.join(" ");
    }
    //#endregion

    private RetrieveAllEntities(successCallBack, errorCallBack) {
    const _this = this;
    const entityFiltersValue = this.evaluateEntityFilters(1);

    const request = [
    '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">',
    '<soapenv:Header><a:SdkClientVersion xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">7.0</a:SdkClientVersion></soapenv:Header>',
    "<soapenv:Body>",
    '<Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">',
    '<request i:type="a:RetrieveAllEntitiesRequest" xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">',
    '<a:Parameters xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">',
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>EntityFilters</b:key>",
    '<b:value i:type="c:EntityFilters" xmlns:c="http://schemas.microsoft.com/xrm/2011/Metadata">' +
    this.xmlEncode(entityFiltersValue) +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>RetrieveAsIfPublished</b:key>",
    '<b:value i:type="c:boolean" xmlns:c="http://www.w3.org/2001/XMLSchema">' +
    this.xmlEncode("true") +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "</a:Parameters>",
    '<a:RequestId i:nil="true" />',
    "<a:RequestName>RetrieveAllEntities</a:RequestName>",
    "</request>",
    "</Execute>",
    "</soapenv:Body>",
    "</soapenv:Envelope>"
    ].join("");
    const req = new XMLHttpRequest();
    req.open(
    "POST",
    this.getUrl() + "/XRMServices/2011/Organization.svc/web",
    true
    );
    // try {
    // req.responseType = "msxml-document";
    // } catch (e) {}
    req.setRequestHeader("Accept", "application/xml, text/xml, */*");
    req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    req.setRequestHeader(
    "SOAPAction",
    "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute"
    );
    req.onreadystatechange = function() {
    if (req.readyState === 4 /* complete */) {
    req.onreadystatechange = null; // Addresses potential memory leak issue with IE
    if (req.status === 200) {
    // Success
    const doc = req.responseXML;
    try {
    _this.setSelectionNamespaces(doc);
    } catch (e) {}
    const entityMetadataNodes = _this.selectNodes(
    doc,
    "//c:EntityMetadata"
    );
    const entityMetadataCollection: XrmMetaEntity[] = [];
    for (let i = 0; i < entityMetadataNodes.length; i++) {
    const a = _this.objectifyNode(entityMetadataNodes[i]);
    a._type = "EntityMetadata";
    entityMetadataCollection.push(a);
    }
    successCallBack(entityMetadataCollection);
    } else {
    errorCallBack(_this.getError(req));
    }
    }
    };
    req.send(request);
    }

    private RetrieveAttributes(LogicalName, successCallBack, errorCallBack) {
    let MetadataId = null;
    const _this = this;
    if (LogicalName == null) {
    throw new Error(
    "SDK.Metadata.RetrieveEntity requires either the LogicalName or MetadataId parameter not be null."
    );
    }
    if (LogicalName != null) {
    if (typeof LogicalName !== "string") {
    throw new Error(
    "SDK.Metadata.RetrieveEntity LogicalName must be a string value."
    );
    }
    MetadataId = "00000000-0000-0000-0000-000000000000";
    }
    if (MetadataId != null && LogicalName == null) {
    if (typeof MetadataId !== "string") {
    throw new Error(
    "SDK.Metadata.RetrieveEntity MetadataId must be a string value."
    );
    }
    }
    if (typeof successCallBack !== "function") {
    throw new Error(
    "SDK.Metadata.RetrieveEntity successCallBack must be a function."
    );
    }
    if (typeof errorCallBack !== "function") {
    throw new Error(
    "SDK.Metadata.RetrieveEntity errorCallBack must be a function."
    );
    }
    const entityFiltersValue = this.evaluateEntityFilters(2);

    let entityLogicalNameValueNode = "";
    if (LogicalName == null) {
    entityLogicalNameValueNode = '<b:value i:nil="true" />';
    } else {
    entityLogicalNameValueNode =
    '<b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">' +
    this.xmlEncode(LogicalName.toLowerCase()) +
    "</b:value>";
    }
    const request = [
    '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">',
    // Allows retrieval if ImageAttributeMetadata objects
    '<soapenv:Header><a:SdkClientVersion xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">6.0</a:SdkClientVersion></soapenv:Header>',
    "<soapenv:Body>",
    '<Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">',
    '<request i:type="a:RetrieveEntityRequest" xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">',
    '<a:Parameters xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">',
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>EntityFilters</b:key>",
    '<b:value i:type="c:EntityFilters" xmlns:c="http://schemas.microsoft.com/xrm/2011/Metadata">' +
    this.xmlEncode(entityFiltersValue) +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>MetadataId</b:key>",
    '<b:value i:type="ser:guid" xmlns:ser="http://schemas.microsoft.com/2003/10/Serialization/">' +
    this.xmlEncode(MetadataId) +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>RetrieveAsIfPublished</b:key>",
    '<b:value i:type="c:boolean" xmlns:c="http://www.w3.org/2001/XMLSchema">' +
    this.xmlEncode("true") +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>LogicalName</b:key>",
    entityLogicalNameValueNode,
    "</a:KeyValuePairOfstringanyType>",
    "</a:Parameters>",
    '<a:RequestId i:nil="true" />',
    "<a:RequestName>RetrieveEntity</a:RequestName>",
    "</request>",
    "</Execute>",
    "</soapenv:Body>",
    "</soapenv:Envelope>"
    ].join("");
    const req = new XMLHttpRequest();
    req.open(
    "POST",
    this.getUrl() + "/XRMServices/2011/Organization.svc/web",
    true
    );
    // try {
    // req.responseType = "msxml-document";
    // } catch (e) {}
    req.setRequestHeader("Accept", "application/xml, text/xml, */*");
    req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    req.setRequestHeader(
    "SOAPAction",
    "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute"
    );
    req.onreadystatechange = function() {
    if (req.readyState === 4 /* complete */) {
    req.onreadystatechange = null; // Addresses potential memory leak issue with IE
    if (req.status === 200) {
    const doc = req.responseXML;
    try {
    _this.setSelectionNamespaces(doc);
    } catch (e) {}
    const a: XrmMetaEntity = _this.objectifyNode(
    _this.selectSingleNode(doc, "//b:value")
    );
    a._type = "EntityMetadata";
    successCallBack(a.Attributes);
    } else {
    // Failure
    errorCallBack(_this.getError(req));
    }
    }
    };
    req.send(request);
    }

    private RetrieveRelations(
    LogicalName,
    MetadataId,
    successCallBack,
    errorCallBack
    ) {
    const _this = this;
    if (LogicalName == null && MetadataId == null) {
    throw new Error(
    "SDK.Metadata.RetrieveEntity requires either the LogicalName or MetadataId parameter not be null."
    );
    }
    if (LogicalName != null) {
    if (typeof LogicalName !== "string") {
    throw new Error(
    "SDK.Metadata.RetrieveEntity LogicalName must be a string value."
    );
    }
    MetadataId = "00000000-0000-0000-0000-000000000000";
    }
    if (MetadataId != null && LogicalName == null) {
    if (typeof MetadataId !== "string") {
    throw new Error(
    "SDK.Metadata.RetrieveEntity MetadataId must be a string value."
    );
    }
    }
    if (typeof successCallBack !== "function") {
    throw new Error(
    "SDK.Metadata.RetrieveEntity successCallBack must be a function."
    );
    }
    if (typeof errorCallBack !== "function") {
    throw new Error(
    "SDK.Metadata.RetrieveEntity errorCallBack must be a function."
    );
    }
    // Attributes and entities
    const entityFiltersValue = this.evaluateEntityFilters(8);

    let entityLogicalNameValueNode = "";
    if (LogicalName == null) {
    entityLogicalNameValueNode = '<b:value i:nil="true" />';
    } else {
    entityLogicalNameValueNode =
    '<b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">' +
    this.xmlEncode(LogicalName.toLowerCase()) +
    "</b:value>";
    }
    const request = [
    '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">',
    // Allows retrieval if ImageAttributeMetadata objects
    '<soapenv:Header><a:SdkClientVersion xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">6.0</a:SdkClientVersion></soapenv:Header>',
    "<soapenv:Body>",
    '<Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">',
    '<request i:type="a:RetrieveEntityRequest" xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">',
    '<a:Parameters xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">',
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>EntityFilters</b:key>",
    '<b:value i:type="c:EntityFilters" xmlns:c="http://schemas.microsoft.com/xrm/2011/Metadata">' +
    this.xmlEncode(entityFiltersValue) +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>MetadataId</b:key>",
    '<b:value i:type="ser:guid" xmlns:ser="http://schemas.microsoft.com/2003/10/Serialization/">' +
    this.xmlEncode(MetadataId) +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>RetrieveAsIfPublished</b:key>",
    '<b:value i:type="c:boolean" xmlns:c="http://www.w3.org/2001/XMLSchema">' +
    this.xmlEncode("true") +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>LogicalName</b:key>",
    entityLogicalNameValueNode,
    "</a:KeyValuePairOfstringanyType>",
    "</a:Parameters>",
    '<a:RequestId i:nil="true" />',
    "<a:RequestName>RetrieveEntity</a:RequestName>",
    "</request>",
    "</Execute>",
    "</soapenv:Body>",
    "</soapenv:Envelope>"
    ].join("");
    const req = new XMLHttpRequest();
    req.open(
    "POST",
    this.getUrl() + "/XRMServices/2011/Organization.svc/web",
    true
    );
    // try {
    // req.responseType = "msxml-document";
    // } catch (e) {}
    req.setRequestHeader("Accept", "application/xml, text/xml, */*");
    req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    req.setRequestHeader(
    "SOAPAction",
    "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute"
    );
    req.onreadystatechange = function() {
    if (req.readyState === 4 /* complete */) {
    req.onreadystatechange = null; // Addresses potential memory leak issue with IE
    if (req.status === 200) {
    const doc = req.responseXML;
    try {
    _this.setSelectionNamespaces(doc);
    } catch (e) {}
    const a: XrmMetaEntity = _this.objectifyNode(
    _this.selectSingleNode(doc, "//b:value")
    );
    a._type = "EntityMetadata";
    successCallBack(a);
    } else {
    // Failure
    errorCallBack(_this.getError(req));
    }
    }
    };
    req.send(request);
    }
    private RetrieveAttribute(
    EntityLogicalName,
    LogicalName,
    MetadataId,
    successCallBack,
    errorCallBack
    ) {
    const _this = this;
    if (
    EntityLogicalName == null &&
    LogicalName == null &&
    MetadataId == null
    ) {
    throw new Error(
    "SDK.Metadata.RetrieveAttribute requires either the EntityLogicalName and LogicalName parameters or the MetadataId parameter not be null."
    );
    }
    if (
    MetadataId != null &&
    EntityLogicalName == null &&
    LogicalName == null
    ) {
    if (typeof MetadataId !== "string") {
    throw new Error(
    "SDK.Metadata.RetrieveEntity MetadataId must be a string value."
    );
    }
    } else {
    MetadataId = "00000000-0000-0000-0000-000000000000";
    }
    if (EntityLogicalName != null) {
    if (typeof EntityLogicalName !== "string") {
    {
    throw new Error(
    "SDK.Metadata.RetrieveAttribute EntityLogicalName must be a string value."
    );
    }
    }
    }
    if (LogicalName != null) {
    if (typeof LogicalName !== "string") {
    {
    throw new Error(
    "SDK.Metadata.RetrieveAttribute LogicalName must be a string value."
    );
    }
    }
    }
    if (typeof successCallBack !== "function") {
    throw new Error(
    "SDK.Metadata.RetrieveAttribute successCallBack must be a function."
    );
    }
    if (typeof errorCallBack !== "function") {
    throw new Error(
    "SDK.Metadata.RetrieveAttribute errorCallBack must be a function."
    );
    }

    let entityLogicalNameValueNode;
    if (EntityLogicalName == null) {
    entityLogicalNameValueNode = '<b:value i:nil="true" />';
    } else {
    entityLogicalNameValueNode =
    '<b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">' +
    _this.xmlEncode(EntityLogicalName.toLowerCase()) +
    "</b:value>";
    }
    let logicalNameValueNode;
    if (LogicalName == null) {
    logicalNameValueNode = '<b:value i:nil="true" />';
    } else {
    logicalNameValueNode =
    '<b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">' +
    _this.xmlEncode(LogicalName.toLowerCase()) +
    "</b:value>";
    }
    const request = [
    '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">',
    // Allows retrieval if ImageAttributeMetadata objects
    '<soapenv:Header><a:SdkClientVersion xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">6.0</a:SdkClientVersion></soapenv:Header>',
    "<soapenv:Body>",
    '<Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">',
    '<request i:type="a:RetrieveAttributeRequest" xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">',
    '<a:Parameters xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">',
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>EntityLogicalName</b:key>",
    entityLogicalNameValueNode,
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>MetadataId</b:key>",
    '<b:value i:type="ser:guid" xmlns:ser="http://schemas.microsoft.com/2003/10/Serialization/">' +
    _this.xmlEncode(MetadataId) +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>RetrieveAsIfPublished</b:key>",
    '<b:value i:type="c:boolean" xmlns:c="http://www.w3.org/2001/XMLSchema">' +
    _this.xmlEncode("true") +
    "</b:value>",
    "</a:KeyValuePairOfstringanyType>",
    "<a:KeyValuePairOfstringanyType>",
    "<b:key>LogicalName</b:key>",
    logicalNameValueNode,
    "</a:KeyValuePairOfstringanyType>",
    "</a:Parameters>",
    '<a:RequestId i:nil="true" />',
    "<a:RequestName>RetrieveAttribute</a:RequestName>",
    "</request>",
    "</Execute>",
    "</soapenv:Body>",
    "</soapenv:Envelope>"
    ].join("");
    const req = new XMLHttpRequest();
    req.open(
    "POST",
    _this.getUrl() + "/XRMServices/2011/Organization.svc/web",
    true
    );
    // try {
    // req.responseType = "msxml-document";
    // } catch (e) {}
    req.setRequestHeader("Accept", "application/xml, text/xml, */*");
    req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    req.setRequestHeader(
    "SOAPAction",
    "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute"
    );
    req.onreadystatechange = function() {
    if (req.readyState === 4 /* complete */) {
    req.onreadystatechange = null; // Addresses potential memory leak issue with IE
    if (req.status === 200) {
    // Success
    const doc = req.responseXML;
    try {
    _this.setSelectionNamespaces(doc);
    } catch (e) {}
    const a = _this.objectifyNode(
    _this.selectSingleNode(doc, "//b:value")
    );
    successCallBack(a);
    } else {
    // Failure
    errorCallBack(_this.getError(req));
    }
    }
    };
    req.send(request);
    }
    }