Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MD-API: Retrieve Email Alerts Metadata #21

Closed
denize opened this issue Mar 10, 2014 · 20 comments
Closed

MD-API: Retrieve Email Alerts Metadata #21

denize opened this issue Mar 10, 2014 · 20 comments

Comments

@denize
Copy link

denize commented Mar 10, 2014

Dear Andrew,
Thanks again for stepping forward to help!

My use case is to retrieve the list of all email alerts in my org with their name, description, sender-address, recipients, & CC'ed recipients. For this I have come up with the below code which I execute from the anonymous exec in dev console.

MetadataService.MetadataPort service = MetadataService.createService();

List<MetadataService.ListMetadataQuery> queries = new List<MetadataService.ListMetadataQuery>();
MetadataService.ListMetadataQuery queryEmailAlert = new MetadataService.ListMetadataQuery();
queryEmailAlert.type_x = 'WorkflowAlert';
queries.add(queryEmailAlert);

//get metadatalist
MetadataService.FileProperties[] properties = service.ListMetaData(queries,30);
for(MetadataService.FileProperties emailalert:properties){
        system.debug('Email Alert Name:::'+emailalert.fullname);

       //read metadata
       MetadataService.Metadata[] mdInfo = service.readMetadata('WorkflowAlert',new String[]{emailalert.fullname}).records;
        for(MetadataService.Metadata md:mdInfo){
            MetadataService.WorkflowAlert alert = (MetadataService.WorkflowAlert)md;
            system.debug('description:::'+alert.description);
            system.debug('From:::'+alert.senderAddress);
        }
}

But the execution stops with the error:

 "incompatible types since an instance of MetadataService.Metadata is never an instance of MetadataService.WorkflowAlert"

I believe it doesn't allow Metadata to be Typecast to WorkflowAlert. But I am not sure if the results can be achieved without doing that.
Can you please help letting me know where I am going wrong here?
Greatly appreciate your help! Many Thanks!

Note:
Since the class generated by the MetadataAPI wsdl exceeds my Apex Limit, I have cut it short to contain only what is required to achieve the required use case. Below is my short version of the MetadataService class used here for additional info, in case you like to have a look.

