One record is created per installation. For example, if 5 orgs installed 2 packages, 10 records are created.
Supported Calls
describeSObjects(), query(), retrieve()
Special Access Rules
To initiate a push upgrade for a first-generation managed package, the Upload AppExchange Packages user permission is required.
To initiate a push upgrade for an unlocked or second-generation managed package, the Create and Update Second-Generation Packages user permission is required.
The push upgrade feature is only available to first- and second-generation managed packages that have passed AppExchange security review. To enable push upgrades for your managed package, log a support case in the Salesforce Partner Community.
For unlocked packages, push upgrades are enabled by default.
Fields
| Field Name | Details |
|---|---|
| CustomUpgradeType |
|
| HasRestrictionEnabled |
|
| InstalledStatus |
|
| InstanceName |
|
| IsCustomUpgradeAllowed |
|
| MetadataPackageId |
|
| MetadataPackageVersionId |
|
| OrgKey |
|
| OrgName |
|
| OrgStatus |
|
| OrgType |
|
| ParentOrg |
|
Usage
| Query | String |
|---|---|
| Get all package subscriber orgs with a specific package ID | SELECT Id, OrgKey, OrgStatus, OrgName, OrgType FROM PackageSubscriber WHERE MetadataPackageVersionId = '04t...' |
| Get all package subscriber orgs that have an installed package created by the org you’re logged in to | SELECT Id, OrgKey, OrgStatus, OrgName, OrgType FROM PackageSubscriber WHERE InstalledStatus = 'i' |
Filter PackageSubscriber Objects by Instance
If you have packages with many subscribers, querying PackageSubscriber objects can take a while. To improve query performance, add filters to your PackageSubscriber queries, such as an InstanceName filter. InstanceName is a field that represents the instance that the subscriber org is hosted on.
- Get the org’s package and the latest released version of the
package.
/** * Get the MetadataPackage object corresponding to this org's managed package */ public MetadataPackage getMetadataPackage() throws ConnectionException { // retrieve the managed package, which won’t have an empty namespace QueryResult result = conn.query("select id from MetadataPackage where namespaceprefix <> ''"); return (MetadataPackage) result.getRecords()[0]; } /** * Get the latest MetadataPackageVersion object of the given MetadataPackage */ public MetadataPackageVersion getLatestMetadataPackageVersion(MetadataPackage metadataPackage) throws ConnectionException { // get the latest released version of the given package String query = "Select id, ReleaseState, MajorVersion, MinorVersion, PatchVersion, MetadataPackageId" + " From MetadataPackageVersion" + " Where MetadataPackageId = '%s' and ReleaseState = 'Released'" + " Order by majorversion desc, minorversion desc, patchversion desc"; QueryResult result = conn.query(String.format(query, metadataPackage.getId())); return (MetadataPackageVersion) result.getRecords()[0]; }
- Get eligible subscribers. The following query strings and methods are modified
to allow querying for PackageSubscribers filtered by an
instance.
static final String PACKAGE_SUBSCRIBER_ORG_KEY_QUERY = "Select OrgKey from PackageSubscribers where OrgStatus = 'Active'" + " and InstalledStatus = 'I'" + " and InstanceName = '%s'"; // placeholder for instance values static final String METADATA_PACKAGE_VERSION_QUERY = "Select Id, Name, ReleaseState, (%s) from MetadataPackageVersion" + " where MetadataPackageId = '%s' AND ReleaseState = 'Released'" + " AND (MajorVersion < %s OR (MajorVersion = %s and MinorVersion < %s)" + " OR (MajorVersion = %s and MinorVersion = %s and PatchVersion < %s))"; /** * Get all PackageSubscribers on the given instance that are eligible to upgrade to the given * MetadataPackageVersion */ public PackageSubscriber[] getEligibleSubscriberIds(MetadataPackageVersion version, String instanceName) throws ConnectionException { String allPackageId = version.getMetadataPackageId(); Integer major = version.getMajorVersion(); Integer minor = version.getMinorVersion(); Integer patch = version.getPatchVersion(); return getEligibleSubscriberIds(major, minor, patch, allPackageId, instanceName); }
public PackageSubscriber[] getEligibleSubscriberIds(Integer major, Integer minor, Integer patch, String packageId, String instanceName) throws ConnectionException { String subscriberQuery = String.format(PACKAGE_SUBSCRIBER_ORG_KEY_QUERY, instanceName); QueryResult results = conn.query(String.format(METADATA_PACKAGE_VERSION_QUERY, subscriberQuery, packageId, major, major, minor, major, minor, patch)); return Arrays.stream(results.getRecords()).map(MetadataPackageVersion.class::cast) .filter(mpv -> mpv.getPackageSubscribers() != null) .flatMap(mpv -> Arrays.stream(mpv.getPackageSubscribers().getRecords())) .map(PackageSubscriber.class::cast) .toArray(PackageSubscriber[]::new); }
- Put it all together. The following code sample shows how to use the previous
methods to modify the workflow to perform package pushes by
instance.
String[] instances = { "NA4" }; // Here we list the instances we would like to push to MetadataPackage metadataPackage = api.getMetadataPackage(); MetadataPackageVersion version = api.getLatestMetadataPackageVersion(metadataPackage); // do pushes by instance to avoid API timeouts retrieving PackageSubscribers for (String instanceName : instances) { PackageSubscriber[] eligibleSubscribers = api.getEligibleSubscriberIds(version, instanceName); // ... proceed with creating PushRequests and PushJobs as before