Skip to content

Commit

Permalink
feat: add sfdx force:source:read command
Browse files Browse the repository at this point in the history
  • Loading branch information
amtrack committed Jan 20, 2022
1 parent 413490a commit 0493b78
Show file tree
Hide file tree
Showing 11 changed files with 8,472 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
root = true

[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false
24 changes: 24 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Release

on:
push:
branches:
- main

jobs:
default:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-node@v2
with:
node-version: 16
- name: Install dependencies
run: yarn install
- name: Release package
run: npx semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*-debug.log
*-error.log
/.nyc_output
/dist
/lib
/package-lock.json
/tmp
node_modules
/.sfdx/
/oclif.manifest.json
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# sfdx-plugin-source-read

> sfdx plugin to read Metadata via CRUD Metadata API
## Installation

```console
sfdx plugins:install sfdx-plugin-source-read
```

## Usage

```console
sfdx force:source:read -m "Profile:Admin"
sfdx force:source:read -p force-app/main/default/profiles/Admin.profile-meta.xml
sfdx force:source:read -m "RecordType:Account.Business"
sfdx force:source:read -p force-app/main/default/objects/Account/recordTypes/Business.recordType-meta.xml
```

## Disclaimer

Currently this has been tested only for `Profiles` and `RecordTypes`.
4 changes: 4 additions & 0 deletions bin/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env node

require('@oclif/command').run()
.catch(require('@oclif/errors/handle'))
3 changes: 3 additions & 0 deletions bin/run.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@echo off

node "%~dp0\run" %*
49 changes: 49 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "sfdx-plugin-source-read",
"description": "sfdx plugin to read Metadata via CRUD Metadata API",
"version": "0.0.0-development",
"author": "Matthias Rolke @amtrack",
"bugs": "https://github.com/amtrack/sfdx-plugin-source-read/issues",
"bin": {
"sfdx-plugin-source-read": "bin/run"
},
"dependencies": {
"@salesforce/command": "4.2.1",
"tslib": "2.3.1"
},
"devDependencies": {
"@oclif/dev-cli": "1.26.10",
"@oclif/plugin-help": "5.1.10",
"@salesforce/dev-config": "3.0.0",
"sfdx-cli": "7.133.0",
"ts-node": "10.4.0",
"typescript": "4.5.4"
},
"engines": {
"node": ">=8.0.0"
},
"files": [
"/bin",
"/lib",
"/oclif.manifest.json",
"/yarn.lock"
],
"homepage": "https://github.com/amtrack/sfdx-plugin-source-read",
"keywords": [
"sfdx-plugin"
],
"license": "MIT",
"oclif": {
"commands": "./lib/commands",
"bin": "sfdx",
"devPlugins": [
"@oclif/plugin-help"
]
},
"repository": "amtrack/sfdx-plugin-source-read",
"scripts": {
"build": "rm -rf lib && tsc -b && oclif-dev manifest",
"prepack": "yarn build",
"prepare": "yarn build"
}
}
3 changes: 3 additions & 0 deletions release.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
branches: ["main"]
};
104 changes: 104 additions & 0 deletions src/commands/force/source/read.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { flags, SfdxCommand } from "@salesforce/command";
import { ComponentSetBuilder } from "@salesforce/plugin-source/lib/componentSetBuilder";
import { AnyJson } from "@salesforce/ts-types";
import { writeFileSync } from "fs";
import { Builder } from "xml2js";

export default class Org extends SfdxCommand {
public static description = "Read Metadata using the CRUD Metadata API";

public static examples = [
`$ sfdx force:source:read -m "Profile:Admin"`,
`$ sfdx force:source:read -m "RecordType:Account.Business"`,
`$ sfdx force:source:read -p force-app/main/default/objects/Account/recordTypes/Business.recordType-meta.xml`
];

protected static flagsConfig = {
metadata: flags.string({
char: "m",
description: `comma-separated list of metadata component names
Example: 'RecordType:Account.Business,Profile:Admin'`
}),
sourcepath: flags.string({
char: "p",
description: `comma-separated list of source file paths to retrieve
Example: 'force-app/main/default/objects/Account/recordTypes/Business.recordType-meta.xml,force-app/main/default/profiles/Admin.profile-meta.xml'`
})
};

protected static requiresUsername = true;
protected static requiresProject = true;

public async run(): Promise<AnyJson> {
const conn = this.org.getConnection();
const sourcePaths = (await this.project.getPackageDirectories()).map(
dir => dir.path
);
const componentSet = await ComponentSetBuilder.build({
sourcepath:
this.flags.sourcepath &&
parseCommaSeparatedValues(this.flags.sourcepath),
manifest: this.flags.manifest && {
manifestPath: this.flags.manifest,
directoryPaths: sourcePaths
},
metadata: this.flags.metadata && {
metadataEntries: parseCommaSeparatedValues(this.flags.metadata),
directoryPaths: sourcePaths
}
});
for (const component of componentSet) {
this.log(
"reading",
`${component.type.name}:${component.fullName}`,
"..."
);
const mdJson = await conn.metadata.read(component.type.name, [
component.fullName
]);
writeFileSync(component["xml"], convertToXml(component, mdJson));
}

return;
}
}

function parseCommaSeparatedValues(commaSeparatedMetadataComponentNames) {
if (!commaSeparatedMetadataComponentNames) {
return [];
}
return commaSeparatedMetadataComponentNames
.split(",")
.map(x => x.trim())
.filter(Boolean);
}

function convertToXml(component, data) {
if (["CustomObject", "Workflow"].includes(component.parent?.type?.name)) {
// remove first part of fullName separated by dot
data.fullName = component.fullName.split(".")[1];
} else {
delete data.fullName;
}
return (
new Builder({
xmldec: {
version: "1.0",
encoding: "UTF-8"
},
rootName: component.type.name,
renderOpts: {
pretty: true,
indent: " ", // 4 spaces
newline: "\n"
}
}).buildObject({
...data,
...{
$: {
xmlns: "http://soap.sforce.com/2006/04/metadata"
}
}
}) + "\n"
);
}
12 changes: 12 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "./node_modules/@salesforce/dev-config/tsconfig",
"compilerOptions": {
"declaration": true,
"outDir": "./lib",
"importHelpers": true,
"rootDir": "./src"
},
"include": [
"./src/**/*"
]
}
Loading

0 comments on commit 0493b78

Please sign in to comment.