public inherited sharing class MetadataApiClient {
public static final String NAMED_CREDENTIAL = 'Salesforce_Metadata';
public static final String API_VERSION = '65.0';
private static final String METADATA_NS = 'http://soap.sforce.com/2006/04/metadata';
private static final String SOAP_NS = 'http://schemas.xmlsoap.org/soap/envelope/';
private static final String XSI_NS = 'http://www.w3.org/2001/XMLSchema-instance';
public class MetadataApiException extends Exception {
}
public class SaveResult {
public Boolean created;
public String fullName;
}
public class RetrieveResult {
public Boolean done;
public String id;
public String status;
public String errorMessage;
public Blob zipFile;
}
public class DeployResult {
public Boolean done;
public Boolean success;
public String id;
public String status;
public String errorMessage;
}
public static Dom.XmlNode readMetadata(String metadataType, String fullName) {
String operation =
'' +
'' +
MetadataXml.escape(metadataType) +
'' +
'' +
MetadataXml.escape(fullName) +
'' +
'';
Dom.XmlNode body = send(operation);
Dom.XmlNode records = MetadataXml.findDescendant(body, 'records');
if (
records == null ||
'true'.equalsIgnoreCase(records.getAttributeValue('nil', XSI_NS))
) {
return null;
}
return records;
}
public static SaveResult updateMetadata(
String metadataType,
String metadataChildrenXml
) {
return save('updateMetadata', metadataType, metadataChildrenXml);
}
public static SaveResult upsertMetadata(
String metadataType,
String metadataChildrenXml
) {
return save('upsertMetadata', metadataType, metadataChildrenXml);
}
public static String startRetrieve(String member, String metadataType) {
Dom.XmlNode body = send(
'' +
'' +
API_VERSION +
'true' +
'' +
MetadataXml.escape(member) +
'' +
MetadataXml.escape(metadataType) +
'' +
''
);
String id = MetadataXml.text(
MetadataXml.findDescendant(body, 'result'),
'id'
);
if (String.isBlank(id)) {
throw new MetadataApiException(
'The Metadata API did not return a retrieve job ID.'
);
}
return id;
}
public static RetrieveResult checkRetrieve(String retrieveId) {
Dom.XmlNode body = send(
'' +
MetadataXml.escape(retrieveId) +
'true' +
''
);
Dom.XmlNode resultNode = MetadataXml.findDescendant(body, 'result');
RetrieveResult result = new RetrieveResult();
result.done = MetadataXml.booleanValue(resultNode, 'done');
result.id = MetadataXml.text(resultNode, 'id');
result.status = MetadataXml.text(resultNode, 'status');
result.errorMessage = MetadataXml.text(resultNode, 'errorMessage');
String zipFile = MetadataXml.text(resultNode, 'zipFile');
result.zipFile = String.isBlank(zipFile)
? null
: EncodingUtil.base64Decode(zipFile);
return result;
}
public static String startDeploy(Blob zipFile) {
Dom.XmlNode body = send(
'' +
EncodingUtil.base64Encode(zipFile) +
'' +
'false' +
'false' +
'false' +
'false' +
'false' +
'false' +
'true' +
'true' +
'NoTestRun' +
''
);
String id = MetadataXml.text(
MetadataXml.findDescendant(body, 'result'),
'id'
);
if (String.isBlank(id)) {
throw new MetadataApiException(
'The Metadata API did not return a deployment job ID.'
);
}
return id;
}
public static DeployResult checkDeploy(String deployId) {
Dom.XmlNode body = send(
'' +
MetadataXml.escape(deployId) +
'true' +
''
);
Dom.XmlNode resultNode = MetadataXml.findDescendant(body, 'result');
DeployResult result = new DeployResult();
result.done = MetadataXml.booleanValue(resultNode, 'done');
result.success = MetadataXml.booleanValue(resultNode, 'success');
result.id = MetadataXml.text(resultNode, 'id');
result.status = MetadataXml.text(resultNode, 'status');
result.errorMessage = MetadataXml.text(resultNode, 'errorMessage');
if (String.isBlank(result.errorMessage)) {
Dom.XmlNode problem = MetadataXml.findDescendant(resultNode, 'problem');
result.errorMessage = problem == null ? null : problem.getText();
}
return result;
}
public static String toolingAutoResponseRuleName(Id ruleId) {
HttpRequest request = new HttpRequest();
request.setEndpoint(
'callout:' +
NAMED_CREDENTIAL +
'/services/data/v' +
API_VERSION +
'/tooling/query/?q=' +
EncodingUtil.urlEncode(
'SELECT Id, Name, EntityDefinitionId FROM AutoResponseRule WHERE Id = \'' +
String.escapeSingleQuotes(String.valueOf(ruleId)) +
'\' LIMIT 1',
'UTF-8'
)
);
request.setMethod('GET');
request.setTimeout(120000);
HttpResponse response = new Http().send(request);
if (response.getStatusCode() < 200 || response.getStatusCode() >= 300) {
throw new MetadataApiException(
'Tooling API returned HTTP ' +
response.getStatusCode() +
': ' +
response.getBody()
);
}
Map payload = (Map) JSON.deserializeUntyped(
response.getBody()
);
List