//Generated by wsdl2apex
public class MetadataService {
    public class MetadataPort {
        public String endpoint_x = 'https://cs30.salesforce.com/services/Soap/m/30.0';
        public Map<String,String> inputHttpHeaders_x;
        public Map<String,String> outputHttpHeaders_x;
        public String clientCertName_x;
        public String clientCert_x;
        public String clientCertPasswd_x;
        public Integer timeout_x;
        public MetadataService.SessionHeader_element SessionHeader;
        public MetadataService.DebuggingInfo_element DebuggingInfo;
        public MetadataService.CallOptions_element CallOptions;
        public MetadataService.DebuggingHeader_element DebuggingHeader;
        private String SessionHeader_hns = 'SessionHeader=http://soap.sforce.com/2006/04/metadata';
        private String DebuggingInfo_hns = 'DebuggingInfo=http://soap.sforce.com/2006/04/metadata';
        private String CallOptions_hns = 'CallOptions=http://soap.sforce.com/2006/04/metadata';
        private String DebuggingHeader_hns = 'DebuggingHeader=http://soap.sforce.com/2006/04/metadata';
        private String[] ns_map_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata', 'MetadataService'};
        public MetadataService.FileProperties[] listMetadata(MetadataService.ListMetadataQuery[] queries,Double asOfVersion) {
            MetadataService.listMetadata_element request_x = new MetadataService.listMetadata_element();
            MetadataService.listMetadataResponse_element response_x;
            request_x.queries = queries;
            request_x.asOfVersion = asOfVersion;
            Map<String, MetadataService.listMetadataResponse_element> response_map_x = new Map<String, MetadataService.listMetadataResponse_element>();
            response_map_x.put('response_x', response_x);
            WebServiceCallout.invoke(
              this,
              request_x,
              response_map_x,
              new String[]{endpoint_x,
              '',
              'http://soap.sforce.com/2006/04/metadata',
              'listMetadata',
              'http://soap.sforce.com/2006/04/metadata',
              'listMetadataResponse',
              'MetadataService.listMetadataResponse_element'}
            );
            response_x = response_map_x.get('response_x');
            return response_x.result;
        }
        public MetadataService.ReadResult readMetadata(String type_x,String[] fullNames) {
            MetadataService.readMetadata_element request_x = new MetadataService.readMetadata_element();
            MetadataService.readMetadataResponse_element response_x;
            request_x.type_x = type_x;
            request_x.fullNames = fullNames;
            Map<String, MetadataService.readMetadataResponse_element> response_map_x = new Map<String, MetadataService.readMetadataResponse_element>();
            response_map_x.put('response_x', response_x);
            WebServiceCallout.invoke(
              this,
              request_x,
              response_map_x,
              new String[]{endpoint_x,
              '',
              'http://soap.sforce.com/2006/04/metadata',
              'readMetadata',
              'http://soap.sforce.com/2006/04/metadata',
              'readMetadataResponse',
              'MetadataService.readMetadataResponse_element'}
            );
            response_x = response_map_x.get('response_x');
            return response_x.result;
        }
    }
    public class ReadResult {
        public MetadataService.Metadata[] records;
        private String[] records_type_info = new String[]{'records','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'records'};
    }
    public class Metadata {
        public String fullName;
        private String[] fullName_type_info = new String[]{'fullName','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'fullName'};
    }
    public class WorkflowEmailRecipient {
        public String field;
        public String recipient;
        public String type_x;
        private String[] field_type_info = new String[]{'field','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] recipient_type_info = new String[]{'recipient','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] type_x_type_info = new String[]{'type','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'field','recipient','type_x'};
    }
    public class WorkflowAlert {
        public String[] ccEmails;
        public String description;
        public Boolean protected_x;
        public MetadataService.WorkflowEmailRecipient[] recipients;
        public String senderAddress;
        public String senderType;
        public String template;
        private String[] ccEmails_type_info = new String[]{'ccEmails','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] description_type_info = new String[]{'description','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] protected_x_type_info = new String[]{'protected','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] recipients_type_info = new String[]{'recipients','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] senderAddress_type_info = new String[]{'senderAddress','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] senderType_type_info = new String[]{'senderType','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] template_type_info = new String[]{'template','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'ccEmails','description','protected_x','recipients','senderAddress','senderType','template'};
    }
    public class DebuggingInfo_element {
        public String debugLog;
        private String[] debugLog_type_info = new String[]{'debugLog','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'debugLog'};
    }
    public class CallOptions_element {
        public String client;
        private String[] client_type_info = new String[]{'client','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'client'};
    }
    public class DebuggingHeader_element {
        public MetadataService.LogInfo[] categories;
        public String debugLevel;
        private String[] categories_type_info = new String[]{'categories','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] debugLevel_type_info = new String[]{'debugLevel','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'categories','debugLevel'};
    }
    public class SessionHeader_element {
        public String sessionId;
        private String[] sessionId_type_info = new String[]{'sessionId','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'sessionId'};
    }
    public class LogInfo {
        public String category;
        public String level;
        private String[] category_type_info = new String[]{'category','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] level_type_info = new String[]{'level','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'category','level'};
    }
    public class listMetadataResponse_element {
        public MetadataService.FileProperties[] result;
        private String[] result_type_info = new String[]{'result','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'result'};
    }
    public class ListMetadataQuery {
        public String folder;
        public String type_x;
        private String[] folder_type_info = new String[]{'folder','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] type_x_type_info = new String[]{'type','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'folder','type_x'};
    }
    public class FileProperties {
        public String createdById;
        public String createdByName;
        public DateTime createdDate;
        public String fileName;
        public String fullName;
        public String id;
        public String lastModifiedById;
        public String lastModifiedByName;
        public DateTime lastModifiedDate;
        public String manageableState;
        public String namespacePrefix;
        public String type_x;
        private String[] createdById_type_info = new String[]{'createdById','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] createdByName_type_info = new String[]{'createdByName','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] createdDate_type_info = new String[]{'createdDate','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] fileName_type_info = new String[]{'fileName','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] fullName_type_info = new String[]{'fullName','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] id_type_info = new String[]{'id','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] lastModifiedById_type_info = new String[]{'lastModifiedById','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] lastModifiedByName_type_info = new String[]{'lastModifiedByName','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] lastModifiedDate_type_info = new String[]{'lastModifiedDate','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] manageableState_type_info = new String[]{'manageableState','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] namespacePrefix_type_info = new String[]{'namespacePrefix','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] type_x_type_info = new String[]{'type','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'createdById','createdByName','createdDate','fileName','fullName','id','lastModifiedById','lastModifiedByName','lastModifiedDate','manageableState','namespacePrefix','type_x'};
    }
    public class listMetadata_element {
        public MetadataService.ListMetadataQuery[] queries;
        public Double asOfVersion;
        private String[] queries_type_info = new String[]{'queries','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] asOfVersion_type_info = new String[]{'asOfVersion','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'queries','asOfVersion'};
    }
    public class readMetadata_element {
        public String type_x;
        public String[] fullNames;
        private String[] type_x_type_info = new String[]{'type','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] fullNames_type_info = new String[]{'fullNames','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'type_x','fullNames'};
    }
    public class readMetadataResponse_element {
        public MetadataService.ReadResult result;
        private String[] result_type_info = new String[]{'result','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'result'};
    }
    public static MetadataService.MetadataPort createService() {
        partnerSoapSforceCom.Soap sp = new partnerSoapSforceCom.Soap();
//        system.debug('loging in...');
        partnerSoapSforceCom.LoginResult loginResult = sp.login('uid','pwd');
//        system.debug('Login Result:::' +loginResult);

        MetadataService.MetadataPort service = new MetadataService.MetadataPort();
        service.SessionHeader = new MetadataService.SessionHeader_element();
        service.SessionHeader.sessionId = loginResult.sessionId;
//      service.SessionHeader.sessionId = UserInfo.getSessionId();
        return service;    
    }
}
@denize
Copy link
Author

denize commented Mar 11, 2014

Hi Andrew,

I am able to get around the "Incompatibe issue" mentioned in the above post by extending the WorkflowAlert class as below
(added Workflowaction extends Metadata & WorkflowAlert extends Workflowaction as per the WSDL)
and now the classes look like below:

