Skip to content

Commit

Permalink
Added command to clean standard page layouts
Browse files Browse the repository at this point in the history
  • Loading branch information
FabienTaillon committed Sep 19, 2019
1 parent 7069575 commit 191d9c2
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 6 deletions.
44 changes: 39 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Link the plugin: sfdx plugins:link .
* [`sfdx texei:data:import -d <string> [-u <string>] [--apiversion <string>] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-texeidataimport--d-string--u-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal)
* [`sfdx texei:package:dependencies:install [-k <string>] [-b <string>] [-p <string>] [-n <string>] [-w <number>] [-r] [-v <string>] [-u <string>] [--apiversion <string>] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-texeipackagedependenciesinstall--k-string--b-string--p-string--n-string--w-number--r--v-string--u-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal)
* [`sfdx texei:source:customlabel:replace -l <string> -v <string> [-p <string>] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-texeisourcecustomlabelreplace--l-string--v-string--p-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal)
* [`sfdx texei:source:layouts:cleanorg [-p <string>] [-v <string>] [-u <string>] [--apiversion <string>] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-texeisourcelayoutscleanorg--p-string--v-string--u-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal)
* [`sfdx texei:user:update [-v <string>] [-u <string>] [--apiversion <string>] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-texeiuserupdate--v-string--u-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal)

## `sfdx texei:data:export -o <string> -d <string> [-u <string>] [--apiversion <string>] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`
Expand Down Expand Up @@ -58,7 +59,7 @@ EXAMPLE
Data exported!
```

_See code: [src/commands/texei/data/export.ts](https://github.com/texei/texei-sfdx-plugin/blob/v0.0.8/src/commands/texei/data/export.ts)_
_See code: [src/commands/texei/data/export.ts](https://github.com/texei/texei-sfdx-plugin/blob/v0.0.9/src/commands/texei/data/export.ts)_

## `sfdx texei:data:import -d <string> [-u <string>] [--apiversion <string>] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`

Expand Down Expand Up @@ -89,7 +90,7 @@ EXAMPLE
Data imported!
```

_See code: [src/commands/texei/data/import.ts](https://github.com/texei/texei-sfdx-plugin/blob/v0.0.8/src/commands/texei/data/import.ts)_
_See code: [src/commands/texei/data/import.ts](https://github.com/texei/texei-sfdx-plugin/blob/v0.0.9/src/commands/texei/data/import.ts)_

## `sfdx texei:package:dependencies:install [-k <string>] [-b <string>] [-p <string>] [-n <string>] [-w <number>] [-r] [-v <string>] [-u <string>] [--apiversion <string>] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`

Expand Down Expand Up @@ -142,7 +143,7 @@ EXAMPLE
$ texei:package:dependencies:install -u MyScratchOrg -v MyDevHub -k "1:MyPackage1Key 2: 3:MyPackage3Key" -b "DEV"
```

_See code: [src/commands/texei/package/dependencies/install.ts](https://github.com/texei/texei-sfdx-plugin/blob/v0.0.8/src/commands/texei/package/dependencies/install.ts)_
_See code: [src/commands/texei/package/dependencies/install.ts](https://github.com/texei/texei-sfdx-plugin/blob/v0.0.9/src/commands/texei/package/dependencies/install.ts)_

## `sfdx texei:source:customlabel:replace -l <string> -v <string> [-p <string>] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`

Expand All @@ -166,7 +167,40 @@ EXAMPLE
$ texei:source:customlabel:replace --label GreatSalesforceBlog --value https://blog.texei.com
```

_See code: [src/commands/texei/source/customlabel/replace.ts](https://github.com/texei/texei-sfdx-plugin/blob/v0.0.8/src/commands/texei/source/customlabel/replace.ts)_
_See code: [src/commands/texei/source/customlabel/replace.ts](https://github.com/texei/texei-sfdx-plugin/blob/v0.0.9/src/commands/texei/source/customlabel/replace.ts)_

## `sfdx texei:source:layouts:cleanorg [-p <string>] [-v <string>] [-u <string>] [--apiversion <string>] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`

delete unused standard layouts from scratch org

```
USAGE
$ sfdx texei:source:layouts:cleanorg [-p <string>] [-v <string>] [-u <string>] [--apiversion <string>] [--json]
[--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]
OPTIONS
-p, --path=path path to layouts
-u, --targetusername=targetusername username or alias for the target
org; overrides default target org
-v, --targetdevhubusername=targetdevhubusername username or alias for the dev hub
org; overrides default dev hub org
--apiversion=apiversion override the api version used for
api requests made by this command
--json format output as json
--loglevel=(trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL) [default: warn] logging level for
this command invocation
EXAMPLES
$ texei:source:layouts:cleanorg
$ texei:source:layouts:cleanorg --targetusername myScratchOrg --targetdevhubusername myDevHub
```

_See code: [src/commands/texei/source/layouts/cleanorg.ts](https://github.com/texei/texei-sfdx-plugin/blob/v0.0.9/src/commands/texei/source/layouts/cleanorg.ts)_

## `sfdx texei:user:update [-v <string>] [-u <string>] [--apiversion <string>] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`

Expand Down Expand Up @@ -198,5 +232,5 @@ EXAMPLES
$ sfdx texei:user:update --values "UserPermissionsKnowledgeUser=true --json"
```

_See code: [src/commands/texei/user/update.ts](https://github.com/texei/texei-sfdx-plugin/blob/v0.0.8/src/commands/texei/user/update.ts)_
_See code: [src/commands/texei/user/update.ts](https://github.com/texei/texei-sfdx-plugin/blob/v0.0.9/src/commands/texei/user/update.ts)_
<!-- commandsstop -->
3 changes: 3 additions & 0 deletions messages/source-layouts-cleanorg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"commandDescription": "delete unused standard layouts from scratch org"
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "texei-sfdx-plugin",
"description": "Texeï's plugin for sfdx",
"version": "0.0.8",
"version": "0.0.9",
"author": "Texeï",
"bugs": "https://github.com/https://github.com/texei/texei-sfdx-plugin/issues",
"dependencies": {
Expand Down
114 changes: 114 additions & 0 deletions src/commands/texei/source/layouts/cleanorg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { flags, SfdxCommand } from '@salesforce/command';
import { Messages, SfdxError } from '@salesforce/core';
import { SaveResult } from "jsforce";
import * as fs from 'fs';
import * as path from 'path';

const util = require('util');

// Initialize Messages with the current plugin directory
Messages.importMessagesDirectory(__dirname);

const defaultLayoutsFolder = 'force-app/main/default/layouts';

// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core,
// or any library that is using the messages framework can also be loaded this way.
const messages = Messages.loadMessages('texei-sfdx-plugin', 'source-layouts-cleanorg');

export default class CleanOrg extends SfdxCommand {

public static description = messages.getMessage('commandDescription');

public static examples = [
`$ texei:source:layouts:cleanorg`,
`$ texei:source:layouts:cleanorg --targetusername myScratchOrg --targetdevhubusername myDevHub`
];

protected static flagsConfig = {
path: flags.string({ char: 'p', required: false, description: `path to layouts` })
};

// Comment this out if your command does not require an org username
protected static requiresUsername = true;

// Comment this out if your command does not require a hub org username
protected static requiresDevhubUsername = true;

// Set this to true if your command requires a project workspace; 'requiresProject' is false by default
protected static requiresProject = true;

public async run(): Promise<any> {

// First check this is a Scratch Org, we don't want to delete Layouts from a real org (maybe add a bypass flag later)
// Remove this first part when this.org.checkScratchOrg() works
const orgId15 = (await this.org.getOrgId()).substring(0, 15);
const scratchOrgResult = await this.hubOrg.getConnection().query(`Select Id FROM ActiveScratchOrg where ScratchOrg = '${orgId15}'`) as any;
if (scratchOrgResult.records.length !== 1) {
throw new SfdxError('This command only works on Scratch Org, you fool!');
}

let deletedLayouts = [];

// Read files in directory
const pathToFile = this.flags.path ? this.flags.path : defaultLayoutsFolder;

const filesPath = path.join(
process.cwd(),
pathToFile
);

// Read files
const readDir = util.promisify(fs.readdir);
let layoutsFiles = await readDir(filesPath, "utf8");
// Don't know why metadata API retrieved & as %26 whereas other characters are ok. Hardcoding for now (booo)
layoutsFiles = layoutsFiles.map(x => x.replace('.layout-meta.xml','').replace('%26','&').replace('%27','\''));

// Only look at standard objects
let standardObjects:Set<String> = new Set<String>(layoutsFiles.map(x => {
const obj = x.split('-')[0];
if (!obj.includes('__')) {
// Should be enough to know if it's a standard object
return obj;
}
}));
standardObjects.delete(undefined);

// Query the org to get layouts for these standard objects
const conn = this.org.getConnection();
const objectList:string = `'${Array.from(standardObjects).join().replace(/,/gi,'\',\'')}'`;
const query = `Select TableEnumOrId, Name from Layout where TableEnumOrId IN (${objectList}) order by TableEnumOrId`;
const results = await conn.tooling.query(query) as any;

let layoutsOnOrg:Set<String> = new Set<String>();
for (const layout of results.records) {
layoutsOnOrg.add(`${layout.TableEnumOrId}-${layout.Name}`);
}

const layoutsToDelete:string[] = Array.from(layoutsOnOrg).filter(lay => layoutsFiles.includes(lay) ? undefined : lay) as string[];

if (layoutsToDelete.length > 0) {

// TODO: log after delete, once errors are handled correctly
this.ux.log(`Deleting layouts:`);
for (const lay of layoutsToDelete) {
this.ux.log(lay);
}

// Use metadata API so that this won't be visible in force:source:status
// This call is limited to 10 records, splitting (maybe refactor later to use destructiveChanges.xml)
let promises: Array<Promise<SaveResult | SaveResult[]>> = new Array<Promise<SaveResult | SaveResult[]>>();

while(layoutsToDelete.length) {
promises.push(conn.metadata.delete('Layout', layoutsToDelete.splice(0,10)));
}

// TODO: handle errors correctly
await Promise.all(promises);
}
else {
this.ux.log(`Nothing to delete.`);
}

return { deleted: deletedLayouts };
}
}

0 comments on commit 191d9c2

Please sign in to comment.