def save_component(self, component_attribute, body): """ This method contains 5 steps: 1. Post classid to get MetadataContainerId 2. Post Component Member 3. Post ContainerAsyncRequest to get AsyncRequest Id 4. Get ContainerAsyncRequest Response by Id in step 3 5. Delete the MetadataContainerId Notes: Because if ContainerAsyncRequest has problem, we can't reuse the MetadataContainerId, so we need to delete it and get it every time. @component_attribute @body: Code content """ # Component Attribute component_type = component_attribute["type"] component_id = component_attribute["id"] component_body = component_attribute["body"] # Get MetadataContainerId data = { "name": "Save" + component_type[4 : len(component_type)] + component_id } container_url = '/services/data/v28.0/tooling/sobjects/MetadataContainer' result = self.post(container_url, data) print ("MetadataContainer Response: ", result) # If status_code < 399, it means post succeed if result["status_code"] < 399: container_id = result.get("id") else: # If status_code < 399, it means post failed, # If DUPLICATE Container Id, just delete it and restart this function if result["errorCode"] == "DUPLICATE_VALUE": error_message = result["message"] container_id = error_message[error_message.rindex("1dc"): len(error_message)] delete_result = self.delete(container_url + "/" + container_id) if delete_result["status_code"] < 399: util.sublime_status_message("container_id is deleted.") # We can't reuse the container_id which caused error # Post Request to get MetadataContainerId return self.save_component(component_attribute, body) else: util.sublime_error_message(result) return # Post ApexComponentMember data = { "ContentEntityId": component_id, "MetadataContainerId": container_id, "Body": body } url = "/services/data/v28.0/tooling/sobjects/" + component_type + "Member" result = self.post(url, data) print ("Post ApexComponentMember: ", result) # Post ContainerAsyncRequest data = { "MetadataContainerId": container_id, "isCheckOnly": False } sync_request_url = '/services/data/v28.0/tooling/sobjects/ContainerAsyncRequest' result = self.post(sync_request_url, data) request_id = result.get("id") print ("Post ContainerAsyncRequest: ", result) # Get ContainerAsyncRequest Result result = self.get(sync_request_url + "/" + request_id) state = result["State"] print ("Get ContainerAsyncRequest: ") pprint.pprint(result) return_result = {} if state == "Completed": return_result = { "success": True } while state == "Queued": print ("Async Request is queued, please wait for 5 seconds...") time.sleep(5) result = self.get(sync_request_url + "/" + request_id) state = result["State"] if state == "Completed": return_result = { "success": True } if state == "Failed": # This error need more process, because of confused single quote compile_errors = unescape(result["CompilerErrors"]) compile_errors = json.loads(compile_errors) if len(compile_errors) > 0: compile_error = compile_errors[0] extend = util.none_value(compile_error["extent"]) line = util.none_value(compile_error["line"]) problem = util.none_value(compile_error["problem"]) name = util.none_value(compile_error["name"]) error_message = extend + ": " + name + " has problem: " +\ problem + " at line " + str(line) else: error_message = result["ErrorMsg"] error_message = unescape(error_message, {"'": "'", """: '"'}) return_result = { "success": False, "message": error_message } # Whatever succeed or failed, just delete MetadataContainerId delete_result = self.delete(container_url + "/" + container_id) status_code = delete_result["status_code"] if status_code < 399: util.sublime_status_message("container_id is deleted.") # Result used in thread invoke self.result = return_result
def save_component(self, component_attribute, body): """ This method contains 5 steps: 1. Post classid to get MetadataContainerId 2. Post Component Member 3. Post ContainerAsyncRequest to get AsyncRequest Id 4. Get ContainerAsyncRequest Response by Id in step 3 5. Delete the MetadataContainerId Notes: Because if ContainerAsyncRequest has problem, we can't reuse the MetadataContainerId, so we need to delete it and get it every time. @component_attribute @body: Code content """ # Component Attribute component_type = component_attribute["type"] component_id = component_attribute["id"] component_body = component_attribute["body"] # Get MetadataContainerId data = { "name": "Save" + component_type[4:len(component_type)] + component_id } container_url = '/services/data/v28.0/tooling/sobjects/MetadataContainer' result = self.post(container_url, data) print("MetadataContainer Response: ", result) # If status_code < 399, it means post succeed if result["status_code"] < 399: container_id = result.get("id") else: # If status_code < 399, it means post failed, # If DUPLICATE Container Id, just delete it and restart this function if result["errorCode"] == "DUPLICATE_VALUE": error_message = result["message"] container_id = error_message[error_message. rindex("1dc"):len(error_message)] delete_result = self.delete(container_url + "/" + container_id) if delete_result["status_code"] < 399: util.sublime_status_message("container_id is deleted.") # We can't reuse the container_id which caused error # Post Request to get MetadataContainerId return self.save_component(component_attribute, body) else: util.sublime_error_message(result) return # Post ApexComponentMember data = { "ContentEntityId": component_id, "MetadataContainerId": container_id, "Body": body } url = "/services/data/v28.0/tooling/sobjects/" + component_type + "Member" result = self.post(url, data) print("Post ApexComponentMember: ", result) # Post ContainerAsyncRequest data = {"MetadataContainerId": container_id, "isCheckOnly": False} sync_request_url = '/services/data/v28.0/tooling/sobjects/ContainerAsyncRequest' result = self.post(sync_request_url, data) request_id = result.get("id") print("Post ContainerAsyncRequest: ", result) # Get ContainerAsyncRequest Result result = self.get(sync_request_url + "/" + request_id) state = result["State"] print("Get ContainerAsyncRequest: ", result) return_result = {} if state == "Completed": return_result = {"success": True} while state == "Queued": print("Async Request is queued, please wait for 5 seconds...") time.sleep(5) result = self.get(sync_request_url + "/" + request_id) state = result["State"] if state == "Completed": return_result = {"success": True} if state == "Failed": # This error need more process, because of confused single quote compile_errors = unescape(result["CompilerErrors"]) compile_errors = json.loads(compile_errors) if len(compile_errors) > 0: compile_error = compile_errors[0] extend = util.none_value(compile_error["extent"]) line = util.none_value(compile_error["line"]) problem = util.none_value(compile_error["problem"]) name = util.none_value(compile_error["name"]) error_message = extend + ": " + name + " has problem: " +\ problem + " at line " + str(line) else: error_message = result["ErrorMsg"] error_message = unescape(error_message, { "'": "'", """: '"' }) return_result = {"success": False, "message": error_message} # Whatever succeed or failed, just delete MetadataContainerId delete_result = self.delete(container_url + "/" + container_id) status_code = delete_result["status_code"] if status_code < 399: util.sublime_status_message("container_id is deleted.") # Result used in thread invoke self.result = return_result
def refresh_components(self, component_types): """ Download the specified components @component_types: just support ApexPage, ApexComponent, ApexTrigger and ApexClass """ # Firstly Login self.login(True) # Put totalSize at first item component_metadata = {} for component_type in component_types: component_type_attrs = self.toolingapi_settings[component_type] component_outputdir = component_type_attrs["outputdir"] component_body = component_type_attrs["body"] component_extension = component_type_attrs["extension"] component_soql = component_type_attrs["soql"] result = self.query_all(component_soql) # The users password has expired, you must call SetPassword # before attempting any other API operations # Database.com not support ApexComponent if result["status_code"] > 399: continue size = len(result["records"]) print (SEPRATE) print (str(component_type) + " Size: " + str(size)) print (SEPRATE) records = result["records"] component_attributes = {} for record in records: # Get Component Name of this record component_name = record['Name'] component_url = record['attributes']['url'] component_id = record["Id"] print (str(component_type) + " ==> " + str(record['Name'])) # Write mapping of component_name with component_url # into metadata.sublime-settings component_attributes[component_name] = { "url": component_url, "id": component_id } # Get the body body = record[component_body] # Save Component Body, Component Type to attribute component_attributes[component_name]["body"] = component_body component_attributes[component_name]["extension"] = component_extension component_attributes[component_name]["type"] = component_type # Judge Component is Test Class or not if component_type == "ApexClass": if "@isTest" in body or "testMethod" in body or\ "testmethod" in body or "test" in component_name or\ "Test" in component_name: component_attributes[component_name]["is_test"] = True else: component_attributes[component_name]["is_test"] = False # Write body to local file fp = open(component_outputdir + "/" + component_name +\ component_extension, "wb") try: body = bytes(body, "UTF-8") except: body = body.encode("UTF-8") fp.write(body) # Set status_message util.sublime_status_message(component_name + " [" + component_type + "] Downloaded") component_metadata[component_type] = component_attributes # Self.result is used to keep thread result self.result = component_metadata
def refresh_components(self, component_types): """ Download the specified components @component_types: just support ApexPage, ApexComponent, ApexTrigger and ApexClass """ # Firstly Login self.login(True) # Put totalSize at first item component_metadata = {} for component_type in component_types: component_type_attrs = self.toolingapi_settings[component_type] component_outputdir = component_type_attrs["outputdir"] component_body = component_type_attrs["body"] component_extension = component_type_attrs["extension"] component_soql = component_type_attrs["soql"] result = self.query_all(component_soql) # The users password has expired, you must call SetPassword # before attempting any other API operations # Database.com not support ApexComponent if result["status_code"] > 399: continue size = len(result["records"]) print(SEPRATE) print(str(component_type) + " Size: " + str(size)) print(SEPRATE) records = result["records"] component_attributes = {} for record in records: # Get Component Name of this record component_name = record['Name'] component_url = record['attributes']['url'] component_id = record["Id"] print(str(component_type) + " ==> " + str(record['Name'])) # Write mapping of component_name with component_url # into metadata.sublime-settings component_attributes[component_name] = { "url": component_url, "id": component_id } # Get the body body = record[component_body] # Save Component Body, Component Type to attribute component_attributes[component_name]["body"] = component_body component_attributes[component_name][ "extension"] = component_extension component_attributes[component_name]["type"] = component_type # Judge Component is Test Class or not if component_type == "ApexClass": if "@isTest" in body or "testMethod" in body or\ "testmethod" in body or "test" in component_name or\ "Test" in component_name: component_attributes[component_name]["is_test"] = True else: component_attributes[component_name]["is_test"] = False # Write body to local file fp = open(component_outputdir + "/" + component_name +\ component_extension, "wb") try: body = bytes(body, "UTF-8") except: body = body.encode("UTF-8") fp.write(body) # Set status_message util.sublime_status_message(component_name + " [" + component_type + "] Downloaded") component_metadata[component_type] = component_attributes # Self.result is used to keep thread result self.result = component_metadata