Skip to content

Commit

Permalink
Merge pull request #70 from amtrack/feat/sf
Browse files Browse the repository at this point in the history
feat: use sf plugin style and update dependencies
  • Loading branch information
amtrack authored Oct 24, 2023
2 parents 5d24ced + 1169752 commit 8af10b4
Show file tree
Hide file tree
Showing 12 changed files with 1,786 additions and 1,947 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,20 @@ jobs:
- name: Install dependencies
run: |
yarn install
yarn global add sfdx-cli
yarn global add @salesforce/cli
- name: Run tests
run: yarn run test
- name: Run E2E tests
env:
SFDX_AUTH_URL_DEVHUB: ${{ secrets.SFDX_AUTH_URL_DEVHUB }}
run: |
sfdx auth:sfdxurl:store -d -a devhub -f /dev/stdin <<< "${SFDX_AUTH_URL_DEVHUB}"
sfdx force:org:create -f config/project-scratch-def.json -s
sf org login sfdx-url -d -a devhub -f /dev/stdin <<< "${SFDX_AUTH_URL_DEVHUB}"
sf org create scratch -f config/project-scratch-def.json -d
yarn run test:e2e
- name: Delete scratch org
if: always()
run: |
sfdx force:org:delete -p
sf org delete scratch -p
- name: Release package
run: npx semantic-release
env:
Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
# sfdx-plugin-source-read

> sfdx plugin to read Metadata via CRUD Metadata API
> sfdx/sf plugin to read Metadata via CRUD Metadata API
For certain Metadata Types there is a different behaviour of the [file-based](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_retrieve.htm) vs. [CRUD-based](https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_readMetadata.htm) Metadata API.

This sfdx plugin provides the `sfdx force:source:read` command to read Metadata using the "CRUD-based" Metadata API
similar to `sfdx force:source:retrieve` (which uses the "file-based" Metadata API).
This plugin provides the `sf force source read` command to read Metadata using the "CRUD-based" Metadata API
similar to `sf force source retrieve` (which uses the "file-based" Metadata API).

## Example

- When retrieving a `Profile` using `sfdx force:source:retrieve` it only contains `<userPermissions>`.
- When retrieving a `Profile` using `sfdx force:source:read` it contains all fields like `<fieldPermissions>`, `<tabVisibilities>` and more.
- When retrieving a `Profile` using `sf force source retrieve` it only contains `<userPermissions>`.
- When retrieving a `Profile` using `sf force source read` it contains all fields like `<fieldPermissions>`, `<tabVisibilities>` and more.

## Installation

```console
sfdx plugins:install sfdx-plugin-source-read
sf 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
sf force source read -m "Profile:Admin"
sf force source read -p force-app/main/default/profiles/Admin.profile-meta.xml
sf force source read -m "RecordType:Account.Business"
sf force source read -p force-app/main/default/objects/Account/recordTypes/Business.recordType-meta.xml
```

## Disclaimer
Expand Down
24 changes: 13 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@
"sfdx-plugin-source-read": "bin/run"
},
"dependencies": {
"@salesforce/command": "5.2.16",
"@salesforce/source-deploy-retrieve": "7.4.0",
"tslib": "2.4.0"
"@salesforce/sf-plugins-core": "4.0.0",
"@salesforce/source-deploy-retrieve": "9.7.28"
},
"devDependencies": {
"@oclif/test": "2.3.20",
"@types/mocha": "10.0.1",
"chai": "4.3.7",
"@salesforce/dev-config": "4.0.1",
"@salesforce/prettier-config": "0.0.3",
"@types/mocha": "10.0.3",
"chai": "4.3.10",
"execa": "5.1.1",
"mocha": "10.2.0",
"nyc": "15.1.0",
"oclif": "3.9.0",
"oclif": "4.0.3",
"prettier": "3.0.3",
"ts-node": "10.9.1",
"typescript": "5.0.4"
"typescript": "5.2.2"
},
"engines": {
"node": ">=14.16"
Expand All @@ -38,7 +39,8 @@
"license": "MIT",
"oclif": {
"commands": "./lib/commands",
"bin": "sfdx",
"bin": "sf",
"topicSeparator": " ",
"additionalHelpFlags": [
"-h"
]
Expand All @@ -48,7 +50,7 @@
"build": "rm -rf lib && tsc -p . && oclif manifest",
"prepack": "yarn build",
"prepare": "yarn build",
"test": "nyc --reporter=lcov --reporter=text mocha --require ts-node/register \"test/**/*.test.ts\"",
"test:e2e": "mocha --require ts-node/register --timeout 60000 \"test/**/*.e2e.ts\""
"test": "tsc -p test && nyc --reporter=lcov --reporter=text mocha --require ts-node/register \"test/**/*.test.ts\"",
"test:e2e": "tsc -p test && mocha --require ts-node/register --timeout 60000 \"test/**/*.e2e.ts\""
}
}
6 changes: 6 additions & 0 deletions prettier.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const salesforcePrettierConfig = require("@salesforce/prettier-config");

