Skip to content
Closed
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
4 changes: 4 additions & 0 deletions messages/package_version_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ No subscriber package was found for seed id: %s
Only one package in a Dev Hub is allowed per converted from first-generation package, but the following were found:
%s

# convertPackagingNotEnabledOnOrg

Can't convert package. The org you specified doesn't have the required second-generation packaging permission enabled. Enable this permission on your Dev Hub org, and try again.

# errorMissingPackageIdOrPath

You must specify either a package ID or a package path to create a new package version.
Expand Down
35 changes: 34 additions & 1 deletion src/package/packageConvert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,15 @@ export async function findOrCreatePackage2(
project?: SfProject
): Promise<string> {
const query = `SELECT Id, Name FROM Package2 WHERE ConvertedFromPackageId = '${seedPackage}'`;
const queryResult = (await connection.tooling.query<PackagingSObjects.Package2>(query)).records;
let queryResult;
try {
queryResult = (await connection.tooling.query<PackagingSObjects.Package2>(query)).records;
} catch (e) {
if (isPackage2NotSupportedError(e)) {
throw messages.createError('convertPackagingNotEnabledOnOrg');
}
throw e;
}
if (queryResult?.length > 1) {
const ids = queryResult.map((r) => r.Id);
throw messages.createError('errorMoreThanOnePackage2WithSeed', [ids.join(', ')]);
Expand Down Expand Up @@ -100,6 +108,9 @@ export async function findOrCreatePackage2(

const createResult = await connection.tooling.create('Package2', request);
if (!createResult.success) {
if (createResult.errors?.some((error) => isPackage2NotSupportedError(error))) {
throw messages.createError('convertPackagingNotEnabledOnOrg');
}
throw pkgUtils.combineSaveErrors('Package2', 'create', createResult.errors);
}

Expand Down Expand Up @@ -475,3 +486,25 @@ const isStatusEqualTo = (
results: PackageVersionCreateRequestResult[],
statuses: Package2VersionStatus[] = []
): boolean => (!results?.length ? false : statuses.some((status) => results[0].Status === status));

/**
* Detects the tooling-API error thrown when a Dev Hub does not have second-generation
* packaging enabled and the Package2 entity is therefore not accessible. The full server
* message may append custom-object WSDL boilerplate, so match on a substring (consistent
* with the handling in packageVersionRetrieve.ts).
*
* @param err the error thrown by a Package2 tooling call
* @returns true if the error indicates Package2 is not supported on the org
*/
const isPackage2NotSupportedError = (err: unknown): boolean => {
let msg: string;
if (err instanceof Error) {
msg = err.message;
} else if (typeof err === 'object' && err !== null && 'message' in err) {
// jsforce SaveError objects are plain objects that carry a `message` field.
msg = String(err.message);
} else {
msg = String(err);
}
return msg.includes("sObject type 'Package2' is not supported.");
};
81 changes: 81 additions & 0 deletions test/package/packageConvert.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,87 @@ describe('packageConvert', () => {
expect((e as Error).message).to.include('No subscriber package was found for seed id: 0Ho3i000000Gmj6CAC');
}
});

it('will throw an actionable error when the Dev Hub does not have 2GP enabled', async () => {
const notSupported = new Error(
"sObject type 'Package2' is not supported. If you are attempting to use a custom object, be sure to append the '__c' after the entity name. Please reference your WSDL or the describe call for the appropriate names."
);
notSupported.name = 'INVALID_TYPE';
const conn = {
tooling: {
query: () => {
throw notSupported;
},
},
} as unknown as Connection;

try {
await findOrCreatePackage2('0Ho3i000000Gmj6CAC', conn);
expect.fail('should have thrown');
} catch (e) {
expect((e as Error).message).to.equal(
"Can't convert package. The org you specified doesn't have the required second-generation packaging permission enabled. Enable this permission on your Dev Hub org, and try again."
);
// the raw INVALID_TYPE text must not leak to the user
expect((e as Error).message).to.not.include('is not supported');
expect((e as Error).name).to.not.equal('INVALID_TYPE');
}
});

it('will throw an actionable error when the Package2 create reports 2GP not supported', async () => {
const conn = await testOrg.getConnection();

$$.SANDBOX.stub(conn.tooling, 'query')
.onFirstCall()
// @ts-ignore
.resolves({ records: [] })
.onSecondCall()
// @ts-ignore
.resolves({ records: [{ Name: 'pkg', Description: 'desc', NamespacePrefix: 'ns' }] });
$$.SANDBOX.stub(conn.tooling, 'create').resolves({
errors: [{ errorCode: 'INVALID_TYPE', message: "sObject type 'Package2' is not supported." }],
success: false,
id: undefined,
});

try {
await findOrCreatePackage2('0Ho3i000000Gmj6CAC', conn);
expect.fail('should have thrown');
} catch (e) {
expect((e as Error).message).to.equal(
"Can't convert package. The org you specified doesn't have the required second-generation packaging permission enabled. Enable this permission on your Dev Hub org, and try again."
);
expect((e as Error).message).to.not.include('is not supported');
}
});
});

it('convertPackage surfaces the actionable 2GP-not-enabled error', async () => {
const notSupported = new Error("sObject type 'Package2' is not supported.");
notSupported.name = 'INVALID_TYPE';
const conn = {
tooling: {
query: () => {
throw notSupported;
},
},
} as unknown as Connection;

try {
await convertPackage('0Ho3i000000Gmj6CAC', conn, {
buildInstance: '',
installationKey: '',
definitionfile: '',
installationKeyBypass: true,
wait: Duration.minutes(1),
});
expect.fail('should have thrown');
} catch (e) {
expect((e as Error).message).to.equal(
"Can't convert package. The org you specified doesn't have the required second-generation packaging permission enabled. Enable this permission on your Dev Hub org, and try again."
);
expect((e as Error).message).to.not.include('is not supported');
}
});
it('will throw correct error when create call fails', async () => {
const conn = await testOrg.getConnection();
Expand Down
Loading