Simon Fell > Its just code > Metadata API and Reports

Sunday, October 26, 2008

I've seen a few questions on this recently (this is new for v14.0), so i thought I put together a sample of calling retrieve in the metadata file api to fetch a package of all your reports. This is in VS.NET 2005, but should be easy to translate to other environments. The code does the following steps (this is more complex that other cases because Reports don't support Wildcards)

  1. Calls the partner api login to get a sessionId and the URL to the metadata API for your username.
  2. Creates and configures an instance of the metadata client.
  3. Calls ListMetadata to find out all the ReportFolders.
  4. Using the list of ReportFolders, calls ListMetadata again to find out all the Reports in those ReportFolders.
  5. Uses Retrieve / CheckStatus / CheckRetrieveStatus to fetch a package of the reports, and writes the package out to c:\reports.zip

using System;
using System.Collections.Generic;
using System.Text;

namespace mdRetrieve
{
    class Program
    {
        static void Main(string[] args)
        {
            // regular Enterprise/Partner Login call
	    // I added a WebReference of the partner wsdl as sf, and the Metadata WSDL as md
            sf.SforceService svc = new sf.SforceService();
            sf.LoginResult lr = svc.login(args[0], args[1]);

            // set up a MetdataService client
            md.MetadataService ms = new md.MetadataService();
            ms.SessionHeaderValue = new md.SessionHeader();
            ms.SessionHeaderValue.sessionId = lr.sessionId;
            ms.Url = lr.metadataServerUrl;

            Console.WriteLine("Logged in as {0}", lr.userInfo.userName);
            String [] reportFiles = ListReports(ms);
            RetrieveReports(ms, reportFiles);
        }


        static String [] ListReports(md.MetadataService ms)
        {
            // can't use wildcards with reports, so need to fetch the list
            // of ReportFolders first, then fetch all the reports in
            // each folder.
            md.ListMetadataQuery q = new md.ListMetadataQuery();
            q.type = "ReportFolder";
            md.FileProperties[] fp = ms.listMetadata(new md.ListMetadataQuery[] { q });
            if (fp == null)
            {
                Console.WriteLine("No report folders returned");
                return new String[0];
            }
            List reportFiles = new List();
            q.type = "Report";
            foreach (md.FileProperties p in fp)
            {
                q.folder = p.fullName;
                // listMetadata can take more than one item at a time
                // left as an exercise for the reader to batch up these calls.
                md.FileProperties[] rps = ms.listMetadata(new md.ListMetadataQuery[] { q });
                if (fp == null) continue;
                foreach (md.FileProperties rp in rps)
                {
                    Console.WriteLine("{0}", rp.fileName);
                    reportFiles.Add(rp.fullName);
                }
            }
            return reportFiles.ToArray();
        }

        static void RetrieveReports(md.MetadataService ms, String [] reportFiles) {
            // build up an unpackaged retrieve request for the list of reports.
            md.RetrieveRequest r = new md.RetrieveRequest();
            r.apiVersion = 14.0;
            r.unpackaged = new md.Package();
            md.PackageTypeMembers m = new md.PackageTypeMembers();
            m.name = "Report";
            m.members = reportFiles;
            r.unpackaged.types = new md.PackageTypeMembers[] { m };

            // start the retrieve request
            md.AsyncResult ar = ms.retrieve(r);
            // wait for it to complete, sleeping as necassary.
            while (!ar.done)
            {
                System.Threading.Thread.Sleep(ar.secondsToWait * 1000);
                ar = ms.checkStatus(new String[] { ar.id })[0];
            }

            // did it work ?
            if (ar.state == md.AsyncRequestState.Error)
                Console.WriteLine("{0} {1}", ar.statusCode, ar.message);
            else
            {
                // now actually go get the results 
                md.RetrieveResult rr = ms.checkRetrieveStatus(ar.id);
                if (rr.messages != null)
                    foreach (md.RetrieveMessage rm in rr.messages)
                        Console.WriteLine("{0} : {1}", rm.fileName, rm.problem);

                // write the zipFile out to a disk file.
                using (System.IO.FileStream fs = new System.IO.FileStream("c:\\reports.zip", System.IO.FileMode.Create))
                    fs.Write(rr.zipFile, 0, rr.zipFile.Length);
            }
        }
    }
}