All files / src/rules cds_naming.ts

93.38% Statements 127/136
81.25% Branches 26/32
100% Functions 8/8
93.38% Lines 127/136

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 132 133 134 135 136 1371x 1x 1x 1x 1x 1x 1x 1x 1x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 23441x 1x 11729x 11729x 11729x 11729x 57354x 57354x 57354x 57354x 57354x 57354x 57354x 57354x 57354x 57354x 57354x 57354x 57354x 57354x 57354x 57354x 57354x 11729x 11729x 11183x 11183x 11729x 11729x 254x 254x 11729x 11729x 271x 271x 11729x 11729x 359x 337x 337x 22x 22x 359x 1x 1x 21x 21x 359x     21x 21x 359x     21x 21x 21x 359x     21x 21x 359x 8x 8x 8x 8x 13x 13x 13x 11729x 11729x 21x 21x 21x 2x 2x 19x 21x 3x 3x 16x 21x 2x 2x 14x 21x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x       11729x 11729x  
import {Issue} from "../issue";
import {IRule, IRuleMetadata, RuleTag} from "./_irule";
import {IObject} from "../objects/_iobject";
import {IRegistry} from "../_iregistry";
import {BasicRuleConfig} from "./_basic_rule_config";
import {DataDefinition} from "../objects";
import {CDSDefineView, CDSDefineAbstract, CDSDefineCustom, CDSDefineTableFunction, CDSExtendView, CDSDefineProjection} from "../cds/expressions";
import {ExpressionNode} from "../abap/nodes/expression_node";
 
export class CDSNamingConf extends BasicRuleConfig {
  /** Expected prefix for basic interface views */
  public basicInterfaceView: string = "ZI_";
  /** Expected prefix for composite interface views */
  public compositeInterfaceView: string = "ZI_";
  /** Expected prefix for consumption views */
  public consumptionView: string = "ZC_";
  /** Expected prefix for basic restricted reuse views */
  public basicRestrictedReuseView: string = "ZR_";
  /** Expected prefix for composite restricted reuse views */
  public compositeRestrictedReuseView: string = "ZR_";
  /** Expected prefix for private views */
  public privateView: string = "ZP_";
  /** Expected prefix for remote API views */
  public remoteAPIView: string = "ZA_";
  /** Expected prefix for view extends */
  public viewExtend: string = "ZX_";
  /** Expected prefix for extension include views */
  public extensionIncludeView: string = "ZE_";
  /** Expected prefix for derivation functions */
  public derivationFunction: string = "ZF_";
  /** Expected prefix for abstract entities */
  public abstractEntity: string = "ZD_";
}
 
export class CDSNaming implements IRule {
  private conf = new CDSNamingConf();
 
  public getMetadata(): IRuleMetadata {
    return {
      key: "cds_naming",
      title: "CDS Naming",
      shortDescription: `Checks CDS naming conventions based on the VDM prefix rules`,
      extendedInformation: `Validates that CDS entity names follow the expected prefix conventions:
I_ for interface views, C_ for consumption views, R_ for restricted reuse views,
P_ for private views, A_ for remote API views, X_ for view extends,
E_ for extension include views, F_ for derivation functions, D_ for abstract entities.
 
Names must also start with Z after the prefix (custom namespace).
 
https://help.sap.com/docs/SAP_S4HANA_ON-PREMISE/ee6ff9b281d8448f96b4fe6c89f2bdc8/8a8cee943ef944fe8936f4cc60ba9bc1.html`,
      tags: [RuleTag.SingleFile, RuleTag.Naming],
      badExample: `define view entity ZMY_VIEW as select from source { key id }`,
      goodExample: `define view entity ZI_MY_VIEW as select from source { key id }`,
    };
  }
 
  public getConfig() {
    return this.conf;
  }
 
  public setConfig(conf: CDSNamingConf) {
    this.conf = conf;
  }
 
  public initialize(_reg: IRegistry): IRule {
    return this;
  }
 
  public run(o: IObject): Issue[] {
    if (o.getType() !== "DDLS" || !(o instanceof DataDefinition)) {
      return [];
    }
 
    const tree = o.getTree();
    if (tree === undefined) {
      return [];
    }
 
    const file = o.findSourceFile();
    if (file === undefined) {
      return [];
    }
 
    const name = o.getDefinitionName();
    if (name === undefined) {
      return [];
    }
 
    const nameStr = name.toUpperCase();
    const allowedPrefixes = this.getAllowedPrefixes(tree);
    if (allowedPrefixes.length === 0) {
      return [];
    }
 
    const matched = allowedPrefixes.some(p => nameStr.startsWith(p.toUpperCase()));
    if (!matched) {
      const prefixList = [...new Set(allowedPrefixes)].join(", ");
      const message = `CDS name "${name}" should have one of the expected prefixes: ${prefixList}`;
      return [Issue.atRow(file, 1, message, this.getMetadata().key, this.getConfig().severity)];
    }
 
    return [];
  }
 
  private getAllowedPrefixes(tree: ExpressionNode): string[] {
    const root = tree.get();
 
    if (root instanceof CDSExtendView) {
      return [this.conf.viewExtend];
    }
 
    if (root instanceof CDSDefineAbstract) {
      return [this.conf.abstractEntity];
    }
 
    if (root instanceof CDSDefineTableFunction) {
      return [this.conf.derivationFunction];
    }
 
    if (root instanceof CDSDefineView || root instanceof CDSDefineProjection || root instanceof CDSDefineCustom) {
      return [
        this.conf.basicInterfaceView,
        this.conf.compositeInterfaceView,
        this.conf.consumptionView,
        this.conf.basicRestrictedReuseView,
        this.conf.compositeRestrictedReuseView,
        this.conf.privateView,
        this.conf.remoteAPIView,
        this.conf.extensionIncludeView,
      ];
    }

    return [];
  }
 
}