    public virtual class WorkflowAction extends Metadata {
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{};
    }
    public class WorkflowAlert extends WorkflowAction {
//        public String type = 'WorkflowAlert';
//        public String fullName;
        public String[] ccEmails;
        public String description;
        public Boolean protected_x;
        public MetadataService.WorkflowEmailRecipient[] recipients;
        public String senderAddress;
        public String senderType;
        public String template;
//        private String[] type_att_info = new String[]{'xsi:type'};
//        private String[] fullName_type_info = new String[]{'fullName','http://soap.sforce.com/2006/04/metadata','string','0','1','false'};
        private String[] ccEmails_type_info = new String[]{'ccEmails','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] description_type_info = new String[]{'description','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] protected_x_type_info = new String[]{'protected','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] recipients_type_info = new String[]{'recipients','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] senderAddress_type_info = new String[]{'senderAddress','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] senderType_type_info = new String[]{'senderType','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] template_type_info = new String[]{'template','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'ccEmails','description','protected_x','recipients','senderAddress','senderType','template'};
    }

but now when I try to execute the code I get the below error:

System.CalloutException: Web service callout failed: Unable to parse callout response. Apex type not found for element ccEmails: Class.MetadataService.MetadataPort.readMetadata: line 49, column 1 AnonymousBlock: line 14, column 1 AnonymousBlock: line 14, column 1

I tried the below solutions but they did NOT solve the issue:

1.private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'}; 
updated the 2nd value in apex_schema_type_info to "false" from "true"

2. Inserted the Type and fullName variable as suggested in one of your other posts.

But neither of them seem to work. Could you please let me know of what is going wrong here?
Could I request you to look into this since this very urgent for one of my deliveries?
Many Thanks!

@afawcett
Copy link
Contributor

I am UK based, will take a few hours tonight see if I can help! :-)

@afawcett
Copy link
Contributor

I see your attempting to use the new Spring'14 readMetadata operation, I assume your in a Spring'14 sandbox then? The polymorphic nature of the XML returned by the readMetadata method does make things a little tricky actually. I had a similar issue using readMetadata with Layouts for my blog here. http://andyinthecloud.com/2013/12/24/updating-layouts-in-apex/, take a look at the section "Working around xsi:type". Take a look at my version of readMetadata here, https://gist.github.com/afawcett/8108494#file-metadataservice-cls-L6648. This is prototype code ahead of Salesforce formally updating the platform to Spring'14, where i planned to more fully support this for other Metadata Types, not just Layout. I can have a go at getting this to work with WorkflowAlert if you want.

@afawcett
Copy link
Contributor

Hi,

GitHub appears to be having some problems, I'm hoping this email reaches
you.

Based on the work i did in my blog around the Spring'14 Metadata API i got
it working.

// Read Workflow Alert
MetadataService.WorkflowAlert wfa =
(MetadataService.WorkflowAlert) service.readMetadata('WorkflowAlert',
new String[] { 'Account.Test' }).getRecords()[0];
System.debug('Description ' + wfa.description);
System.debug('Sender Address ' + wfa.senderAddress);

Note that i noticed if you pass an invalid metadata full name in, it
currently doesn't fail with an exception.

I'm not sure if this is a bug in the Metadata API or the Apex wrapper, I'll
have to look into it further.

Please see attached files (see comment below for Gist).

Thanks,

Andy

@afawcett
Copy link
Contributor

Ok GitHub is back, i've updated this Gist with a new MetadataService.cls (only for Spring'14 orgs, API v30). https://gist.github.com/afawcett/8108494

@denize
Copy link
Author

denize commented Mar 12, 2014

Hi Andrew,
Thanks once again for the great help.
Yes I am using Spring'14.
I am able to get around the callout issue using your version of the MetadataService.cls. But I am unable to get it to work still. Below is my dev console code:

MetadataService.MetadataPort service = MetadataService.createService();

List<MetadataService.ListMetadataQuery> queries = new List<MetadataService.ListMetadataQuery>();
MetadataService.ListMetadataQuery queryEmailAlert = new MetadataService.ListMetadataQuery();
queryEmailAlert.type_x = 'WorkflowAlert';
queries.add(queryEmailAlert);

//get the list of WF Alerts
MetadataService.FileProperties[] properties = service.ListMetaData(queries,30);
String[] listOfWfa;
integer i=0;
for(MetadataService.FileProperties wfafile:properties){
    listOfWfa[i++] = wfafile.fullname;
}

//Read Metadata
MetadataService.WorkflowAlert[] alert = (MetadataService.WorkflowAlert)service.readMetadata('WorkflowAlert',listOfWfa).getrecords();
for(MetadataService.WorkflowAlert wfa:alert){
        system.debug('description:::'+wfa.description);
        system.debug('From Addrss:::'+wfa.senderAddress);
}
system.debug('Number of Email Alerts:::' +properties.size());

But it gives the error:

Incompatible types since an instance of LIST is never an instance of MetadataService.WorkflowAlert

I am trying to obtain the list of workflowalert names from the listmetadata() call and then read the metadata of each workflowalert by passing the WFA names in the string array.
I am unable to understand where I am trying to convert a LIST into WorkflowAlert. Could you please let me know, if you could see where it is going wrong?

Also, I am trying to deeply understand the changes you have in your MetadataService.cls but I am new to this and still learning. So is it possible for you to let me know where I gotta start my learning and go to understand the MetadataAPI concepts in this depth?

Sincerely appreciate your help and thanks once again for looking into this amid your busy schedule.

@afawcett
Copy link
Contributor

No problem, can you confirm your using the MetadataService.cls i gave you in my previous response?

@afawcett
Copy link
Contributor

We are struggling between our respective timezones i think here.

But basically I've updated the MetadataService.readMetadata method from the standard generated one. This is because the XML deserialiser used by the platform does not support returning different data types, even though they all extend the same base Metadata type. So what i have done is allowed the caller of the method to pass in a hint to say which type of response will be expected and thus modify the parameter to the WebServiceCallout.invoke method to tell it the Response type to expect based on this. In order to make it work through a single method i have used an Apex Interface to make sure no matter what metadata response type is used its possible to obtain the list of metadata types. Since the response types all implement the interface (in due course i will make this change to all relevant metadata types, once Spring'14 is out in the wild).

The error your getting still looks like a compilation / save error? So my conclusion is your still not using the version of the MetadataService.cls I gave you earlier? Is this because you don't have room in your org? If so, it might be worth creating a new Spring'14 pre-release org (different email address) so that you can at least use my copy and sample code to confirm for yourself it is working.

@denize
Copy link
Author

denize commented Mar 13, 2014

Hi Andrew,

Yes, I am based in Hong Kong now so time-zone is messy :(

Btw, thanks again for thanks again for the details. Yes I am using your MetadataService.cls but only the sections relevant to the WorkflowAlert functionality since the whole class freezes my apex limit. Below is the abridged version of my MetadataService.cls merged with your changes namely - readmetadataservice, and the relevant classes.

//Generated by wsdl2apex
public class MetadataService {
    public class MetadataPort {
        public String endpoint_x = 'https://cs30.salesforce.com/services/Soap/m/30.0';
        public Map<String,String> inputHttpHeaders_x;
        public Map<String,String> outputHttpHeaders_x;
        public String clientCertName_x;
        public String clientCert_x;
        public String clientCertPasswd_x;
        public Integer timeout_x;
        public MetadataService.SessionHeader_element SessionHeader;
        public MetadataService.DebuggingInfo_element DebuggingInfo;
        public MetadataService.CallOptions_element CallOptions;
        public MetadataService.DebuggingHeader_element DebuggingHeader;
        private String SessionHeader_hns = 'SessionHeader=http://soap.sforce.com/2006/04/metadata';
        private String DebuggingInfo_hns = 'DebuggingInfo=http://soap.sforce.com/2006/04/metadata';
        private String CallOptions_hns = 'CallOptions=http://soap.sforce.com/2006/04/metadata';
        private String DebuggingHeader_hns = 'DebuggingHeader=http://soap.sforce.com/2006/04/metadata';
        private String[] ns_map_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata', 'MetadataService'};
        public MetadataService.FileProperties[] listMetadata(MetadataService.ListMetadataQuery[] queries,Double asOfVersion) {
            MetadataService.listMetadata_element request_x = new MetadataService.listMetadata_element();
            MetadataService.listMetadataResponse_element response_x;
            request_x.queries = queries;
            request_x.asOfVersion = asOfVersion;
            Map<String, MetadataService.listMetadataResponse_element> response_map_x = new Map<String, MetadataService.listMetadataResponse_element>();
            response_map_x.put('response_x', response_x);
            WebServiceCallout.invoke(
              this,
              request_x,
              response_map_x,
              new String[]{endpoint_x,
              '',
              'http://soap.sforce.com/2006/04/metadata',
              'listMetadata',
              'http://soap.sforce.com/2006/04/metadata',
              'listMetadataResponse',
              'MetadataService.listMetadataResponse_element'}
            );
            response_x = response_map_x.get('response_x');
            return response_x.result;
        }
        public MetadataService.IReadResult readMetadata(String type_x,String[] fullNames) {
            MetadataService.readMetadata_element request_x = new MetadataService.readMetadata_element();
            request_x.type_x = type_x;
            request_x.fullNames = fullNames;
            MetadataService.IReadResponseElement response_x;
            Map<String, MetadataService.IReadResponseElement> response_map_x = new Map<String, MetadataService.IReadResponseElement>();
            response_map_x.put('response_x', response_x);
            WebServiceCallout.invoke(
              this,
              request_x,
              response_map_x,
              new String[]{endpoint_x,
              '',
              'http://soap.sforce.com/2006/04/metadata',
              'readMetadata',
              'http://soap.sforce.com/2006/04/metadata',
              'readMetadataResponse',
              'MetadataService.read' + type_x + 'Response_element'}
            );
            response_x = response_map_x.get('response_x');
            return response_x.getResult();
        }
    }
    public interface IReadResult {
        MetadataService.Metadata[] getRecords();
    }
    public class ReadWorkflowAlertResult implements IReadResult {
        public MetadataService.WorkflowAlert[] records;
        public MetadataService.Metadata[] getRecords() { return records; }
        private String[] records_type_info = new String[]{'records','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'records'};
    }
    public interface IReadResponseElement {
        IReadResult getResult();
    }
    public class readWorkflowAlertResponse_element implements IReadResponseElement {
        public MetadataService.ReadWorkflowAlertResult result;
        public IReadResult getResult() { return result; }
        private String[] result_type_info = new String[]{'result','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'result'};
    }
    public class ReadResult {
        public MetadataService.Metadata[] records;
        private String[] records_type_info = new String[]{'records','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'records'};
    }
    public virtual class Metadata {
        public String fullName;
        private String[] fullName_type_info = new String[]{'fullName','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'fullName'};
    }
    public class WorkflowEmailRecipient {
        public String field;
        public String recipient;
        public String type_x;
        private String[] field_type_info = new String[]{'field','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] recipient_type_info = new String[]{'recipient','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] type_x_type_info = new String[]{'type','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'field','recipient','type_x'};
    }
    public virtual class WorkflowAction extends Metadata {
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{};
    }
    public class WorkflowAlert extends Metadata {
//        public String type = 'WorkflowAlert';
        public String fullName;
        public String[] ccEmails;
        public String description;
        public Boolean protected_x;
        public MetadataService.WorkflowEmailRecipient[] recipients;
        public String senderAddress;
        public String senderType;
        public String template;
//        private String[] type_att_info = new String[]{'xsi:type'};
        private String[] fullName_type_info = new String[]{'fullName','http://soap.sforce.com/2006/04/metadata','string','0','1','false'};
        private String[] ccEmails_type_info = new String[]{'ccEmails','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] description_type_info = new String[]{'description','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] protected_x_type_info = new String[]{'protected','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] recipients_type_info = new String[]{'recipients','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] senderAddress_type_info = new String[]{'senderAddress','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] senderType_type_info = new String[]{'senderType','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] template_type_info = new String[]{'template','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'fullname','ccEmails','description','protected_x','recipients','senderAddress','senderType','template'};
    }
    public class DebuggingInfo_element {
        public String debugLog;
        private String[] debugLog_type_info = new String[]{'debugLog','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'debugLog'};
    }
    public class CallOptions_element {
        public String client;
        private String[] client_type_info = new String[]{'client','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'client'};
    }
    public class DebuggingHeader_element {
        public MetadataService.LogInfo[] categories;
        public String debugLevel;
        private String[] categories_type_info = new String[]{'categories','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] debugLevel_type_info = new String[]{'debugLevel','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'categories','debugLevel'};
    }
    public class SessionHeader_element {
        public String sessionId;
        private String[] sessionId_type_info = new String[]{'sessionId','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'sessionId'};
    }
    public class LogInfo {
        public String category;
        public String level;
        private String[] category_type_info = new String[]{'category','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] level_type_info = new String[]{'level','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'category','level'};
    }
    public class listMetadataResponse_element {
        public MetadataService.FileProperties[] result;
        private String[] result_type_info = new String[]{'result','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'result'};
    }
    public class ListMetadataQuery {
        public String folder;
        public String type_x;
        private String[] folder_type_info = new String[]{'folder','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] type_x_type_info = new String[]{'type','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'folder','type_x'};
    }
    public class FileProperties {
        public String createdById;
        public String createdByName;
        public DateTime createdDate;
        public String fileName;
        public String fullName;
        public String id;
        public String lastModifiedById;
        public String lastModifiedByName;
        public DateTime lastModifiedDate;
        public String manageableState;
        public String namespacePrefix;
        public String type_x;
        private String[] createdById_type_info = new String[]{'createdById','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] createdByName_type_info = new String[]{'createdByName','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] createdDate_type_info = new String[]{'createdDate','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] fileName_type_info = new String[]{'fileName','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] fullName_type_info = new String[]{'fullName','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] id_type_info = new String[]{'id','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] lastModifiedById_type_info = new String[]{'lastModifiedById','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] lastModifiedByName_type_info = new String[]{'lastModifiedByName','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] lastModifiedDate_type_info = new String[]{'lastModifiedDate','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] manageableState_type_info = new String[]{'manageableState','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] namespacePrefix_type_info = new String[]{'namespacePrefix','http://soap.sforce.com/2006/04/metadata',null,'0','1','false'};
        private String[] type_x_type_info = new String[]{'type','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'createdById','createdByName','createdDate','fileName','fullName','id','lastModifiedById','lastModifiedByName','lastModifiedDate','manageableState','namespacePrefix','type_x'};
    }
    public class listMetadata_element {
        public MetadataService.ListMetadataQuery[] queries;
        public Double asOfVersion;
        private String[] queries_type_info = new String[]{'queries','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] asOfVersion_type_info = new String[]{'asOfVersion','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'queries','asOfVersion'};
    }
    public class readMetadata_element {
        public String type_x;
        public String[] fullNames;
        private String[] type_x_type_info = new String[]{'type','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] fullNames_type_info = new String[]{'fullNames','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'type_x','fullNames'};
    }
    public class readMetadataResponse_element {
        public MetadataService.ReadResult result;
        private String[] result_type_info = new String[]{'result','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'result'};
    }
    public static MetadataService.MetadataPort createService() {
        partnerSoapSforceCom.Soap sp = new partnerSoapSforceCom.Soap();
//        system.debug('loging in...');
        partnerSoapSforceCom.LoginResult loginResult = sp.login(uid,pwd);
//        system.debug('Login Result:::' +loginResult);

        MetadataService.MetadataPort service = new MetadataService.MetadataPort();
        service.SessionHeader = new MetadataService.SessionHeader_element();
        service.SessionHeader.sessionId = loginResult.sessionId;
//      service.SessionHeader.sessionId = UserInfo.getSessionId();
        return service;    
    }
}

Meanwhile, as per your advice, I am also creating new orgs and importing the whole of your MetadataService.cls into it and check if it that works there.

@afawcett
Copy link
Contributor

Ok, if I get a chance i will try the one you have copied in above with the code your trying to execute, but on first glance it looks like you have the correct changes to your local copy.

@denize
Copy link
Author

denize commented Mar 13, 2014

Thanks Andrew! I just tested it with your complete metadataservice.cls in new Orgs and found the error still occurs. Below is the error I see in my Dev Console.

Incompatible types since an instance of LIST is never an instance of MetadataService.WorkflowAlert

So I believe if you just run my dev console code against your MetadataService.cls that you have in your Org, you will be able to see the issue. Below is my dev console code:

MetadataService.MetadataPort service = MetadataService.createService();

List<MetadataService.ListMetadataQuery> queries = new List<MetadataService.ListMetadataQuery>();
MetadataService.ListMetadataQuery queryEmailAlert = new MetadataService.ListMetadataQuery();
queryEmailAlert.type_x = 'WorkflowAlert';
queries.add(queryEmailAlert);

//get the list of WF Alerts
MetadataService.FileProperties[] properties = service.ListMetaData(queries,30);
String[] listOfWfa;
integer i=0;
for(MetadataService.FileProperties wfafile:properties){
    listOfWfa[i++] = wfafile.fullname;
}

//Read Metadata
MetadataService.WorkflowAlert[] alert = (MetadataService.WorkflowAlert)service.readMetadata

('WorkflowAlert',listOfWfa).getrecords();
for(MetadataService.WorkflowAlert wfa:alert){
        system.debug('description:::'+wfa.description);
        system.debug('From Addrss:::'+wfa.senderAddress);
}
system.debug('Number of Email Alerts:::' +properties.size());

May I request you to kindly have a look into this. Thanks once again.

@afawcett
Copy link
Contributor

Are you getting this error saving the code or running it?

@afawcett
Copy link
Contributor

Ah forget the last question, your running via dev console, its likely a compile issue. I'll take a look for you with my copy of the MetadataService.cls file in my Spring'14 org.

@denize
Copy link
Author

denize commented Mar 13, 2014

Yes Andrew, I am running from Anonymous Exec in Dev Console

@denize
Copy link
Author

denize commented Mar 13, 2014

The MetadataService.cls saved without any issue. The error I get is when I run the other code in anonymous exec in dev console.

@afawcett
Copy link
Contributor

This works for me...

MetadataService.MetadataPort service = MetadataServiceExamples.createService();
String[] listOfWfa = new String[] { 'Account.Test' };
MetadataService.WorkflowAlert[] alert = 
    (MetadataService.WorkflowAlert[]) service.readMetadata('WorkflowAlert',listOfWfa).getrecords();
for(MetadataService.WorkflowAlert wfa:alert){
        system.debug('description:::'+wfa.description);
        system.debug('From Addrss:::'+wfa.senderAddress);
}

Note you cast was incorrect, you need to cast to a list not a single item....

MetadataService.WorkflowAlert[] alert = (MetadataService.WorkflowAlert[])
    service.readMetadata('WorkflowAlert',listOfWfa).getRecords();

@denize
Copy link
Author

denize commented Mar 14, 2014

Thanks very much Andrew. I wouldn't have known that. It's working for me now but only for upto 10 records. If I try to run the dev console code for all the workflow alerts fetched fetched from ListMetadata() it gives the error:

System.CalloutException: Web service callout failed: WebService returned a SOAP Fault: EXCEEDED_ID_LIMIT: record limit reached. cannot submit more than 10 records in this operation faultcode=sf:EXCEEDED_ID_LIMIT faultactor=: Class.MetadataService.MetadataPort.readMetadata: line 71, column 1 AnonymousBlock: line 20, column 1 AnonymousBlock: line 20, column 1

Does this mean this webservice can handle only 10? If yes, is this limit set as a parameter MetadataService.cls? can this be modified or is there any work around you could suggest? The only option that I could think is to split the list of workflow alerts into batches of 10 and call the webservice for each batch until all the batches are processed. But I'm afraid it it will exceed the Callout limit. Can I please know your thoughts on this?

However, thanks so much for bringing this to this extent. This wouldn't have been possible without your guidance. Thanks again!

@afawcett
Copy link
Contributor

Yes this appears to be a documented limit.

http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_readMetadata.htm

Array of full names of the components to read. Limit: 10. You must submit arrays of only one type of component. For example, you could submit an array of 10 custom objects or 10 profiles, but not a mix of both types.

Yes splitting the calls up will get you further, you can make up to 10 outbound calls, since you have used 1 for the list, you have 9 remaining, so 90 possible to retrieve. Your other option to go further is to use Batch Apex, you could pass your list into a Batch Apex class (the start method supports returning a list, then when using Database.executeBatch set the scope size 100, the execute method will pass 100 at a time. chunk this up to 10 api calls per execute call). This way you can get through much more, but requires some Batch Apex programming.

@denize
Copy link
Author

denize commented Mar 15, 2014

Thanks Andrew! All the fullnames I have are for WorkflowAlert and there are 250 of them. So splitting them also didn't help. So seems it's time for me to pick up batch apex :) Will surely try it and post you. Thanks very much Andrew!!!

@afawcett
Copy link
Contributor

FYI. The main release in the repo now support Workflow reading.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants