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 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 22921x 1x 11469x 11469x 11469x 11469x 56074x 56074x 56074x 56074x 56074x 56074x 56074x 56074x 56074x 56074x 56074x 56074x 56074x 56074x 56074x 56074x 56074x 11469x 11469x 10935x 10935x 11469x 11469x 248x 248x 11469x 11469x 265x 265x 11469x 11469x 348x 328x 328x 20x 20x 348x 1x 1x 19x 19x 348x     19x 19x 348x     19x 19x 19x 348x     19x 19x 348x 8x 8x 8x 8x 11x 11x 11x 11469x 11469x 19x 19x 19x 2x 2x 17x 19x 3x 3x 14x 19x 2x 2x 12x 19x 12x 12x 12x 12x 12x 12x 12x 12x 12x 12x 12x       11469x 11469x  
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 [];
  }
 
}