Extending the ISVTE Plugin

Extending the ISVTE Plugin

Rules Explained

The ISVTE Plugin reports are controlled by a set of rules which are executed against an inventory of the package. These rules are defined in the file rules.ts. The rules are categorized based on the sections of the report they apply to.

Inventory Structure

To see the inventory structure that the rules run against, execute the isvte plugin with the –json flag and inspect the result.MetadataInventory object.

sfdx isvte:mdscan -d <metadata folder> --json | jq .result.MetadataInventory

The output will look similar to:

{
  "CustomApplication": {
    "index": "0",
    "count": 1,
    "LightingAppCount": 1,
    "LightningConsoleCount": 0,
    "ClassicAppCount": 0,
    "ClassicConsoleCount": 0
  },
  "ApexClass": {
    "index": "1",
    "count": 2,
    "FutureCalls": 0,
    "AuraEnabledCalls": 1,
    "InvocableCalls": 0,
    "BatchApex": 0,
    "SchedulableApex": 0,
    "ApexRest": 0,
    "ApexSoap": 0
  },
  "FlexiPage": {
    "index": "2",
    "count": 2
  },
  "LightningComponentBundle": {
    "index": "3",
    "count": 5,
    "ExposedComponents": 3,
    "targets": {
      "lightning__AppPage": 3,
      "lightning__HomePage": 1
    }
  },
  "PermissionSet": {
    "index": "4",
    "count": 1
  },
  "CustomTab": {
    "index": "5",
    "count": 1
  },
  "apiVersions": {
    "mdapi": 46,
    "ApexClass": {
      "appAnalyticsRequestController": 45,
      "appAnalyticsRequestControllerTest": 45
    },
    "LightningComponentBundle": {
      "appAnalyticsRequestCard": 45,
      "appAnalyticsRequestList": 45,
      "appAnalyticsRequestPageWrapper": 45,
      "newAnalyticsRequestForm": 45,
      "pubsub": 45
    }
  },
  "componentProperties": {},
  "dependencies": {
    "namespaces": {
      "sfLma": 1
    },
    "components": {
      "sfLMA__Package__c": {
        "type": "Custom",
        "name": "Package",
        "extension": "c",
        "namespace": "sfLMA",
        "fullName": "sfLMA__Package__c"
      },
    }
  }
}

Querying The Inventory

Inventory items are queried using the metadataType propery within rules. This metadataType is almost exactly a metadataType as defined in the Metadata API Documentation but also includes other items that come out of the plugin’s inventory of the package. To reference Lightning Web Components exposed to App Pages from the inventory above you can do:

metadataType: 'LightningComponentBundle.targets.lightning__AppPage'

You can also use * as a wildcard, so to identify the api versions of all ApexClasses, you can do:

metadataType: 'apiVersions.ApexClass.*'

Rule Conditions

Most rules have a condition which causes them to fire. This condition is defined as:

interface ICondition {
  metadataType: string, //The Metadata Type to query
  operator: operatorTypes, // The operator of the condition
  operand?: number | [number | 'minAPI',number | 'minAPI'], //value that operator works on
  expiration?: string, //Expiration date of the condition
  processAlways?: Boolean,  //(only within a conditionOr OR a conditionAnd)
  conditionPerItem?: Boolean, // (only within a conditionAnd)
  conditionOr?: ICondition, //Extra condition to be ORed with this condition
  conditionAnd?: ICondition //Extra condition to be ANDed with this condition
  showDetails?: Boolean //Toggle whether individual items that meet the condition are displayed
}

metadataType is defined as above.

operator can be one of: ‘always’, ‘never’, ‘exists’, ‘notexists’, ‘null’, ‘gt’, ‘gte’, ‘lt’, ‘lte’, ‘eq’, ‘between’

operand is the numerical value (or the placeholder minAPI) that the operator works against.

So a condition like:

{
  metadataType: 'dependencies.namespaces.sfLMA',
  operator: 'gte',
  operand: 1
}

would return true in the sample inventory because there is a dependency identified on the sfLma Namespace

"dependencies": {
    "namespaces": {
      "sfLma": 1
    }
}

conditionAnd and conditionOr are extra conditions that can be used to increase the complexity of a query.

For example:

{
  metadataType: 'Flow.FlowTemplate',
  operator: 'notexists',
  conditionAnd: {
    processAlways: false,
    metadataType: 'Flow',
    operator: 'exists'
  }
},

would return true if there are no Flow Templates and there are Flows.

A condition can have a conditionAnd or a conditionOr or neither, but not both. You can however, chain as many conditions as you need using conditionAnd and conditionOr.

metadataType: 'apiVersions.ApexClass.*',
operator: 'notexists',
conditionOr: {
  metadataType: 'componentProperties.ApexClass.*.StripInaccessible',
  operator: 'eq',
  operand: 1,
  conditionOr: {
    metadataType: 'componentProperties.ApexClass.*.SECURITY_ENFORCED',
    operator: 'eq',
    operand: 1,
    conditionAnd: {
      metadataType: 'apiVersions.ApexClass.*',
      operator: 'gte',
      operand: 48,
      conditionPerItem: true
    }
  }
}

Rule Results

Several Rule Types have a “Result” component which describes what is presented based on the outcome of the condition.

A Result is defined as:

