22

For a continuous integration setup, I only want to install/upgrade some packages (through the new InstalledPackage mechanism) when necessary. I've looked at the Metadata WSDL (and documentation) and Tooling WSDL and while there is a PackageVersion element, I do not see any API that returns a list of the installed packages.

I may have missed the appropriate operation or perhaps there is some other mechanism that could be used. My base requirement is to be able to obtain the information in some Java code (that is wrapped in an Ant task and used in the decision logic of the continuous integration build).

7 Answers 7

16

There are a couple of routes into listing packages giving both the namespace and versions of those installed in your org via the Metadata API. In summary you have two routes, that both result in the same information...

  • listMetadata call followed by retrieve call. The first is to use the 'listMetadata' operation using the InstalledPackage component type, which is a good way of listing whats in your org (this will give the namespace but not the version). You will then to explicitly request further details on the components via a 'retrieve' (explicitly listing in the package.xml those components you want). The resulting .installedPackage file contains the version info you need.
  • retrieve call (with wildcards). The second option is to utilise the InstalledPackage component type in a package.xml with a wildcard *. This is the most direct route and only requires one call to the Metadata API. The resulting .installedPackage file contains namespace (in its filename) and the version info you also need.

Note: If your interested in further information on install and uninstall via Ant you might find this useful.

The following should give you all you need to decide your approach either in Java or Ant.

Take for example this view of installed packages in an org...

enter image description here

'listMetadata' call via Java.

Here is a slightly modified Java example from the documentation.

public void listMetadata() {
  try {
    ListMetadataQuery query = new ListMetadataQuery();
    query.setType("InstalledPackage");
    //query.setFolder(null);
    double asOfVersion = 28.0;
    // Assuming that the SOAP binding has already been established.
    FileProperties[] lmr = metadataConnection.listMetadata(
        new ListMetadataQuery[] {query}, asOfVersion);
    if (lmr != null) {
      for (FileProperties n : lmr) {
        System.out.println("Component fullName: " + n.getFullName());
        System.out.println("Component type: " + n.getType());
      }
    }            
  } catch (ConnectionException ce) {
    ce.printStackTrace();
  }
}

'listMetadata' call via the Salesforce Ant Migration Tool.

In this toolkit is a listMetadata Ant task that wraps the above operation to list Metadata components.

 <sf:listMetadata 
    username="${sf.username}"
    password="${sf.password}"
    metadataType="InstalledPackage"/>

Gives this output...

[sf:listMetadata] ************************************************************

[sf:listMetadata] FileName: installedPackages/packagea.installedPackage

[sf:listMetadata] FullName/Id: packagea/0A3G0000000UPPRKA4

[sf:listMetadata] Manageable State: null

[sf:listMetadata] Namespace Prefix: packagea

[sf:listMetadata] Created By (Name/Id): Metadata Admin/005G0000002bhKMIAY

[sf:listMetadata] Last Modified By (Name/Id): Metadata Admin/005G0000002bhKMIAY

[sf:listMetadata] ************************************************************

[sf:listMetadata] ************************************************************

[sf:listMetadata] FileName: installedPackages/packageb.installedPackage

[sf:listMetadata] FullName/Id: packageb/0A3G0000000UPPTKA4

[sf:listMetadata] Manageable State: null

[sf:listMetadata] Namespace Prefix: packageb

[sf:listMetadata] Created By (Name/Id): Metadata Admin/005G0000002bhKMIAY

[sf:listMetadata] Last Modified By (Name/Id): Metadata Admin/005G0000002bhKMIAY

[sf:listMetadata] ************************************************************

'retrieve' call via Java.

The code linked in this help topic, shows quite a complex amount of Java code to do the 'retrieve' operation. Oddly it does not specify anything in the RetrieveRequest parameter it passes. In your case you would have to add the required packages you want to retrieve (from the listMedata) to the specifiedFiles member in this class. Then using the code in the doc parse the zip file returned to find the .installedPackage files. Quite a lof code, so since your in the world of Ant, read on...

'retrieve' call via the Salesforce Ant Migration Tool.

The Bulk Retrieve Ant target provided in the toolkit wraps the 'retrieve' call above. And also generates the package.xml internally with the wildcard on the component type you provide. This for me is the best route for you if your wanting to do this in Ant. Then utilise Ant's other tasks to read the files or roll your own.

<mkdir dir="${basedir}/installedPackages"/>
<sf:bulkRetrieve
    username="${sf.username}"
    password="${sf.password}"
    metadataType="InstalledPackage"
    retrieveTarget="${basedir}/installedPackages"/>     

Results in the following...

enter image description here

