Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use sf plugin style and update dependencies #70

Merged
merged 1 commit into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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