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

100% Statements 39/39
100% Branches 16/16
100% Functions 13/13
100% Lines 39/39

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    1x 1x 1x 1x 1x                                   35x 35x 1x     34x       3x 1x             34x 2x     34x 3x   34x 4x     34x                     9x 12x     9x 9x 4x     9x 2x 1x         7x 1x                           2x 1x           2x       2x               34x 4x 1x         3x 3x 1x           1x  
"use strict";
 
const _ = require("lodash");
const AnnotationTarget = require("../annotations/AnnotationTarget");
const CollectionType = require("./CollectionType");
const OnDeleteAction = require("./OnDeleteAction");
const ReferentialConstraint = require("./ReferentialConstraint");
 
/**
 * Envelops a navigation property.
 *
 * 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 NavigationProperty
   * @memberof NavigationProperty
   */
  constructor(rawMetadata) {
    super(rawMetadata);
    if (!this.name) {
      throw new Error("Name attribute is mandatory for NavigationProperty.");
    }
 
    if (_.has(this.raw.$, "Partner")) {
      // there is quite a lot of logic related to Partner attribute
      // see http://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/cs01/odata-csdl-xml-v4.01-cs01.html#sec_PartnerNavigationProperty
      // since it is not used in library so far, only value is stored if specified, and no check are performed
      Object.defineProperty(this, "partner", {
        get: () => this.raw.$.Partner,
      });
    }
 
    // there is quite a lot of logic related to ContainsTarget attribute
    // see http://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/cs01/odata-csdl-xml-v4.01-cs01.html#sec_ContainmentNavigationProperty
    // since it is not used in library so far, only value is stored, and no check are performed
    Object.defineProperty(this, "containsTarget", {
      get: () => this.raw.$.ContainsTarget === "true",
    });
 
    let constraints = (rawMetadata.ReferentialConstraint || []).map(
      (c) => new ReferentialConstraint(c)
    );
    Object.defineProperty(this, "referentialConstraints", {
      get: () => constraints,
    });
 
    this._processOnDelete(rawMetadata);
  }
 
  /**
   * Initializes schema dependent properties. Decoupled from constructor,
   * because it needs to resolve schema (type) references.
   *
   * @param {CsdlSchema} schema to resolve references
   * @memberof NavigationProperty
   */
  initSchemaDependentProperties(schema) {
    Object.defineProperty(this, "type", {
      get: () => schema.getType(this.raw.$.Type),
    });
 
    let isCollection = this.type instanceof CollectionType;
    Object.defineProperty(this, "isCollection", {
      get: () => isCollection,
    });
 
    if (isCollection) {
      if (_.has(this.raw.$, "Nullable")) {
        throw new Error(
          "Nullable MUST NOT be specified for a collection-valued navigation property."
        );
      }
    } else {
      Object.defineProperty(this, "nullable", {
        get: () => this.raw.$.Nullable !== "false",
      });
    }
  }
 
  /**
   * Gets navigation property target information.
   *
   * @param {CsdlSchema} schema to resolve model paths
   * @param {EntitySet|Singleton} source model of the source set/singleton
   * @returns {Object} navigation property target
   * @memberof NavigationProperty
   */
  getTarget(schema, source) {
    let navigationBinding = source.navigationPropertyBindings.filter(
      (b) => b.path === this.name
    )[0];
    // Target must resolve to an entity set, singleton, or direct or indirect containment navigation property of a singleton in scope.
    // The path can traverse single-valued containment navigation properties or single-valued complex properties before ending in a containment
    // navigation property, and there MUST NOT be any non-containment navigation properties prior to the final segment.
    // we are currently assuming that the target can be entity set only
    let entitySet = navigationBinding
      ? schema.getEntityContainer().resolveModelPath(navigationBinding.target)
      : undefined;
 
    return {
      entitySet: entitySet,
      entityType: this.type,
      isMultiple: this.isCollection,
    };
  }
 
  _processOnDelete(rawMetadata) {
    if (_.has(rawMetadata, "OnDelete")) {
      if (rawMetadata.OnDelete.length > 1) {
        throw new Error(
          "NavigationProperty may contain at most one child element OnDelete."
        );
      }
 
      let onDelete = new OnDeleteAction(rawMetadata.OnDelete[0]);
      Object.defineProperty(this, "onDelete", {
        get: () => onDelete,
      });
    }
  }
}
 
module.exports = NavigationProperty;