3
  • Very helpful. It's a pity that the listMetadata doesn't output the versionNumber. So it looks (as you suggest) like for now that the bulkRetrieve is the way to go. I may work up the will power to further wrap that in Ant code to, for example, leave the version number directly in a property.
    – Keith C
    Jul 3, 2013 at 21:48
  • How do you get the package name that you pass to the packageNames attribute on the retrieve target in ant? I use the names shown on the installed packages screen and all of them work except for those with special characters in the title (& and .). The ListMetadata only gives the name that matches the prefix.
    – Maggie
    Oct 14, 2015 at 18:47
  • Its just the namespace, not the package name. Oct 17, 2015 at 20:50
8

Found the most easiest way to get package name and version numbers.

SELECT DurableId, Id, IsSalesforce,MajorVersion,MinorVersion, Name,NamespacePrefix FROM Publisher enter image description here

Now its clear that we can simply query this information using SOQL. This can be further queried via API using java and used in ant.

1
  • 3
    DurableId or Name is not package name. This the Publisher name. A single publisher can have multiple package names.
    – dmachop
    Apr 5, 2019 at 21:10
7

Looks like with tooling api there is a better way to get this . You can run the below query via the tooling rest api or soap api with v41.0 and above

SELECT Id, SubscriberPackageId, SubscriberPackage.NamespacePrefix,
  SubscriberPackage.Name, SubscriberPackageVersion.Id,
  SubscriberPackageVersion.Name, SubscriberPackageVersion.MajorVersion,
  SubscriberPackageVersion.MinorVersion,
  SubscriberPackageVersion.PatchVersion,
  SubscriberPackageVersion.BuildNumber
 FROM InstalledSubscriberPackage
 ORDER BY SubscriberPackageId

This should list all the packages installed in the org with their versions .

1
  • This worked for me, just wish there was an easier way to grab that SubscriberPackageId than resorting to Tooling API. Related post here Feb 10, 2020 at 21:21
5

In all versions of Salesforce (AFAIK) you can get a list of all packages using SOQL with the Enterprise WSDL and by setting your client to match that of the Eclipse force.com IDE plugin.

The following SOQL queries are required (lifted from the Open Source Force.com IDE plugin)

DEVELOPMENT_PACKAGES = "SELECT Id, Name, Description, IsManaged FROM DevelopmentPackageVersion";
INSTALLED_PACKAGES = "SELECT Id, Name, Description, IsManaged, VersionName FROM InstalledPackageVersion";

If you try to run these on the Enterprise WSDL using the query call, you'll get back an Invalid Type response of the following:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sf="urn:fault.partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Body>
        <soapenv:Fault>
            <faultcode>sf:INVALID_TYPE</faultcode>
            <faultstring>INVALID_TYPE: sObject type &apos;InstalledPackageVersion&apos; is not supported.</faultstring>
            <detail>
                <sf:InvalidSObjectFault xsi:type="sf:InvalidSObjectFault">
                    <sf:exceptionCode>INVALID_TYPE</sf:exceptionCode>
                    <sf:exceptionMessage>sObject type &apos;InstalledPackageVersion&apos; is not supported.</sf:exceptionMessage>
                    <sf:row>-1</sf:row>
                    <sf:column>-1</sf:column>
                </sf:InvalidSObjectFault>
            </detail>
        </soapenv:Fault>
    </soapenv:Body>
</soapenv:Envelope>

Of course, the Force.COM IDE plugin is able to get those packages, so you'll want to make your client consuming the Enterprise service add the client element to the CallOptions node of your soap request body and set it to apex_eclipse/v$versionnumber where $versionNumber is the version of the API. After doing this your client is granted additional privileges granted to the IDE that normal clients are not. In fact, during my testing, it would seem the API doesn't care what the value after the '/v' in the apex_eclipse/v$versionNumber string is.

When calling login, you'll need to provide the client element and set it to apex_eclipse/v$versionNumber, if you don't, the sessionid will not be associated with an eclipse session and subsequent API calls that are made will not be made impersonating the eclipse IDE, even with the client element set correctly. You MUST pass the client element on the login call AND subsequent calls

Login Request

<x:Envelope xmlns:x="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn1="urn:partner.soap.sforce.com">
    <x:Header>
    <h:CallOptions xmlns:h="urn:partner.soap.sforce.com" xmlns="urn:partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <defaultNamespace xsi:nil="true"/>
      <returnFieldDataTypes xsi:nil="true"/>
      <urn1:client>apex_eclipse/v35.0</urn1:client>
    </h:CallOptions>
    <h:LoginScopeHeader xmlns:h="urn:partner.soap.sforce.com" xmlns="urn:partner.soap.sforce.com"/>
  </x:Header>
    <x:Body>
        <urn1:login>
            <urn1:username>username</urn1:username>
            <urn1:password>password</urn1:password>
        </urn1:login>
    </x:Body>
</x:Envelope>

Raw Soap Request

