Calling Retrieve from the Metadata API in VB.NET

The retrieve call is just the opposite of the deploy call.  With the retrieve call we receive the list of files that are requested and information about those files.  The files come back in a zip file that matches the structure that you would see in your Eclipse projects.

Here is the source code I was using to test the retrieve call.  Please see the comments in the code for details about specific sections.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
Private Sub DoRetrieve()
        Dim newRetrieveResult As SFMetadataAPI.RetrieveResult
        Dim asyncResultItem As New SFMetadataAPI.AsyncResult
        Dim newRetrieveRequest As New SFMetadataAPI.RetrieveRequest
        //The Package object is very important.  This tell the Retrieve() call what files to pull down.
        Dim myPackage As New SFMetadataAPI.Package
        //The PackageTypeMembers are the members of the package that should be retrieved.
        Dim myPackageMembers(0 To 25) As SFMetadataAPI.PackageTypeMembers
        Dim myPackageMemberItem As New SFMetadataAPI.PackageTypeMembers
        Dim fileNameForRetrievedZip As String
        Dim extractedFolder As String
        Dim myFiles(0 To 0) As String
        Dim myFilesForStandardObjects(0 To 25) As String
        Dim packagesToDownload(0 To 0) As String
        Dim asyncProcIDArray(0 To 0) As String
        Dim metaDataTypes As New ArrayList
        Dim newFiles As ArrayList
        Dim i As Integer
        newRetrieveRequest.apiVersion = 26
        If False Then
            'Use Package that is already setup in the Salesforce Org
            newRetrieveRequest.specificFiles = Nothing
            newRetrieveRequest.singlePackage = True
            packagesToDownload(0) = "MetaDataTest" 'This is a test package I created in my Salesforce Org for testing
            newRetrieveRequest.packageNames = packagesToDownload
        Else
            'Individual Files using a package structure       
            newRetrieveRequest.specificFiles = Nothing
            newRetrieveRequest.singlePackage = True
            newRetrieveRequest.packageNames = Nothing
            'Create the package structure
            myPackage.fullName = "pullPackage"
            //Set the API version.  We could place this in the app.config file so that it is not hard-coded
            myPackage.version = "26.0"
            //Add each of the metadata API types that we want to retrieve
            metaDataTypes.Add("ApexClass")
            metaDataTypes.Add("ApexComponent")
            metaDataTypes.Add("ApexPage")
            metaDataTypes.Add("ApexTrigger")
            metaDataTypes.Add("CustomLabels")
            metaDataTypes.Add("CustomApplication")
            metaDataTypes.Add("CustomObject")
            metaDataTypes.Add("CustomObjectTranslation")
            metaDataTypes.Add("CustomPageWebLink")
            metaDataTypes.Add("CustomSite")
            metaDataTypes.Add("CustomTab")
            metaDataTypes.Add("Dashboard")
            metaDataTypes.Add("DataCategoryGroup")
            metaDataTypes.Add("HomePageComponent")
            metaDataTypes.Add("HomePageLayout")
            metaDataTypes.Add("Layout")
            metaDataTypes.Add("Letterhead")
            metaDataTypes.Add("PermissionSet")
            metaDataTypes.Add("Profile")
            metaDataTypes.Add("RemoteSiteSetting")
            metaDataTypes.Add("Report")
            metaDataTypes.Add("ReportType")
            metaDataTypes.Add("Scontrol")
            metaDataTypes.Add("StaticResource")
            metaDataTypes.Add("Translations")
            metaDataTypes.Add("Workflow")
            myFiles(0) = "*" '* will bring back all files 'If you want just one file you can specify that "startHereController"
            myFilesForStandardObjects(0) = "*"
            myFilesForStandardObjects(1) = "Account"
            myFilesForStandardObjects(2) = "AccountContactRole"
            myFilesForStandardObjects(3) = "Activity"
            myFilesForStandardObjects(4) = "Asset"
            myFilesForStandardObjects(5) = "Campaign"
            myFilesForStandardObjects(6) = "CampaignMember"
            myFilesForStandardObjects(7) = "Case"
            myFilesForStandardObjects(8) = "CaseContactRole"
            myFilesForStandardObjects(9) = "Contact"
            myFilesForStandardObjects(10) = "ContentVersion"
            myFilesForStandardObjects(11) = "Contract"
            myFilesForStandardObjects(12) = "ContractContactRole"
            myFilesForStandardObjects(13) = "Event"
            myFilesForStandardObjects(14) = "Idea"
            //The JigsawSavedSearch did not seem to want to be retrieved yet.
            'myFilesForStandardObjects(15) = "JigsawSavedSearch"
            myFilesForStandardObjects(15) = "Lead"
            myFilesForStandardObjects(16) = "Opportunity"
            myFilesForStandardObjects(17) = "OpportunityContactRole"
            myFilesForStandardObjects(18) = "OpportunityLineItem"
            myFilesForStandardObjects(19) = "PartnerRole"
            myFilesForStandardObjects(20) = "Product2"
            myFilesForStandardObjects(21) = "Site"
            myFilesForStandardObjects(22) = "Solution"
            myFilesForStandardObjects(23) = "Task"
            myFilesForStandardObjects(24) = "User"
            myFilesForStandardObjects(25) = "UserLicense"
            'Metadata Name Location
            For i = 0 To 25
                //Create PackageTypeMembers one by one and place them in the myPackageMembers array
                myPackageMemberItem = New SFMetadataAPI.PackageTypeMembers
                myPackageMemberItem.name = metaDataTypes(i)
                If myPackageMemberItem.name = "CustomObject" Then
                    myPackageMemberItem.members = myFilesForStandardObjects
                Else
                    myPackageMemberItem.members = myFiles
                End If
                myPackageMembers(i) = myPackageMemberItem
            Next
            myPackage.types = myPackageMembers
            newRetrieveRequest.unpackaged = myPackage
        End If
        //Now that our package is built we can call the retrieve call on the Metadata API
        asyncResultItem = sfMetaData.retrieve(newRetrieveRequest)
        //The done property will tell us if the retrieve call is complete.
        If (asyncResultItem.done = False) Then
            //The waitUntilDone will use the id value returned from the original retrieve call.  Then Salesforce knows which retrieve call we are asking about for its status.
            waitUntilDone(asyncResultItem.id)
        End If
        //Now that the retrieve call is fully done we call checkRetrieveStatus to get the actual results of the retrieve call
        newRetrieveResult = sfMetaData.checkRetrieveStatus(asyncResultItem.id)
        If newRetrieveResult.messages Is Nothing Then
            //If messages are not nothing then we have returned some data
            fileNameForRetrievedZip = "C:\Users\terry.luschen\Documents\Salesforce Info\DeployTest3\Retrieve.zip"
            extractedFolder = "C:\Users\terry.luschen\Documents\Salesforce Info\DeployTest3\"
            //Delete the destination folder if it already exists to remove it and all sub-directories and files.
            If System.IO.Directory.Exists(extractedFolder) Then
                System.IO.Directory.Delete(extractedFolder, True)
            End If
            //Do some minor thread waiting so the file and directory changes have time to complete.
            Threading.Thread.Sleep(500)
            System.IO.Directory.CreateDirectory(extractedFolder)
            Threading.Thread.Sleep(500)
            //The zip file that has been downloaded because of the retrieve() call is simply a property called 'zipFile' on the result object.
            System.IO.File.WriteAllBytes(fileNameForRetrievedZip, newRetrieveResult.zipFile())
            'This is the zip file utility being used to extract the files.  The dll is called Ionic.Zip.dll
            Using zip As Ionic.Zip.ZipFile = Ionic.Zip.ZipFile.Read(fileNameForRetrievedZip)
                zip.ExtractAll("C:\Users\terry.luschen\Documents\Salesforce Info\DeployTest3\")
            End Using
            //We now have a folder that has all of the files that we wanted to retrieve.  The example here is basically requesting most of the
            //  metadata API objects.  This could make the retrieve rather slow if it is a large org.
        End If
    End Sub
Private Sub waitUntilDone(ByVal idValue As String)
        Dim asyncResultItems() As SFMetadataAPI.AsyncResult
        Dim asyncProcIDArray(0 To 0) As String
        asyncProcIDArray(0) = idValue
        Do
            Threading.Thread.Sleep(500)
            //The checkstatus call will ask Salesforce if the retrieve job requested has been completed or not.
            asyncResultItems = sfMetaData.checkStatus(asyncProcIDArray)
        Loop While asyncResultItems(0).done = False
End Sub

I hope that this code gives you a head start when you need to create some custom code to interact with the Metadata API.  It is a very powerful API and I think developers will continue to build more and more utilities around the metadata API to make our lives easier.

If there are other parts of the Metadata API that you would like me to explore please let me know.

This entry was posted in Technology and tagged , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>