module.exports = {
...salesforcePrettierConfig,
singleQuote: false,
};
2 changes: 1 addition & 1 deletion sfdx-project.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
],
"namespace": "",
"sfdcLoginUrl": "https://login.salesforce.com",
"sourceApiVersion": "56.0"
"sourceApiVersion": "59.0"
}
73 changes: 32 additions & 41 deletions src/commands/force/source/read.ts
Original file line number Diff line number Diff line change
@@ -1,75 +1,66 @@
import { flags, SfdxCommand } from "@salesforce/command";
import {
ComponentSetBuilder,
SourceComponent,
} from "@salesforce/source-deploy-retrieve";
import { Flags, SfCommand, requiredOrgFlagWithDeprecations } from "@salesforce/sf-plugins-core";
import { ComponentSetBuilder, SourceComponent } from "@salesforce/source-deploy-retrieve";
import { filePathsFromMetadataComponent } from "@salesforce/source-deploy-retrieve/lib/src/utils/filePathGenerator";
import { mkdir, writeFile } from "fs/promises";
import type { MetadataType } from "jsforce/api/metadata";
import { dirname, join } from "path";
import { convertToXml, parseCommaSeparatedValues } from "../../../utils";

export class SourceReadCommand extends SfdxCommand {
public static description = "Read Metadata using the CRUD Metadata API";
export class SourceReadCommand extends SfCommand<any> {
public static readonly summary = "Read Metadata using the CRUD Metadata API";
public static readonly description = "Read Metadata using the CRUD Metadata API";

public static examples = [
`$ sfdx <%= command.id %> -m "Profile:Admin"`,
`$ sfdx <%= command.id %> -m "RecordType:Account.Business"`,
`$ sfdx <%= command.id %> -p force-app/main/default/objects/Account/recordTypes/Business.recordType-meta.xml`,
public static readonly examples = [
`$ <%= config.bin %> <%= command.id %> -m "Profile:Admin"`,
`$ <%= config.bin %> <%= command.id %> -m "RecordType:Account.Business"`,
`$ <%= config.bin %> <%= command.id %> -p force-app/main/default/objects/Account/recordTypes/Business.recordType-meta.xml`,
];

protected static flagsConfig = {
metadata: flags.string({
public static readonly flags = {
"target-org": requiredOrgFlagWithDeprecations,
metadata: Flags.string({
char: "m",
description: `comma-separated list of metadata component names
summary: `comma-separated list of metadata component names
Example: 'RecordType:Account.Business,Profile:Admin'`,
}),
sourcepath: flags.string({
sourcepath: Flags.string({
char: "p",
description: `comma-separated list of source file paths to retrieve
summary: `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 static readonly requiresProject = true;

public async run(): Promise<any> {
const conn = this.org.getConnection();
const { flags } = await this.parse(SourceReadCommand);
const conn = flags["target-org"].getConnection();
const packageDirectories = this.project.getPackageDirectories();
const defaultPackageDirectory = this.project.getDefaultPackage().path;
const sourcePaths = packageDirectories.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),
sourcepath: flags.sourcepath && parseCommaSeparatedValues(flags.sourcepath),
manifest: flags.manifest && {
manifestPath: flags.manifest,
directoryPaths: sourcePaths,
},
...(flags.metadata && {
metadata: {
metadataEntries: parseCommaSeparatedValues(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 as MetadataType,
component.fullName
);
this.log("reading", `${component.type.name}:${component.fullName}`, "...");
const mdJson = await conn.metadata.read(component.type.name as MetadataType, component.fullName);
let filePath;
if (component instanceof SourceComponent) {
filePath = component.xml;
} else {
filePath = filePathsFromMetadataComponent(
component,
join(defaultPackageDirectory, "main", "default")
).find((p) => p.endsWith(`.${component.type.suffix}-meta.xml`));
filePath = filePathsFromMetadataComponent(component, join(defaultPackageDirectory, "main", "default")).find(
(p) => p.endsWith(`.${component.type.suffix}-meta.xml`),
);
await mkdir(dirname(filePath), { recursive: true });
}
await writeFile(filePath, convertToXml(component, mdJson));
Expand Down
4 changes: 1 addition & 3 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Builder } from "xml2js";

export function parseCommaSeparatedValues(
commaSeparatedMetadataComponentNames
) {
export function parseCommaSeparatedValues(commaSeparatedMetadataComponentNames) {
if (!commaSeparatedMetadataComponentNames) {
return [];
}
Expand Down
19 changes: 4 additions & 15 deletions test/commands/force/source/read.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,14 @@ import { expect } from "chai";
import { readFile } from "node:fs/promises";
import { join } from "node:path";

describe("E2E:force:source:read", () => {
describe("E2E:force source read", () => {
it("reads the Admin Profile", async () => {
const result = await run(`force:source:read -m Profile:Admin`);
const result = await run(`force source read -m Profile:Admin`);
expect(result.stdout).to.contain("reading Profile:Admin");
const profile = await readFile(
join(
"force-app",
"main",
"default",
"profiles",
"Admin.profile-meta.xml"
),
"utf8"
);
const profile = await readFile(join("force-app", "main", "default", "profiles", "Admin.profile-meta.xml"), "utf8");
const lines = profile.split("\n");
expect(lines[0]).to.equal(`<?xml version="1.0" encoding="UTF-8"?>`);
expect(lines[1]).to.equal(
`<Profile xmlns="http://soap.sforce.com/2006/04/metadata">`
);
expect(lines[1]).to.equal(`<Profile xmlns="http://soap.sforce.com/2006/04/metadata">`);
expect(lines[2]).to.equal(` <applicationVisibilities>`);
expect(lines.length).to.be.greaterThan(100);
});
Expand Down
9 changes: 9 additions & 0 deletions test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "@salesforce/dev-config/tsconfig-test",
"include": ["./**/*.ts"],
"compilerOptions": {
"skipLibCheck": true,
"strictNullChecks": true,
"esModuleInterop": true
}
}
11 changes: 2 additions & 9 deletions test/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@ import { convertToXml, parseCommaSeparatedValues } from "../src/utils";
describe("utils", () => {
describe("parseCommaSeparatedValues", () => {
it("should parse", () => {
expect(parseCommaSeparatedValues("foo-bar,baz,bazn")).to.deep.equal([
"foo-bar",
"baz",
"bazn",
]);
expect(parseCommaSeparatedValues("foo-bar,baz,bazn")).to.deep.equal(["foo-bar", "baz", "bazn"]);
});
});
describe("convertToXml", () => {
Expand All @@ -31,10 +27,7 @@ describe("utils", () => {
},
],
};
const mdXml = readFileSync(
join("test", "fixtures", "Admin.profile-meta.xml"),
"utf8"
);
const mdXml = readFileSync(join("test", "fixtures", "Admin.profile-meta.xml"), "utf8");
expect(convertToXml(component, data)).to.deep.equal(mdXml);
});
});
Expand Down
17 changes: 7 additions & 10 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
{
"extends": "@salesforce/dev-config/tsconfig",
"include": ["./src/**/*.ts"],
"compilerOptions": {
"outDir": "lib",
"module": "commonjs",
"target": "es2020",
"lib": ["es2020"],
"sourceMap": true,
"declaration": true,
"moduleResolution": "node",
"rootDir": "src",
"skipLibCheck": true,
"strictNullChecks": true,
"esModuleInterop": true,
"alwaysStrict": true,
"noUnusedLocals": true
},
"include": ["./src/**/*.ts"]
"declaration": false,
}
}
Loading

0 comments on commit 8af10b4

Please sign in to comment.