interface IResult {
  label: string, //Friendly output to display when rule is triggered 
  message: string, //Text block to display
  url?: string, //link to content
  showDetails?: Boolean //Toggle whether the individual items that trigger the rule are displayed
}

Inventory Report Rules

Metadata components listed in the Inventory report are defined in the mdTypes list within the rules file. Note that this list defines only what is displayed in the report; it has no impact on what is actually inventoried.

Inventoried items conform to the following interface:

interface IMetadataType {
  label: string,
  metadataType: string;
}

label is the text to be displayed.

metadataType is the item to be counted.

Sample Rules:

{
  label: 'Permission Sets',
  metadataType: 'PermissionSet'
},
{
  label: 'Custom Profiles',
  metadataType: 'Profile'
},
{
  label: 'Custom Metadata',
  metadataType: 'CustomMetadata'
}

Output:

=== Inventory of Package:
Metadata Type                           Count
──────────────────────────────────────  ─────
Permission Sets                         4
Custom Profiles                         0
Custom Metadata                         0

Enablement, Quality, and Partner Alert Rules

Enablement content listed under the Best Practices and Feature Recommendations heading is defined in the enablementRules list.

Code Quality content listed under the Quality Rules heading is defined in the qualityRules list.

Partner Alerts listed under the Partner Alerts heading are defined in the alertRules list.

These rules all follow the general structure:

interface IRule {
  name: string, // The Rule Name
  condition: ICondition, // Logic to determine whether the rule is triggered
  resultTrue?: IResult, //Output if the condition is met 
  resultFalse?: IResult //Output if the condition is not met
}

Sample Rule:

{
    name: 'Flows',
    condition: {
      metadataType: 'Flow',
      operator: 'notexists',
    },
    resultTrue: {
      label: 'Take Advantage of Flows',
      message: 'Flows are a powerful tool to enable forms based workflows and process automation to your users. See this webinar for more information.',
      url: 'https://partners.salesforce.com/0693A000007S2Dq'
    },
  },

Output:

=== Best Practices and Feature Recommendations:

Take Advantage of Flows:
Flows are a powerful tool to enable forms based workflows and process automation to your users. See this webinar for more information.
URL:https://partners.salesforce.com/0693A000007S2Dq

Edition Installation Rules

The rules that define which editions your package can be installed into are defined in the editionWarningRules list. They are defined as:

interface IInstallRule {
  name: string, //Salesforce Edition
  blockingItems: {label: String, condition: ICondition}[] //Conditions which, if true, mean the package cannot be installed in this edition
}

Sample Rules:

{
  name: 'Essentials',
  blockingItems: [{
      label: 'Record Types',
      condition: {
        metadataType: 'RecordType',
        operator: 'exists'  
      }
    },
    {
      label: 'Custom Profiles',
      condition: {
        metadataType: 'Profile',
        operator: 'exists'  
      }
    },
  ]
}

Output

=== Installation Warnings
Package cannot be installed in Essentials due to:
  Record Types 
  Custom Profiles 

Dependency Rules

Dependency Rules identify a dependency on a feature, cloud, or other item. They are defined in the dependencyRules list and follow the structure:

interface IDependecyRule {
  name: string, //Name for the dependency rule
  label: string, //Friendly output of the dependency rule
  condition: ICondition //Condition which fires the dependency rule
}

Sample Rule:

{
  name: 'TCRM',
  label: 'TableauCRM',
  condition: {
    metadataType: 'WaveApplication',
    operator: 'exists',
    conditionOr: {
      metadataType: 'WaveDataflow',
      operator: 'exists',
      conditionOr: {
        metadataType: 'WaveDashboard',
        operator: 'exists',
        conditionOr: {
          metadataType: 'WaveDataset',
          operator: 'exists',
          conditionOr: {
            metadataType: 'WaveLens',
            operator: 'exists',
            conditionOr: {
              metadataType: 'WaveRecipe',
              operator: 'exists',
              conditionOr: {
                metadataType: 'WaveTemplateBundle',
                operator: 'exists',
                conditionOr: {
                  metadataType: 'WaveXmd',
                  operator: 'exists'
                }
                }
              }
            }
          }
        }
    }
  }
}

Output:

=== Dependencies:
TableauCRM 

Data Models

Data models can be used to dynamically create a dependency rule. They are defined in the object dataModels and follow the structure:

interface IDataModel {
  name: string, //Name of the cloud or feature this data model describes
  label: string,  //Friendly output of the cloud or feature name
  fields?: string[], //Array of fields (in Object.Field format) included in this datamodel
  objects?: string[], //Array of objects included in this data model
  namespaces?: string[], //Array of namespaces included in this data model
}

This rule type will fire if any references are found to the namespaces within the namespaces property or if any references are found to objects listed in the objects property.

For example, Work.com uses the wkcc namespace for the Command Center managed package and enabling Work.com in an org creates the objects: Employee,EmployeeCrisisAssessment, InternalOrganizationUnit, and Crisis. If a package refers to these items, then there is a dependecy on Work.com.

{
  name: 'work.com',
  label: 'Work.com',
  namespaces: ['wkcc'],
  objects: ['Employee','EmployeeCrisisAssessment','InternalOrganizationUnit','Crisis'],
  fields: ['Location.Status__c']
},

Feedback or Bugs | Edit this Article