generated from salesforcecli/plugin-template
-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: listmetadata and describemetadata
* feat: add the mdapi:listmetadata command * fix: lint fixes and command snapshot * chore: update oclif topics * chore: add unit tests * chore: add NUTs * chore: add NUTs * chore: add a comment to the NUTs * feat: add the mdapi:describemetadata command and tests * fix: use short description
- Loading branch information
Showing
11 changed files
with
765 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule dreamhouse-lwc
deleted from
ae7f21
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"description": "display the metadata types enabled for your org", | ||
"examples": [ | ||
"$ sfdx force:mdapi:describemetadata -a 43.0", | ||
"$ sfdx force:mdapi:describemetadata -u [email protected]", | ||
"$ sfdx force:mdapi:describemetadata -f /path/to/outputfilename.txt", | ||
"$ sfdx force:mdapi:describemetadata -u [email protected] -f /path/to/outputfilename.txt" | ||
], | ||
"flags": { | ||
"apiversion": "API version to use", | ||
"resultfile": "path to the file where results are stored", | ||
"filterknown": "filter metadata known by the CLI" | ||
}, | ||
"flagsLong": { | ||
"apiversion": "The API version to use. The default is the latest API version", | ||
"resultfile": "The path to the file where the results of the command are stored. Directing the output to a file makes it easier to extract relevant information for your package.xml manifest file. The default output destination is the console.", | ||
"filterknown": "Filters all the known metadata from the result such that all that is left are the types not yet fully supported by the CLI." | ||
}, | ||
"invalidResultFile": "Invalid resultfile parameter specified: %s\nMust be a valid file path." | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"description": "display properties of metadata components of a specified type", | ||
"examples": [ | ||
"$ sfdx force:mdapi:listmetadata -m CustomObject", | ||
"$ sfdx force:mdapi:listmetadata -m CustomObject -a 43.0", | ||
"$ sfdx force:mdapi:listmetadata -m CustomObject -u [email protected]", | ||
"$ sfdx force:mdapi:listmetadata -m CustomObject -f /path/to/outputfilename.txt", | ||
"$ sfdx force:mdapi:listmetadata -m Dashboard --folder foldername", | ||
"$ sfdx force:mdapi:listmetadata -m Dashboard --folder foldername -a 43.0", | ||
"$ sfdx force:mdapi:listmetadata -m Dashboard --folder foldername -u [email protected]", | ||
"$ sfdx force:mdapi:listmetadata -m Dashboard --folder foldername -f /path/to/outputfilename.txt", | ||
"$ sfdx force:mdapi:listmetadata -m CustomObject -u [email protected] -f /path/to/outputfilename.txt" | ||
], | ||
"flags": { | ||
"apiversion": "API version to use", | ||
"resultfile": "path to the file where results are stored", | ||
"metadatatype": "metadata type to be retrieved, such as CustomObject; metadata type value is case-sensitive", | ||
"folder": "folder associated with the component; required for components that use folders; folder names are case-sensitive" | ||
}, | ||
"flagsLong": { | ||
"apiversion": "The API version to use. The default is the latest API version", | ||
"resultfile": "The path to the file where the results of the command are stored. The default output destination is the console.", | ||
"metadatatype": "The metadata type to be retrieved, such as CustomObject or Report. The metadata type value is case-sensitive.", | ||
"folder": "The folder associated with the component. This parameter is required for components that use folders, such as Dashboard, Document, EmailTemplate, or Report. The folder name value is case-sensitive." | ||
}, | ||
"invalidResultFile": "Invalid resultfile parameter specified: %s\nMust be a valid file path.", | ||
"noMatchingMetadata": "No metadata found for type: %s in org: %s" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
/* | ||
* Copyright (c) 2020, salesforce.com, inc. | ||
* All rights reserved. | ||
* Licensed under the BSD 3-Clause license. | ||
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause | ||
*/ | ||
|
||
import * as os from 'os'; | ||
import * as path from 'path'; | ||
import * as fs from 'fs'; | ||
import { flags, FlagsConfig } from '@salesforce/command'; | ||
import { Messages, SfdxError } from '@salesforce/core'; | ||
import { DescribeMetadataResult } from 'jsforce'; | ||
import { RegistryAccess } from '@salesforce/source-deploy-retrieve'; | ||
import { SourceCommand } from '../../../sourceCommand'; | ||
|
||
Messages.importMessagesDirectory(__dirname); | ||
const messages = Messages.loadMessages('@salesforce/plugin-source', 'md.describe'); | ||
|
||
interface FsError extends Error { | ||
code: string; | ||
} | ||
|
||
export class DescribeMetadata extends SourceCommand { | ||
public static readonly description = messages.getMessage('description'); | ||
public static readonly examples = messages.getMessage('examples').split(os.EOL); | ||
public static readonly requiresUsername = true; | ||
public static readonly flagsConfig: FlagsConfig = { | ||
apiversion: flags.builtin({ | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore force char override for backward compat | ||
char: 'a', | ||
description: messages.getMessage('flags.apiversion'), | ||
longDescription: messages.getMessage('flagsLong.apiversion'), | ||
}), | ||
resultfile: flags.filepath({ | ||
char: 'f', | ||
description: messages.getMessage('flags.resultfile'), | ||
longDescription: messages.getMessage('flagsLong.resultfile'), | ||
}), | ||
filterknown: flags.boolean({ | ||
char: 'k', | ||
description: messages.getMessage('flags.filterknown'), | ||
longDescription: messages.getMessage('flagsLong.filterknown'), | ||
hidden: true, | ||
}), | ||
}; | ||
|
||
private describeResult: DescribeMetadataResult; | ||
private targetFilePath: string; | ||
|
||
public async run(): Promise<DescribeMetadataResult> { | ||
await this.describe(); | ||
this.resolveSuccess(); | ||
return this.formatResult(); | ||
} | ||
|
||
protected async describe(): Promise<void> { | ||
const apiversion = this.getFlag<string>('apiversion'); | ||
|
||
this.validateResultFile(); | ||
|
||
const connection = this.org.getConnection(); | ||
this.describeResult = await connection.metadata.describe(apiversion); | ||
|
||
if (this.flags.filterknown) { | ||
this.logger.debug('Filtering for only metadata types unregistered in the CLI'); | ||
const registry = new RegistryAccess(); | ||
this.describeResult.metadataObjects = this.describeResult.metadataObjects.filter((md) => { | ||
try { | ||
// An error is thrown when a type can't be found by name, and we want | ||
// the ones that can't be found. | ||
registry.getTypeByName(md.xmlName); | ||
return false; | ||
} catch (e) { | ||
return true; | ||
} | ||
}); | ||
} | ||
} | ||
|
||
// No-op implementation since any describe metadata status would be a success. | ||
// The only time this command would report an error is if it failed | ||
// flag parsing or some error during the request, and those are captured | ||
// by the command framework. | ||
/* eslint-disable-next-line @typescript-eslint/no-empty-function */ | ||
protected resolveSuccess(): void {} | ||
|
||
protected formatResult(): DescribeMetadataResult { | ||
if (this.targetFilePath) { | ||
fs.writeFileSync(this.targetFilePath, JSON.stringify(this.describeResult, null, 2)); | ||
this.ux.log(`Wrote result file to ${this.targetFilePath}.`); | ||
} else if (!this.isJsonOutput()) { | ||
this.ux.styledJSON(this.describeResult); | ||
} | ||
return this.describeResult; | ||
} | ||
|
||
private validateResultFile(): void { | ||
if (this.flags.resultfile) { | ||
this.targetFilePath = path.resolve(this.flags.resultfile); | ||
// Ensure path exists | ||
fs.mkdirSync(path.dirname(this.targetFilePath), { recursive: true }); | ||
try { | ||
const stat = fs.statSync(this.targetFilePath); | ||
if (!stat.isFile()) { | ||
throw SfdxError.create('@salesforce/plugin-source', 'md.describe', 'invalidResultFile', [ | ||
this.targetFilePath, | ||
]); | ||
} | ||
} catch (err: unknown) { | ||
const e = err as FsError; | ||
if (e.code !== 'ENOENT') { | ||
throw err; | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
/* | ||
* Copyright (c) 2020, salesforce.com, inc. | ||
* All rights reserved. | ||
* Licensed under the BSD 3-Clause license. | ||
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause | ||
*/ | ||
|
||
import * as os from 'os'; | ||
import * as path from 'path'; | ||
import * as fs from 'fs'; | ||
import { flags, FlagsConfig } from '@salesforce/command'; | ||
import { Messages, SfdxError } from '@salesforce/core'; | ||
import { Optional } from '@salesforce/ts-types'; | ||
import { FileProperties, ListMetadataQuery } from 'jsforce'; | ||
import { SourceCommand } from '../../../sourceCommand'; | ||
|
||
Messages.importMessagesDirectory(__dirname); | ||
const messages = Messages.loadMessages('@salesforce/plugin-source', 'md.list'); | ||
|
||
export type ListMetadataCommandResult = FileProperties[]; | ||
|
||
interface FsError extends Error { | ||
code: string; | ||
} | ||
|
||
export class ListMetadata extends SourceCommand { | ||
public static readonly description = messages.getMessage('description'); | ||
public static readonly examples = messages.getMessage('examples').split(os.EOL); | ||
public static readonly requiresUsername = true; | ||
public static readonly flagsConfig: FlagsConfig = { | ||
apiversion: flags.builtin({ | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore force char override for backward compat | ||
char: 'a', | ||
description: messages.getMessage('flags.apiversion'), | ||
longDescription: messages.getMessage('flagsLong.apiversion'), | ||
}), | ||
resultfile: flags.filepath({ | ||
char: 'f', | ||
description: messages.getMessage('flags.resultfile'), | ||
longDescription: messages.getMessage('flagsLong.resultfile'), | ||
}), | ||
metadatatype: flags.string({ | ||
char: 'm', | ||
description: messages.getMessage('flags.metadatatype'), | ||
longDescription: messages.getMessage('flagsLong.metadatatype'), | ||
required: true, | ||
}), | ||
folder: flags.string({ | ||
description: messages.getMessage('flags.folder'), | ||
longDescription: messages.getMessage('flagsLong.folder'), | ||
}), | ||
}; | ||
|
||
private listResult: Optional<FileProperties[]>; | ||
private targetFilePath: string; | ||
|
||
public async run(): Promise<ListMetadataCommandResult> { | ||
await this.list(); | ||
this.resolveSuccess(); | ||
return this.formatResult(); | ||
} | ||
|
||
protected async list(): Promise<void> { | ||
const apiversion = this.getFlag<string>('apiversion'); | ||
const type = this.getFlag<string>('metadatatype'); | ||
const folder = this.getFlag<string>('folder'); | ||
|
||
this.validateResultFile(); | ||
|
||
const query: ListMetadataQuery = { type, folder }; | ||
const connection = this.org.getConnection(); | ||
const result = (await connection.metadata.list(query, apiversion)) || []; | ||
this.listResult = Array.isArray(result) ? result : [result]; | ||
} | ||
|
||
// No-op implementation since any list metadata status would be a success. | ||
// The only time this command would report an error is if it failed | ||
// flag parsing or some error during the request, and those are captured | ||
// by the command framework. | ||
/* eslint-disable-next-line @typescript-eslint/no-empty-function */ | ||
protected resolveSuccess(): void {} | ||
|
||
protected formatResult(): ListMetadataCommandResult { | ||
if (this.targetFilePath) { | ||
fs.writeFileSync(this.targetFilePath, JSON.stringify(this.listResult, null, 2)); | ||
this.ux.log(`Wrote result file to ${this.targetFilePath}.`); | ||
} else if (!this.isJsonOutput()) { | ||
if (this.listResult.length) { | ||
this.ux.styledJSON(this.listResult); | ||
} else { | ||
this.ux.log(messages.getMessage('noMatchingMetadata', [this.flags.metadatatype, this.org.getUsername()])); | ||
} | ||
} | ||
return this.listResult; | ||
} | ||
|
||
private validateResultFile(): void { | ||
if (this.flags.resultfile) { | ||
this.targetFilePath = path.resolve(this.flags.resultfile); | ||
// Ensure path exists | ||
fs.mkdirSync(path.dirname(this.targetFilePath), { recursive: true }); | ||
try { | ||
const stat = fs.statSync(this.targetFilePath); | ||
if (!stat.isFile()) { | ||
throw SfdxError.create('@salesforce/plugin-source', 'md.list', 'invalidResultFile', [this.targetFilePath]); | ||
} | ||
} catch (err: unknown) { | ||
const e = err as FsError; | ||
if (e.code !== 'ENOENT') { | ||
throw err; | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.