All files / lib/model/nw/schema NavigationProperty.js

100% Statements 35/35
100% Branches 8/8
100% Functions 13/13
100% Lines 34/34

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131    1x                                       41x 41x   41x 20x     41x 4x     41x 16x     41x   8x 8x 1x       7x       41x   5x   5x 1x         4x       41x   1x           41x   2x                         4x     4x     4x 1x         6x 3x 1x         2x                             3x 1x               1x  
"use strict";
 
const AnnotationTarget = require("../../oasis/annotations/AnnotationTarget");
 
/**
 * Envelops a navigation property.
 *
 * There are big differences OASIS-CSDL and MC-CSDL navigation properties. SAP follows MC-CSDL in this.
 *
 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/mc-csdl/e83d21c4-7f0a-4cc7-ac38-f2fbe15d3398
 * (http://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/cs01/odata-csdl-xml-v4.01-cs01.html#sec_NavigationProperty)
 *
 * @class NavigationProperty
 * @extends {AnnotationTarget}
 */
class NavigationProperty extends AnnotationTarget {
  /**
   * Creates an instance of NavigationProperty.
   * @param {Object} rawMetadata raw metadata object for navigation property
   * @memberof NavigationProperty
   */
  constructor(...args) {
    const rawMetadata = args[0];
    super(...args);
 
    Object.defineProperty(this, "relationship", {
      get: () => rawMetadata.$.Relationship,
    });
 
    Object.defineProperty(this, "fromRole", {
      get: () => rawMetadata.$.FromRole,
    });
 
    Object.defineProperty(this, "toRole", {
      get: () => rawMetadata.$.ToRole,
    });
 
    Object.defineProperty(this, "association", {
      get: () => {
        const association = this.model.resolveModelPath(this.relationship);
        if (!association) {
          throw new Error(
            `Association for navigation property ${this.name} does not exists.`
          );
        }
        return association;
      },
    });
 
    Object.defineProperty(this, "associationEnd", {
      get: () => {
        const associationEnd = this.association.findEnd(this.toRole);
 
        if (!associationEnd) {
          throw new Error(
            `Association endpoint for navigation property ${this.name} does not exists.`
          );
        }
 
        return associationEnd;
      },
    });
 
    Object.defineProperty(this, "type", {
      get: () => {
        return {
          elementType: this.associationEnd.type,
        };
      },
    });
 
    Object.defineProperty(this, "isCollection", {
      get: () => {
        return this.associationEnd.multiplicity === "*";
      },
    });
  }
 
  /**
   * Gets navigation property target information.
   *
   * @param {CsdlSchema} schema to resolve model paths
   * @returns {Object} navigation property target
   * @memberof NavigationProperty
   */
  getTarget(schema) {
    let associationSet = schema
      .getEntityContainer()
      .associationSets.find(
        (a) => `${schema.namespace}.${a.association.name}` === this.relationship
      );
 
    if (!associationSet) {
      throw new Error(
        `AssociationSet for association '${this.relationship}' not found.`
      );
    }
 
    let end = associationSet.ends.find((e) => e.role === this.toRole);
    if (!end) {
      throw new Error(
        `AssociationSet End with ${this.toRole} role not present in '${this.relationship}' association set.`
      );
    }
 
    return {
      associationSetEnd: end,
      entitySet: end.entitySet,
      entityType: end.entitySet.entityType,
      isMultiple: end.associationEnd.multiplicity === "*",
    };
  }
 
  /**
   * Gets legacy api object. (XML casing, maybe some other changes.)
   *
   * @returns {Object} legacy api object
   * @memberof NavigationProperty
   */
  getLegacyApiObject() {
    return Object.assign(super.getLegacyApiObject(), {
      getTarget: (schema) => this.getTarget(schema),
      Relationship: this.relationship,
      FromRole: this.fromRole,
      ToRole: this.toRole,
    });
  }
}
 
module.exports = NavigationProperty;