def download_metadata_metadata(job, org): org.status = 'Downloading Metadata' org.save() try: # instantiate the metadata WSDL metadata_client = Client(settings.SALESFORCE_METADATA_WSDL) # URL for metadata API metadata_url = org.instance_url # set the metadata url based on the login result metadata_client.set_options(location=metadata_url) # set the session id from the login result session_header = metadata_client.factory.create("SessionHeader") session_header.sessionId = org.access_token metadata_client.set_options(soapheaders=session_header) # query for the list of metadata types all_metadata = metadata_client.service.describeMetadata( settings.SALESFORCE_API_VERSION) # Components for listing metadata component_list = [] loop_counter = 0 # loop through metadata types for component_type in all_metadata[0]: # create the component type record and save component_type_record = ComponentType() component_type_record.org = org component_type_record.name = component_type.xmlName component_type_record.save() # Component is a folder component - eg Dashboard, Document, EmailTemplate, Report if not component_type.inFolder: # set up the component type to query for components component = metadata_client.factory.create("ListMetadataQuery") component.type = component_type.xmlName # Add metadata to list component_list.append(component) else: # Append "Folder" keyword onto end of component type component = metadata_client.factory.create("ListMetadataQuery") # EmailTemplate = EmailFolder (for some reason) if component_type.xmlName == 'EmailTemplate': component.type = 'EmailFolder' else: component.type = component_type.xmlName + 'Folder' # All folders for specified metadata type all_folders = metadata_client.service.listMetadata( [component], settings.SALESFORCE_API_VERSION) folder_list = [] folder_loop_counter = 0 # Loop through folders for folder in all_folders: # Exclude managed package folders if 'namespacePrefix' not in folder or not folder.namespacePrefix: # Create component for folder to query folder_component = metadata_client.factory.create( "ListMetadataQuery") folder_component.type = component_type.xmlName folder_component.folder = folder.fullName folder_list.append(folder_component) if len(folder_list) >= 3 or (len(all_folders) - folder_loop_counter) <= 3: # Loop through folder components for folder_component in metadata_client.service.listMetadata( folder_list, settings.SALESFORCE_API_VERSION): # create the component record and save component_record = Component() component_record.component_type = component_type_record component_record.name = folder_component.fullName component_record.save() folder_list = [] folder_loop_counter = folder_loop_counter + 1 # Run the metadata query only if the list has reached 3 (the max allowed to query) # at one time, or if there is less than 3 components left to query if len(component_list) >= 3 or (len(all_metadata[0]) - loop_counter) <= 3: # loop through the components returned from the component query for component in metadata_client.service.listMetadata( component_list, settings.SALESFORCE_API_VERSION): # Exclude managed package components if 'namespacePrefix' not in component or not component.namespacePrefix: # Query database for parent component_type component_type_query = ComponentType.objects.filter( name=component.type, org=org.id) # Only add if found if component_type_query: # create the component record and save component_record = Component() component_record.component_type = component_type_query[ 0] component_record.name = component.fullName component_record.save() # clear list once done. This list will re-build to 3 components and re-query the service component_list = [] loop_counter = loop_counter + 1 # If a component type has no child components, remove the component type altogether for component_type in ComponentType.objects.filter(org=org.id): if not Component.objects.filter(component_type=component_type.id): component_type.delete() # Create retrieve request retrieve_request = metadata_client.factory.create('RetrieveRequest') retrieve_request.apiVersion = settings.SALESFORCE_API_VERSION retrieve_request.singlePackage = True retrieve_request.packageNames = None retrieve_request.specificFiles = None # List of components to retrieve files for component_retrieve_list = [] # Component types for the org component_types = ComponentType.objects.filter(org=org.id) # Now query through all components and download actual metadata for component_type in component_types: # Loop through child components of the component type for component in component_type.component_set.all(): # Create PackageTypeMember instant to retrieve component_to_retrieve = metadata_client.factory.create( 'PackageTypeMembers') component_to_retrieve.members = component.name component_to_retrieve.name = component_type.name component_retrieve_list.append(component_to_retrieve) # If more than 5k components to retrieve, run it in batches. Otherwise just do it in # one big hit if len(component_retrieve_list) <= 5000: # Execute the callout for all components retrieve_files(org, metadata_client, retrieve_request, component_retrieve_list, None) else: # Iterate over the component types and run in batches for component_type in component_types: component_retrieve_list = [] # Loop through child components of the component type for component in component_type.component_set.all(): # Create PackageTypeMember instant to retrieve component_to_retrieve = metadata_client.factory.create( 'PackageTypeMembers') component_to_retrieve.members = component.name component_to_retrieve.name = component_type.name component_retrieve_list.append(component_to_retrieve) # Execute the retrieve for the component type retrieve_files(org, metadata_client, retrieve_request, component_retrieve_list, component_type.name) except Exception as error: org.status = 'Error' org.error = error org.error_stacktrace = traceback.format_exc() org.status = 'Finished' org.save() # Check if both jobs are now finished check_overall_status(job)
def retrieve_files(org, metadata_client, retrieve_request, component_retrieve_list, component_type): """ Method to phyiscally retrieve files from Salesforce via the metadata API """ zipfile_name = 'metadata%s.zip' % (str(org.org_number)) # The overall package to retrieve package_to_retrieve = metadata_client.factory.create('Package') package_to_retrieve.apiAccessLevel = None package_to_retrieve.types = component_retrieve_list package_to_retrieve.packageType = None # This stupid line of code took me ages to work out! # Add retrieve package to the retrieve request retrieve_request.unpackaged = package_to_retrieve # Start the async retrieve job retrieve_job = metadata_client.service.retrieve(retrieve_request) # Set the retrieve result - should be unfinished initially retrieve_result = metadata_client.service.checkRetrieveStatus( retrieve_job.id, True) # Continue to query retrieve result until it's done while not retrieve_result.done: # sleep job for 5 seconds time.sleep(10) # check job status retrieve_result = metadata_client.service.checkRetrieveStatus( retrieve_job.id, True) if not retrieve_result.success: org.status = 'Error' if 'errorMessage' in retrieve_result: org.error = retrieve_result.errorMessage elif 'messages' in retrieve_result: org.error = retrieve_result.messages[0] else: # Save the zip file result to server zip_file = open(zipfile_name, 'w+') zip_file.write(b64decode(retrieve_result.zipFile)) zip_file.close() # Delete all existing components for package - they need to be renamed if component_type: ComponentType.objects.filter(org=org.id, name=component_type).delete() else: ComponentType.objects.filter(org=org.id).delete() # Open zip file metadata = ZipFile(zipfile_name, 'r') # Loop through files in the zip file for filename in metadata.namelist(): try: # Set folder and component name folder_name = filename.split('/')[0] component_name = filename.split('/')[1] # Check if component type exists if ComponentType.objects.filter(org=org.id, name=folder_name): # If exists, use this as parent component type component_type_record = ComponentType.objects.filter( org=org.id, name=folder_name)[0] else: # create the component type record and save component_type_record = ComponentType() component_type_record.org = org component_type_record.name = folder_name component_type_record.save() # create the component record and save component_record = Component() component_record.component_type = component_type_record # If more / exist, append if len(filename.split('/')) > 2: component_record.name = component_name + '/' + filename.split( '/')[2] else: component_record.name = component_name component_record.content = metadata.read(filename) component_record.save() # not in a folder (could be package.xml). Skip record except: continue # Delete zip file, no need to store if os.path.isfile(zipfile_name): os.remove(zipfile_name) # Set the Org to finish when all of above is complete org.status = 'Finished'
def download_metadata_tooling(job, org): org.status = 'Downloading Metadata' org.save() try: tooling_url = org.instance_url + '/services/data/v' + str( settings.SALESFORCE_API_VERSION) + '.0/tooling/' headers = { 'Accept': 'application/json', 'Authorization': 'Bearer ' + org.access_token } metadata_types = [ 'ApexClass', 'ApexComponent', 'ApexPage', 'ApexTrigger', ] for component_type in metadata_types: # Build the query string data_query = 'select+id+from+%s+where+NamespacePrefix=null' % component_type # Execute the query metadata_records = requests.get(tooling_url + 'query/?q=' + data_query, headers=headers) # Convert to JSON object metadata_json = metadata_records.json() # Only continue if records exist to query if 'records' in metadata_json: # create the component type record and save component_type_record = ComponentType() component_type_record.org = org component_type_record.name = component_type component_type_record.save() count_children = 0 for component in metadata_json['records']: # Get the URL to query for the full body metadata_url = org.instance_url + component['attributes'][ 'url'] # Query for body record = requests.get(metadata_url, headers=headers) # create the component record and save component_record = Component() component_record.component_type = component_type_record if component_type == 'ApexPage' or component_type == 'ApexComponent': component_record.name = record.json()['Name'] component_record.content = record.json()['Markup'] #ApexClass or ApexTrigger else: component_record.name = record.json()['FullName'] component_record.content = record.json()['Body'] component_record.save() count_children += 1 if count_children == 0: component_type_record.delete() except Exception as error: org.status = 'Error' org.error = error org.error_stacktrace = traceback.format_exc() org.status = 'Finished' org.save() # Check if both jobs are now finished check_overall_status(job)
def download_metadata_metadata(job, org): org.status = 'Downloading Metadata' org.save() try: # instantiate the metadata WSDL metadata_client = Client('http://sforgcompare.herokuapp.com/static/metadata-32.xml') # URL for metadata API metadata_url = org.instance_url + '/services/Soap/m/' + str(settings.SALESFORCE_API_VERSION) + '.0/' + org.org_id # set the metadata url based on the login result metadata_client.set_options(location = metadata_url) # set the session id from the login result session_header = metadata_client.factory.create("SessionHeader") session_header.sessionId = org.access_token metadata_client.set_options(soapheaders = session_header) # query for the list of metadata types all_metadata = metadata_client.service.describeMetadata(settings.SALESFORCE_API_VERSION) # Components for listing metadata component_list = [] loop_counter = 0; # loop through metadata types for component_type in all_metadata[0]: # create the component type record and save component_type_record = ComponentType() component_type_record.org = org component_type_record.name = component_type.xmlName component_type_record.save() # Component is a folder component - eg Dashboard, Document, EmailTemplate, Report if not component_type.inFolder: # set up the component type to query for components component = metadata_client.factory.create("ListMetadataQuery") component.type = component_type.xmlName # Add metadata to list component_list.append(component) else: # Append "Folder" keyword onto end of component type component = metadata_client.factory.create("ListMetadataQuery") # EmailTemplate = EmailFolder (for some reason) if component_type.xmlName == 'EmailTemplate': component.type = 'EmailFolder' else: component.type = component_type.xmlName + 'Folder' # All folders for specified metadata type all_folders = metadata_client.service.listMetadata([component], settings.SALESFORCE_API_VERSION) folder_list = [] folder_loop_counter = 0 # Loop through folders for folder in all_folders: # Create component for folder to query folder_component = metadata_client.factory.create("ListMetadataQuery") folder_component.type = component_type.xmlName folder_component.folder = folder.fullName folder_list.append(folder_component) if len(folder_list) >= 3 or (len(all_folders) - folder_loop_counter) <= 3: # Loop through folder components for folder_component in metadata_client.service.listMetadata(folder_list, settings.SALESFORCE_API_VERSION): # create the component record and save component_record = Component() component_record.component_type = component_type_record component_record.name = folder_component.fullName component_record.save() folder_list = [] folder_loop_counter = folder_loop_counter + 1 # Run the metadata query only if the list has reached 3 (the max allowed to query) # at one time, or if there is less than 3 components left to query if len(component_list) >= 3 or (len(all_metadata[0]) - loop_counter) <= 3: # loop through the components returned from the component query for component in metadata_client.service.listMetadata(component_list, settings.SALESFORCE_API_VERSION): # Query database for parent component_type component_type_query = ComponentType.objects.filter(name = component.type, org = org.id) # Only add if found if component_type_query: # create the component record and save component_record = Component() component_record.component_type = component_type_query[0] component_record.name = component.fullName component_record.save() # clear list once done. This list will re-build to 3 components and re-query the service component_list = [] loop_counter = loop_counter + 1; # If a component type has no child components, remove the component type altogether for component_type in ComponentType.objects.filter(org = org.id): if not Component.objects.filter(component_type = component_type.id): component_type.delete() # Create retrieve request retrieve_request = metadata_client.factory.create('RetrieveRequest') retrieve_request.apiVersion = settings.SALESFORCE_API_VERSION retrieve_request.singlePackage = True retrieve_request.packageNames = None retrieve_request.specificFiles = None component_retrieve_list = [] # Now query through all components and download actual metadata for component_type in ComponentType.objects.filter(org = org.id): # Loop through child components of the component type for component in component_type.component_set.all(): component_to_retrieve = metadata_client.factory.create('PackageTypeMembers') component_to_retrieve.members = component.name component_to_retrieve.name = component_type.name component_retrieve_list.append(component_to_retrieve) # The overall package to retrieve package_to_retrieve = metadata_client.factory.create('Package') package_to_retrieve.apiAccessLevel = None package_to_retrieve.types = component_retrieve_list # Add retrieve package to the retrieve request retrieve_request.unpackaged = package_to_retrieve # Start the async retrieve job retrieve_job = metadata_client.service.retrieve(retrieve_request) # Set the retrieve result - should be unfinished initially retrieve_result = metadata_client.service.checkRetrieveStatus(retrieve_job.id) # Continue to query retrieve result until it's done while not retrieve_result.done: # check job status retrieve_result = metadata_client.service.checkRetrieveStatus(retrieve_job.id) # sleep job for 5 seconds time.sleep(10) if not retrieve_result.success: org.status = 'Error' if 'errorMessage' in retrieve_result: org.error = retrieve_result.errorMessage elif 'messages' in retrieve_result: org.error = retrieve_result.messages[0] else: # Save the zip file result to server zip_file = open('metadata.zip', 'w+') zip_file.write(b64decode(retrieve_result.zipFile)) zip_file.close() # Delete all existing components for package - they need to be renamed ComponentType.objects.filter(org = org.id).delete() # Open zip file metadata = ZipFile('metadata.zip', 'r') # Loop through files in the zip file for filename in metadata.namelist(): try: # Set folder and component name folder_name = filename.split('/')[0] component_name = filename.split('/')[1] # Check if component type exists if ComponentType.objects.filter(org = org.id, name = folder_name): # If exists, use this as parent component type component_type_record = ComponentType.objects.filter(org = org.id, name = folder_name)[0] else: # create the component type record and save component_type_record = ComponentType() component_type_record.org = org component_type_record.name = folder_name component_type_record.save() # create the component record and save component_record = Component() component_record.component_type = component_type_record # If more / exist, append if len(filename.split('/')) > 2: component_record.name = component_name + '/' + filename.split('/')[2] else: component_record.name = component_name component_record.content = metadata.read(filename) component_record.save() # not in a folder (could be package.xml). Skip record except: continue # Delete zip file, no need to store os.remove('metadata.zip') org.status = 'Finished' except Exception as error: org.status = 'Error' org.error = error org.save() # Check if both jobs are now finished check_overall_status(job)
def download_metadata_tooling(job, org): org.status = 'Downloading Metadata' org.save() try: tooling_url = org.instance_url + '/services/data/v' + str(settings.SALESFORCE_API_VERSION) + '.0/tooling/' headers = { 'Accept': 'application/json', 'Authorization': 'Bearer ' + org.access_token } metadata_types = [ 'ApexClass', 'ApexComponent', 'ApexPage', 'ApexTrigger', ] for component_type in metadata_types: data_query = 'select+id+from+' + component_type metadata_records = requests.get(tooling_url + 'query/?q=' + data_query, headers = headers) # Only continue if records exist to query if 'records' in metadata_records.json(): # create the component type record and save component_type_record = ComponentType() component_type_record.org = org component_type_record.name = component_type component_type_record.save() count_children = 0 for component in metadata_records.json()['records']: metadata_url = org.instance_url + component['attributes']['url'] record = requests.get(metadata_url, headers = headers) # Only take non package components if record.json()['NamespacePrefix'] == None: # create the component record and save component_record = Component() component_record.component_type = component_type_record if component_type == 'ApexPage' or component_type == 'ApexComponent': component_record.name = record.json()['Name'] component_record.content = record.json()['Markup'] #ApexClass or ApexTrigger else: component_record.name = record.json()['FullName'] component_record.content = record.json()['Body'] component_record.save() count_children += 1 if count_children == 0: component_type_record.delete() org.status = 'Finished' except Exception as error: org.status = 'Error' org.error = error org.save() # Check if both jobs are now finished check_overall_status(job)
def download_metadata_metadata(job, org): org.status = 'Downloading Metadata' org.save() try: # instantiate the metadata WSDL metadata_client = Client( 'http://sforgcompare.herokuapp.com/static/metadata-32.xml') # URL for metadata API metadata_url = org.instance_url + '/services/Soap/m/' + str( settings.SALESFORCE_API_VERSION) + '.0/' + org.org_id # set the metadata url based on the login result metadata_client.set_options(location=metadata_url) # set the session id from the login result session_header = metadata_client.factory.create("SessionHeader") session_header.sessionId = org.access_token metadata_client.set_options(soapheaders=session_header) # query for the list of metadata types all_metadata = metadata_client.service.describeMetadata( settings.SALESFORCE_API_VERSION) # Components for listing metadata component_list = [] loop_counter = 0 # loop through metadata types for component_type in all_metadata[0]: # create the component type record and save component_type_record = ComponentType() component_type_record.org = org component_type_record.name = component_type.xmlName component_type_record.save() # Component is a folder component - eg Dashboard, Document, EmailTemplate, Report if not component_type.inFolder: # set up the component type to query for components component = metadata_client.factory.create("ListMetadataQuery") component.type = component_type.xmlName # Add metadata to list component_list.append(component) else: # Append "Folder" keyword onto end of component type component = metadata_client.factory.create("ListMetadataQuery") # EmailTemplate = EmailFolder (for some reason) if component_type.xmlName == 'EmailTemplate': component.type = 'EmailFolder' else: component.type = component_type.xmlName + 'Folder' # All folders for specified metadata type all_folders = metadata_client.service.listMetadata( [component], settings.SALESFORCE_API_VERSION) folder_list = [] folder_loop_counter = 0 # Loop through folders for folder in all_folders: # Create component for folder to query folder_component = metadata_client.factory.create( "ListMetadataQuery") folder_component.type = component_type.xmlName folder_component.folder = folder.fullName folder_list.append(folder_component) if len(folder_list) >= 3 or (len(all_folders) - folder_loop_counter) <= 3: # Loop through folder components for folder_component in metadata_client.service.listMetadata( folder_list, settings.SALESFORCE_API_VERSION): # create the component record and save component_record = Component() component_record.component_type = component_type_record component_record.name = folder_component.fullName component_record.save() folder_list = [] folder_loop_counter = folder_loop_counter + 1 # Run the metadata query only if the list has reached 3 (the max allowed to query) # at one time, or if there is less than 3 components left to query if len(component_list) >= 3 or (len(all_metadata[0]) - loop_counter) <= 3: # loop through the components returned from the component query for component in metadata_client.service.listMetadata( component_list, settings.SALESFORCE_API_VERSION): # Query database for parent component_type component_type_query = ComponentType.objects.filter( name=component.type, org=org.id) # Only add if found if component_type_query: # create the component record and save component_record = Component() component_record.component_type = component_type_query[ 0] component_record.name = component.fullName component_record.save() # clear list once done. This list will re-build to 3 components and re-query the service component_list = [] loop_counter = loop_counter + 1 # If a component type has no child components, remove the component type altogether for component_type in ComponentType.objects.filter(org=org.id): if not Component.objects.filter(component_type=component_type.id): component_type.delete() # Create retrieve request retrieve_request = metadata_client.factory.create('RetrieveRequest') retrieve_request.apiVersion = settings.SALESFORCE_API_VERSION retrieve_request.singlePackage = True retrieve_request.packageNames = None retrieve_request.specificFiles = None component_retrieve_list = [] # Now query through all components and download actual metadata for component_type in ComponentType.objects.filter(org=org.id): # Loop through child components of the component type for component in component_type.component_set.all(): component_to_retrieve = metadata_client.factory.create( 'PackageTypeMembers') component_to_retrieve.members = component.name component_to_retrieve.name = component_type.name component_retrieve_list.append(component_to_retrieve) # The overall package to retrieve package_to_retrieve = metadata_client.factory.create('Package') package_to_retrieve.apiAccessLevel = None package_to_retrieve.types = component_retrieve_list # Add retrieve package to the retrieve request retrieve_request.unpackaged = package_to_retrieve # Start the async retrieve job retrieve_job = metadata_client.service.retrieve(retrieve_request) # Set the retrieve result - should be unfinished initially retrieve_result = metadata_client.service.checkRetrieveStatus( retrieve_job.id) # Continue to query retrieve result until it's done while not retrieve_result.done: # check job status retrieve_result = metadata_client.service.checkRetrieveStatus( retrieve_job.id) # sleep job for 5 seconds time.sleep(10) if not retrieve_result.success: org.status = 'Error' if 'errorMessage' in retrieve_result: org.error = retrieve_result.errorMessage elif 'messages' in retrieve_result: org.error = retrieve_result.messages[0] else: # Save the zip file result to server zip_file = open('metadata.zip', 'w+') zip_file.write(b64decode(retrieve_result.zipFile)) zip_file.close() # Delete all existing components for package - they need to be renamed ComponentType.objects.filter(org=org.id).delete() # Open zip file metadata = ZipFile('metadata.zip', 'r') # Loop through files in the zip file for filename in metadata.namelist(): try: # Set folder and component name folder_name = filename.split('/')[0] component_name = filename.split('/')[1] # Check if component type exists if ComponentType.objects.filter(org=org.id, name=folder_name): # If exists, use this as parent component type component_type_record = ComponentType.objects.filter( org=org.id, name=folder_name)[0] else: # create the component type record and save component_type_record = ComponentType() component_type_record.org = org component_type_record.name = folder_name component_type_record.save() # create the component record and save component_record = Component() component_record.component_type = component_type_record # If more / exist, append if len(filename.split('/')) > 2: component_record.name = component_name + '/' + filename.split( '/')[2] else: component_record.name = component_name component_record.content = metadata.read(filename) component_record.save() # not in a folder (could be package.xml). Skip record except: continue # Delete zip file, no need to store os.remove('metadata.zip') org.status = 'Finished' except Exception as error: org.status = 'Error' org.error = error org.save() # Check if both jobs are now finished check_overall_status(job)
def download_metadata_tooling(job, org): org.status = 'Downloading Metadata' org.save() try: tooling_url = org.instance_url + '/services/data/v' + str( settings.SALESFORCE_API_VERSION) + '.0/tooling/' headers = { 'Accept': 'application/json', 'Authorization': 'Bearer ' + org.access_token } metadata_types = [ 'ApexClass', 'ApexComponent', 'ApexPage', 'ApexTrigger', ] for component_type in metadata_types: data_query = 'select+id+from+' + component_type metadata_records = requests.get(tooling_url + 'query/?q=' + data_query, headers=headers) # Only continue if records exist to query if 'records' in metadata_records.json(): # create the component type record and save component_type_record = ComponentType() component_type_record.org = org component_type_record.name = component_type component_type_record.save() count_children = 0 for component in metadata_records.json()['records']: metadata_url = org.instance_url + component['attributes'][ 'url'] record = requests.get(metadata_url, headers=headers) # Only take non package components if record.json()['NamespacePrefix'] == None: # create the component record and save component_record = Component() component_record.component_type = component_type_record if component_type == 'ApexPage' or component_type == 'ApexComponent': component_record.name = record.json()['Name'] component_record.content = record.json()['Markup'] #ApexClass or ApexTrigger else: component_record.name = record.json()['FullName'] component_record.content = record.json()['Body'] component_record.save() count_children += 1 if count_children == 0: component_type_record.delete() org.status = 'Finished' except Exception as error: org.status = 'Error' org.error = error org.save() # Check if both jobs are now finished check_overall_status(job)