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.
* fix: add convert command * fix: remove SfdxProjectJson dep * fix: rootdir working
- Loading branch information
1 parent
c00e432
commit 26e36b2
Showing
4 changed files
with
261 additions
and
0 deletions.
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
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,16 @@ | ||
{ | ||
"description": "convert source into Metadata API format", | ||
"examples": [ | ||
"$ sfdx force:source:convert -r path/to/source", | ||
"$ sfdx force:source:convert -r path/to/source -d path/to/outputdir -n 'My Package'" | ||
], | ||
"flags": { | ||
"rootdir": "a source directory other than the default package to convert", | ||
"outputdir": "output directory to store the Metadata API–formatted files in", | ||
"packagename": "name of the package to associate with the metadata-formatted files", | ||
"manifest": "file path to manifest (package.xml) of metadata types to convert.", | ||
"sourcepath": "comma-separated list of paths to the local source files to convert", | ||
"metadata": "comma-separated list of metadata component names to convert" | ||
}, | ||
"success": "Source was successfully converted to Metadata API format and written to the location: %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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
/* | ||
* 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 { resolve } from 'path'; | ||
import { flags, FlagsConfig } from '@salesforce/command'; | ||
import { Messages } from '@salesforce/core'; | ||
import { MetadataConverter } from '@salesforce/source-deploy-retrieve'; | ||
import { asArray, asString } from '@salesforce/ts-types'; | ||
import { SourceCommand } from '../../../sourceCommand'; | ||
|
||
Messages.importMessagesDirectory(__dirname); | ||
const messages = Messages.loadMessages('@salesforce/plugin-source', 'convert'); | ||
|
||
type ConvertResult = { | ||
location: string; | ||
}; | ||
|
||
export class Convert extends SourceCommand { | ||
public static readonly description = messages.getMessage('description'); | ||
public static readonly examples = messages.getMessage('examples').split(os.EOL); | ||
public static readonly requiresProject = true; | ||
public static readonly requiresUsername = true; | ||
public static readonly flagsConfig: FlagsConfig = { | ||
rootdir: flags.directory({ | ||
char: 'r', | ||
description: messages.getMessage('flags.rootdir'), | ||
}), | ||
outputdir: flags.directory({ | ||
default: './', | ||
char: 'd', | ||
description: messages.getMessage('flags.outputdir'), | ||
}), | ||
packagename: flags.string({ | ||
char: 'n', | ||
description: messages.getMessage('flags.packagename'), | ||
}), | ||
manifest: flags.string({ | ||
char: 'x', | ||
description: messages.getMessage('flags.manifest'), | ||
}), | ||
sourcepath: flags.array({ | ||
char: 'p', | ||
description: messages.getMessage('flags.sourcepath'), | ||
exclusive: ['manifest', 'metadata'], | ||
}), | ||
metadata: flags.array({ | ||
char: 'm', | ||
description: messages.getMessage('flags.metadata'), | ||
exclusive: ['manifest', 'sourcepath'], | ||
}), | ||
}; | ||
|
||
public async run(): Promise<ConvertResult> { | ||
const paths: string[] = []; | ||
|
||
if (this.flags.sourcepath) { | ||
paths.push(...this.flags.sourcepath); | ||
} | ||
|
||
// rootdir behaves exclusively to sourcepath, metadata, and manifest... to maintain backwards compatibility | ||
// we will check here, instead of adding the exclusive option to the flag definition so we don't break scripts | ||
if (this.flags.rootdir && !this.flags.sourcepath && !this.flags.metadata && !this.flags.manifest) { | ||
// only rootdir option passed | ||
paths.push(this.flags.rootdir); | ||
} | ||
|
||
// no options passed, convert the default package (usually force-app) | ||
if (!this.flags.sourcepath && !this.flags.metadata && !this.flags.manifest && !this.flags.rootdir) { | ||
paths.push(this.project.getDefaultPackage().path); | ||
} | ||
|
||
const cs = await this.createComponentSet({ | ||
sourcepath: paths, | ||
manifest: asString(this.flags.manifest), | ||
metadata: asArray<string>(this.flags.metadata), | ||
}); | ||
|
||
const converter = new MetadataConverter(); | ||
const res = await converter.convert(cs.getSourceComponents().toArray(), 'metadata', { | ||
type: 'directory', | ||
outputDirectory: asString(this.flags.outputdir), | ||
packageName: asString(this.flags.packagename), | ||
}); | ||
|
||
this.ux.log(messages.getMessage('success', [res.packagePath])); | ||
|
||
return { | ||
location: resolve(res.packagePath), | ||
}; | ||
} | ||
} |
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,133 @@ | ||
/* | ||
* 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 { join, resolve } from 'path'; | ||
import { Dictionary } from '@salesforce/ts-types'; | ||
import { DeployResult, MetadataConverter } from '@salesforce/source-deploy-retrieve'; | ||
import * as sinon from 'sinon'; | ||
import { expect } from 'chai'; | ||
import { Convert } from '../../../src/commands/force/source/convert'; | ||
import { FlagOptions } from '../../../src/sourceCommand'; | ||
|
||
describe('force:source:convert', () => { | ||
let createComponentSetStub: sinon.SinonStub; | ||
let deployStub: sinon.SinonStub; | ||
|
||
const defaultDir = join('my', 'default', 'package'); | ||
const myApp = join('new', 'package', 'directory'); | ||
|
||
const sandbox = sinon.createSandbox(); | ||
const packageXml = 'package.xml'; | ||
|
||
const run = async (flags: Dictionary<boolean | string | number | string[]> = {}): Promise<DeployResult> => { | ||
// Run the command | ||
return Convert.prototype.run.call({ | ||
flags: Object.assign({}, flags), | ||
ux: { | ||
log: () => {}, | ||
styledHeader: () => {}, | ||
table: () => {}, | ||
}, | ||
logger: { | ||
debug: () => {}, | ||
}, | ||
project: { | ||
getDefaultPackage: () => { | ||
return { path: defaultDir }; | ||
}, | ||
}, | ||
createComponentSet: createComponentSetStub, | ||
}) as Promise<DeployResult>; | ||
}; | ||
|
||
// Ensure SourceCommand.createComponentSet() args | ||
const ensureCreateComponentSetArgs = (overrides?: Partial<FlagOptions>) => { | ||
const defaultArgs = { | ||
sourcepath: [], | ||
manifest: undefined, | ||
metadata: undefined, | ||
}; | ||
const expectedArgs = { ...defaultArgs, ...overrides }; | ||
|
||
expect(createComponentSetStub.calledOnce).to.equal(true); | ||
expect(createComponentSetStub.firstCall.args[0]).to.deep.equal(expectedArgs); | ||
}; | ||
|
||
beforeEach(() => { | ||
sandbox.stub(MetadataConverter.prototype, 'convert').resolves({ packagePath: 'temp' }); | ||
createComponentSetStub = sandbox.stub().returns({ | ||
deploy: deployStub, | ||
getPackageXml: () => packageXml, | ||
getSourceComponents: () => { | ||
return { | ||
toArray: () => {}, | ||
}; | ||
}, | ||
}); | ||
}); | ||
|
||
afterEach(() => { | ||
sandbox.restore(); | ||
}); | ||
|
||
it('should pass along sourcepath', async () => { | ||
const sourcepath = ['somepath']; | ||
const result = await run({ sourcepath, json: true }); | ||
expect(result).to.deep.equal({ location: resolve('temp') }); | ||
ensureCreateComponentSetArgs({ sourcepath }); | ||
}); | ||
|
||
it('should call default package dir if no args', async () => { | ||
const result = await run({ json: true }); | ||
expect(result).to.deep.equal({ location: resolve('temp') }); | ||
ensureCreateComponentSetArgs({ sourcepath: [defaultDir] }); | ||
}); | ||
|
||
it('should call with metadata', async () => { | ||
const result = await run({ metadata: ['ApexClass'], json: true }); | ||
expect(result).to.deep.equal({ location: resolve('temp') }); | ||
ensureCreateComponentSetArgs({ metadata: ['ApexClass'] }); | ||
}); | ||
|
||
it('should call with package.xml', async () => { | ||
const result = await run({ json: true }); | ||
expect(result).to.deep.equal({ location: resolve('temp') }); | ||
ensureCreateComponentSetArgs({ sourcepath: [defaultDir] }); | ||
}); | ||
|
||
it('should call default package dir if no args', async () => { | ||
const result = await run({ json: true }); | ||
expect(result).to.deep.equal({ location: resolve('temp') }); | ||
ensureCreateComponentSetArgs({ sourcepath: [defaultDir] }); | ||
}); | ||
|
||
it('should call root dir with rootdir flag', async () => { | ||
const result = await run({ rootdir: myApp, json: true }); | ||
expect(result).to.deep.equal({ location: resolve('temp') }); | ||
ensureCreateComponentSetArgs({ sourcepath: [myApp] }); | ||
}); | ||
|
||
describe('rootdir should be overwritten by any other flag', () => { | ||
it('sourcepath', async () => { | ||
const result = await run({ rootdir: myApp, sourcepath: [defaultDir], json: true }); | ||
expect(result).to.deep.equal({ location: resolve('temp') }); | ||
ensureCreateComponentSetArgs({ sourcepath: [defaultDir] }); | ||
}); | ||
|
||
it('metadata', async () => { | ||
const result = await run({ rootdir: myApp, metadata: ['ApexClass', 'CustomObject'], json: true }); | ||
expect(result).to.deep.equal({ location: resolve('temp') }); | ||
ensureCreateComponentSetArgs({ metadata: ['ApexClass', 'CustomObject'] }); | ||
}); | ||
|
||
it('package', async () => { | ||
const result = await run({ rootdir: myApp, manifest: packageXml, json: true }); | ||
expect(result).to.deep.equal({ location: resolve('temp') }); | ||
ensureCreateComponentSetArgs({ manifest: packageXml }); | ||
}); | ||
}); | ||
}); |