<x:Envelope xmlns:x="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn1="urn:partner.soap.sforce.com">
    <x:Header>
        <urn1:SessionHeader>
            <urn1:sessionId>Obfuscated</urn1:sessionId>
        </urn1:SessionHeader>
        <urn1:CallOptions>
            <urn1:client>apex_eclipse/v37.0</urn1:client>
        </urn1:CallOptions>

        <urn1:MruHeader>
            <urn1:updateMru>false</urn1:updateMru>
        </urn1:MruHeader>
    </x:Header>
    <x:Body>
        <urn1:query>
            <urn1:queryString>SELECT Id, Name, Description, IsManaged, VersionName FROM InstalledPackageVersion</urn1:queryString>
        </urn1:query>
    </x:Body>
</x:Envelope>

Raw SOAP Response

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sf="urn:sobject.partner.soap.sforce.com">
    <soapenv:Header>
        <LimitInfoHeader>
            <limitInfo>
                <current>10039</current>
                <limit>15000</limit>
                <type>API REQUESTS</type>
            </limitInfo>
        </LimitInfoHeader>
    </soapenv:Header>
    <soapenv:Body>
        <queryResponse>
            <result xsi:type="QueryResult">
                <done>true</done>
                <queryLocator xsi:nil="true"/>
                <records xsi:type="sf:sObject">
                    <sf:type>DevelopmentPackageVersion</sf:type>
                    <sf:Id>0A258000000Cal2CAC</sf:Id>
                    <sf:Id>0A258000000Cal2CAC</sf:Id>
                    <sf:Name>New Test Package</sf:Name>
                    <sf:Description>Test Package</sf:Description>
                    <sf:IsManaged>false</sf:IsManaged>
                </records>
                <size>1</size>
            </result>
        </queryResponse>
    </soapenv:Body>
</soapenv:Envelope>

And there you go for anyone else coming to this thread late like myself. That's how you get lists of packages using the Salesforce API. You can then perform a retrieve() call against the metadata API and provide it a string contaning the package names you retrieved using the soap API as outlined above.

Hopefully this will save other people the time I spent digging through the source repo on GitHub for the Force.com IDE.

1
  • fishofprey.com/2016/03/salesforce-forcecom-ide-superpowers.h‌​tml discusses this and mentions a tool called FuseIT SFDC Explorer that will let you emulate the Force.com IDE and run these queries interactively. The version I used (v3.4.16350.1 beta) had a bug where it wouldn't save the Client in the connection string. However, if you define your connection, close the app, then edit the connection string by hand in the FuseIT.SFDC.Explorer.exe.config file and open it back up then it will work.
    – Tim Lewis
    Jan 9, 2017 at 21:40
3

Use below Code, that would resolve all your problems.. This API is only available in Version 31 and above. So if you create a class. Select version 31 or above.

String PACKAGE_NAMESPACE_PREFIX = 'DSE';
//PackageLicense p1 = new PackageLicense();
PackageLicense pl = [SELECT Id, NamespacePrefix, AllowedLicenses, UsedLicenses, 
                         ExpirationDate,Status FROM PackageLicense WHERE 
                         NamespacePrefix = :PACKAGE_NAMESPACE_PREFIX];

system.debug('PPPPPPPP'+pl); //added to get code formatted
1
  • 1
    Thanks, good to know. Note the original question was looking for version numbers; presently all this gives is the namespace prefix of the package.
    – Keith C
    Dec 2, 2014 at 15:12
3

In Salesforce Apex version 32 and beyond, you can use the following SOQL to retrieve list of all the packages installed in Salesforce.

SELECT AllowedLicenses,CreatedDate,ExpirationDate,Id,IsProvisioned,LastModifiedDate,NamespacePrefix,Status,SystemModstamp,UsedLicenses 
FROM PackageLicense
2

I built some ant macros that we're using at the Salesforce.com Foundation to build Project Cumulus. Our build target manages the upgrade and downgrade of 5 managed packages as well as installing the managed beta release of the Cumulus package. There is an example of logic to handle packages which depend on other packages. In our project, npo02 depends on npe01 so if you need to downgrade npe01, you have to uninstall npo02 first.

I tried to separate out the logic specific to our project vs the more generic functionality:

See the macro and target: updateDependentPackages https://github.com/SalesforceFoundation/Cumulus/blob/dev/build.xml

See the macros getPackageVersion and updatePackageVersion https://github.com/SalesforceFoundation/Cumulus/blob/dev/lib/ant-salesforce.xml

Full documentation of the process is at http://salesforcefoundation.github.io/CumulusCI/

4
  • Thanks for posting this - hopefully it will help others. (I did create an Ant task for the version check/install in the end.)
    – Keith C
    Jan 20, 2014 at 17:31
  • Nice work, those install and uninstall targets look familiar though... ;-) Jan 20, 2014 at 23:19
  • Yes, your targets were definitely an inspiration (hence the credit in the comment above them) to me to dive into learning ant well enough to add the version management functionality in ant instead of Java. Jan 21, 2014 at 22:18
  • Keith C, Is it possible to share the code to check the version? The link provided @github shows 404.
    – SF Learner
    Jul 6, 2015 at 2:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .