Apex Script-Driven Configuration Guide (FDM)
Abraham Lloyd
This documentation explains how Salesforce org admins can successfully set up Cuneiform® for CRM using our collection of Apex scripts. These scripts perform most of the point-and-click configuration required in a fraction of the time spent declaratively performing the same activities.
APPLIES TO | CUNEIFORM FOR CRM FIELD AND DATA MANAGEMENT
Once installed, Cuneiform for CRM requires some manual configuration to enable profiling against your org’s Salesforce objects. This page contains Apex Scripts that automate most of these activities – reducing configuration time to under ten (10) minutes – vs. the thirty (30) minutes configuration takes manually.
If you’re unfamiliar with how to execute Apex Scripts – please consider completing Salesforce’s Developer Console Basics Trailhead Trail. The trail will teach the skills necessary to execute these scripts.
Please visit our Declarative Configuration guide for step-by-step instructions on setting up your Salesforce org to support Cuneiform for CRM. It includes detailed walkthroughs on creating and securing the Connected App that Cuneiform for CRM uses to profile Salesforce Objects and metadata. Use the guide to review Cuneiform for CRM’s configuration requirements.
Apex Script-Driven Configuration Guide
Complete the activities outlined in the following seven (7) steps to finish your Cuneiform for CRM: Field and Data Management configuration using our Apex configuration scripts. Once you’ve completed the configuration, you can verify it using the Cuneiform for CRM: Control Panel.
Create a Self-Signed Certificate for our Connected App
ONE MINUTE
The APIs Cuneiform for CRM leverages (including Metadata, Tooling, and Query APIs) must be securely accessed. We use a self-signed certificate to accomplish this in alignment with Salesforce security recommendations and best practices.
Expand the element below to open the Apex script for this configuration step.
/**
* ─────────────────────────────────────────────────────────────────────────────────────────────────┐
* Anonymous Apex to create our Self-Signed Certificate
* Actions performed by this script:
*
* * [1:] Verify that our Cuneiform for CRM: Self-Signed Certifciate Exists
* * [2:] If it does not -- then create the certificate
*
* This script was created to run before exercising the Control Panel to validate the installation.
* It automates the configuration steps described in the Cuneiform for Salesforce product documentation
* via https://jira-peernova.atlassian.net/wiki/spaces/CFCPD/pages/2598993921
*
* See https://help.salesforce.com/s/articleView?id=sf.security_keys_creating.htm&type=5
* for details on the necessary object and security permissions required to create self-signed
* certificcates and execute this script successfully.
* ─────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
// Initialize local variables
HttpRequest req;
HttpResponse res;
String retrieveCertificateXML;
String certificateXML;
// Initialize the visual formatting variables
String spacer = ' ';
String bdr = '---------------------------------------------------------------------------------------------------------------------------------';
// Initialize success messages (so we don't have to repeat them)
String successLine1 = spacer + 'Nice work! Please visit';
String successLine2 = spacer + 'https://peernova.link/cuneiform/fdm/setup/step-002';
String successLine3 = spacer + 'to complete the next configuration step: Create the Connected App Permission-Set.';
System.debug(bdr);
System.debug(spacer + 'Cuneiform for CRM: Field and Data Management: Certificate Creation Script');
System.debug(spacer + 'Create the Cuneiform for CRM: Self-Signed Certificate in Your Salesforce Org.');
// Initialize the ConnectedApp details
String certificateYear = String.valueOf(Datetime.now().year() + 2);
// Initialize the XML document used to create the certificate
certificateXML = '<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><env:Header><SessionHeader xmlns="http://soap.sforce.com/2006/04/metadata"><sessionId>{0}</sessionId></SessionHeader></env:Header><env:Body><createMetadata xmlns="http://soap.sforce.com/2006/04/metadata"><metadata xsi:type="Certificate"><fullName>Cuneiform_for_CRM_Self_Signed_Certificate</fullName><caSigned>false</caSigned><encryptedWithPlatformEncryption>false</encryptedWithPlatformEncryption><expirationDate>{1}-01-01T00:00:00.000Z</expirationDate><keySize>4096</keySize><masterLabel>Cuneiform for CRM: Self-Signed Certificate</masterLabel><privateKeyExportable>true</privateKeyExportable></metadata></createMetadata></env:Body></env:Envelope>';
// Initialize the xml document containing the read-request details
retrieveCertificateXML = '<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><env:Header><SessionHeader xmlns="http://soap.sforce.com/2006/04/metadata"><sessionId>{0}</sessionId></SessionHeader></env:Header><env:Body><readMetadata xmlns="http://soap.sforce.com/2006/04/metadata"><type>Certificate</type><fullNames>Cuneiform_for_CRM_Self_Signed_Certificate</fullNames></readMetadata></env:Body></env:Envelope>';
retrieveCertificateXML = String.format(retrieveCertificateXML, new String[] {
UserInfo.getSessionId()
});
// Initialize the httpRequest to verify the certificate exists
req = new HttpRequest();
req.setEndpoint(Url.getOrgDomainUrl().toExternalForm() + '/services/Soap/m/60.0');
req.setMethod('POST');
req.setHeader('Content-Type', 'text/xml');
req.setHeader('SOAPAction', '""');
req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
req.setBody(retrieveCertificateXML);
// Process the httpRequest
res = new Http().send(req);
// Check if the certificate already exists
if (res.getBody().contains('<records xsi:type="Certificate"><fullName>Cuneiform_for_CRM_Self_Signed_Certificate</fullName>')) {
System.debug(bdr);
System.debug(spacer + 'We found a self-signed certificate with the API Name');
System.debug(spacer + '[Cuneiform_for_CRM_Self_Signed_Certificate] in this Salesforce org.');
System.debug(spacer);
System.debug(successLine1);
System.debug(successLine2);
System.debug(successLine3);
System.debug(bdr);
} else {
// Otherwise, let's create the certificate
certificateXML = String.format(certificateXML, new String[] {
UserInfo.getSessionId(),
certificateYear
});
// Apply the certificate details to the request and process it
req.setBody(certificateXML);
// Process the httpRequest
res = new Http().send(req);
// Check the response and audit / output the results to the end-user
if (!res.getBody().contains('<success>true</success>')) {
System.debug(bdr);
System.debug(spacer + 'We were unable to create the Cuneiform for CRM: Self-Signed Certificate. Please review the error response for details.');
System.debug(spacer + 'Metadata API Error Response:');
System.debug(spacer);
System.debug(res.getBody().mid(res.getBody().indexOf('<errors>'), res.getBody().indexOf('</errors>') - (res.getBody().indexOf('<errors>') - 9)));
System.debug(spacer);
System.debug(spacer + 'Please review Metadata API Error Response, and try again. Verify that you have access');
System.debug(spacer + 'and permission to create Metadata via the Metadata API, and that the Cuneiform for CRM');
System.debug(spacer + 'Self-Signed Certificate does not already exist in your Org.');
} else {
System.debug(spacer + 'The Self-Signed Certificate was successfully created.');
System.debug(spacer);
System.debug(successLine1);
System.debug(successLine2);
System.debug(successLine3);
}
System.debug(bdr);
}
Execute this Anonymous Apex script to create the
Cuneiform for CRM: Self-Signed Certificate
. Use our step-by-step guide if you are unfamiliar with executing Apex Scripts via the Salesforce Developer Console.
Create the Cuneiform for CRM: Connected App Permission Set
ONE MINUTE
Cuneiform for CRM requires a Connected App to access Salesforce APIs securely. To manage access to our Connected App, we’ll create a permission set named Cuneiform for CRM: Connected App Assignment
. Use this permission set to provide profiling API access to Salesforce users.
Expand the element below to open the Apex script for this configuration step.
/**
* ─────────────────────────────────────────────────────────────────────────────────────────────────┐
* Anonymous Apex to create our Connected App Permission-Set
* Actions performed by this script:
*
* * [1:] Verify that our Connected App permission-set exists
* * [2:] If it does not -- then create the Connected App permission-set
*
* This script was created to run before exercising the Control Panel to validate the installation.
* It automates the configuration steps described in the Cuneiform for Salesforce product documentation
* via https://jira-peernova.atlassian.net/wiki/spaces/CFCPD/pages/2596798483/
*
* See https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_permissionset.htm
* for details on the necessary object and security permissions required to create a permission-set
* and execute this script successfully.
* ─────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
// Initialize local variables
List<PermissionSet> adminPermSet;
String adminPermSetApiName;
List<PermissionSet> permSetValidation;
String permSetLabel;
String permSetApiName;
String permSetDesc;
Boolean permSetSessionActivationRequired;
PermissionSet permSet;
Boolean exceptionOccurred;
DmlException caughtException;
// Default constants we use in our script for layout and testing / debugging purposes
Boolean testException = false;
String spacer = ' ';
String border = '---------------------------------------------------------------------------------------------------------------------------------';
String testExceptionMessage = 'This is a test exception to verify that the script can handle exceptions.';
// Initialize success messages (so we don't have to repeat them)
String successLine1 = spacer + 'Nice work! Please visit';
String successLine2 = spacer + 'https://peernova.link/cuneiform/fdm/setup/step-003';
String successLine3 = spacer + 'to complete the next configuration step: Create the Administrative Permission-Set Group.';
// Initialize script constants (do not change these values)
adminPermSetApiName = 'Cuneiform_for_CRM_Administrative_User';
permSetLabel = 'Cuneiform for CRM: Connected App Assignment';
permSetApiName = 'Cuneiform_for_CRM_Connected_App_Assignment';
permSetDesc = 'This permission set supports administrative access to PeerNova\'s Cuneiform for CRM application. It provides users access to the Connected App that brokers Salesforce API requests.';
permSetSessionActivationRequired = false;
System.debug(border);
System.debug(spacer + 'Cuneiform for CRM: Field and Data Management: Configuration Apex Script');
System.debug(spacer + '1. Verify that the Connected App Permission-Set exists.');
// First, let's verify that our Administrative Permission-Set exists (verifying we're installed)
adminPermSet = [SELECT Id FROM PermissionSet WHERE Name = :adminPermSetApiName];
// Was a valid Permission Set found?
if (adminPermSet.size() == 0) {
// Output that the permission-set was not found -- and needs to be verified
System.debug(border);
System.debug(spacer + 'We could not verify your Cuneiform for CRM: Field and Data Management installation.');
System.debug(border);
System.debug(spacer + 'The Administrative Permission-Set "' + adminPermSetApiName + '" was not found.');
System.debug(spacer + 'Please ensure that Cuneiform for CRM is installed and that you have system administrator');
System.debug(spacer + 'level access to complete the installation.');
System.debug(spacer);
System.debug(spacer + 'You can install Cuneiform for CRM by opening our appexchange listing at');
System.debug(spacer + 'https://peernova.link/cuneiform/install/fdm/appexchange');
System.debug(spacer + 'Click on the "Get It Now" button to install Cuneiform for CRM: Field and Data Management.');
System.debug(border);
} else {
// First, let's verify that our Connected App permission-set exists
permSetValidation = [SELECT Id FROM PermissionSet WHERE Name = :permSetApiName];
// Was a valid Permission Set found?
if (permSetValidation.size() <> 0) {
// Output that the permission-set was verified and already exists
System.debug(border);
System.debug(spacer + 'The Permission-Set "' + permSetApiName + '" was successfully verified.');
System.debug(spacer);
System.debug(successLine1);
System.debug(successLine2);
System.debug(successLine3);
System.debug(border);
// Otherwise, let's create one
} else {
// If not -- then let's create one
permSet = new PermissionSet();
// Default the permission-set with the values we've defined
permSet.Name = permSetApiName;
permSet.Label = permSetLabel;
permSet.Description = permSetDesc;
permSet.HasActivationRequired = permSetSessionActivationRequired;
// Create the permission-set
try {
// Test to see if we should throw an exception
if (testException) {
throw new DmlException(testExceptionMessage);
// Otherwise, insert the permission-set
} else {
insert permSet;
exceptionOccurred = false;
}
} catch (DmlException e) {
caughtException = e;
exceptionOccurred = true;
}
// Output that the permission-set was verified and already exists
System.debug(border);
System.debug(spacer + 'The Permission-Set "' + permSetApiName + '" was not found; attempting to create it.');
// Did an exception occur?
if (exceptionOccurred) {
System.debug(spacer + 'An error occurred while attempting to create the Permission-Set [' + permSetApiName + '].');
System.debug(spacer + 'Error Message: ' + caughtException.getMessage());
System.debug(border);
System.debug(spacer + caughtException.getStackTraceString());
System.debug(border);
System.debug(spacer + 'DML exceptions often occur because of rights or access permissions. Please verify that');
System.debug(spacer + 'the user executing this script has the necessary rights to create a permission-set.');
System.debug(spacer + 'Try executing this script again once you have addressed the root cause of this exception.');
} else {
System.debug(spacer + 'The Permission-Set "' + permSetApiName + '" was successfully created.');
System.debug(spacer);
System.debug(successLine1);
System.debug(successLine2);
System.debug(successLine3);
}
System.debug(border);
}
}
Execute this Anonymous Apex script to create the
Cuneiform for CRM: Connected App Assignment
permission set. Use our step-by-step guide if you are unfamiliar with executing Apex Scripts via the Salesforce Developer Console.
Create the Cuneiform for CRM: Administrators Permission Set Group
ONE MINUTE
To simplify access management to Cuneiform for CRM, we’ll create a permission set group named Cuneiform for CRM: Administrators
to manage profiling access for Salesforce org users. This group will manage all permission-set and end-user assignments representing Cuneiform for CRM: Administrators.
Expand the element below to open the Apex script for this configuration step.
/**
* ─────────────────────────────────────────────────────────────────────────────────────────────────┐
* Anonymous Apex to create our Administrative Permission-Set Group
* Actions performed by this script:
*
* * [1:] Verify that our Administrative Permission-Set Group exists
* * [2:] If it does not -- then create the Administrative Permission-Set Group
* * [3:] Associate the Connected App Permission-Set with the Administrative Permission-Set Group
*
* This script was created to run before exercising the Control Panel to validate the installation.
* It automates the configuration steps described in the Cuneiform for Salesforce product documentation
* via https://jira-peernova.atlassian.net/wiki/spaces/CFCPD/pages/2596798483/
*
* See https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_permissionsetgroup.htm
* and https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_permissionsetgroupcomponent.htm
* for details on the necessary object and security permissions required to create a permission-set group,
* associate a permission-set, and and execute this script successfully.
* ─────────────────────────────────────────────────────────────────────────────────────────────────┘
*/
// Initialize local variables
String permSetGroupLabel;
String permSetGroupApiName;
String permSetGroupDesc;
Boolean permSetGroupSessionActivationRequired;
List<PermissionSet> adminPermSet;
String adminPermSetApiName;
List<PermissionSetGroup> adminPermSetGroup;
PermissionSetGroup permSetGroup;
Boolean exceptionOccurred;
DmlException caughtException;
// Default constants we use in our script for layout and testing / debugging purposes
Boolean testException = false;
String spacer = ' ';
String border = '---------------------------------------------------------------------------------------------------------------------------------';
String testExceptionMessage = 'This is a test exception to verify that the script can handle exceptions.';
// Initialize success messages (so we don't have to repeat them)
String successLine1 = spacer + 'Nice work! Please visit';
String successLine2 = spacer + 'https://peernova.link/cuneiform/fdm/setup/step-004';
String successLine3 = spacer + 'to complete the next permission-set group configuration step: Assign Permission-Sets and Users.';
// Initialize local variables (do not change these values)
adminPermSetApiName = 'Cuneiform_for_CRM_Administrative_User';
permSetGroupLabel = 'Cuneiform for CRM: Administrators';
permSetGroupApiName = 'Cuneiform_for_CRM_Administrators';
permSetGroupDesc = 'This permission set group manages the collection of permission sets required to support administrative access to Cuneiform for CRM. Org Admins can assign this group to users that should have profiling capabilities within their Salesforce Org.';
permSetGroupSessionActivationRequired = false;
System.debug(border);
System.debug(spacer + 'Cuneiform for CRM: Field and Data Management: Configuration Apex Script');
System.debug(spacer + '2. Verify that the Administrative Permission-Set Group exists.');
// First, let's verify that our Administrative Permission-Set exists (verifying we're installed)
adminPermSet = [SELECT Id FROM PermissionSet WHERE Name = :adminPermSetApiName];
// Was a valid Permission Set found?
if (adminPermSet.size() == 0) {
// Output that the permission-set was not found -- and needs to be verified
System.debug(border);
System.debug(spacer + 'The Administrative Permission-Set "' + adminPermSetApiName + '" was not found.');
System.debug(spacer + 'Please ensure that Cuneiform for CRM is installed and that you have system administrator');
System.debug(spacer + 'level access to complete the installation.');
System.debug(spacer);
System.debug(spacer + 'You can install Cuneiform for CRM by opening our appexchange listing at');
System.debug(spacer + 'https://peernova.link/cuneiform/install/fdm/appexchange');
System.debug(spacer + 'Click on the "Get It Now" button to install Cuneiform for CRM: Field and Data Management.');
System.debug(border);
} else {
// Next, let's see if our Permission-Set Group exists
adminPermSetGroup = [SELECT Id FROM PermissionSetGroup WHERE DeveloperName = :permSetGroupApiName];
// Was the administrative permission-set group found?
if (adminPermSetGroup.size() > 0) {
// If so, output that the permission-set group and its configuration was verified
System.debug(border);
System.debug(spacer + 'The Permission-Set Group [' + permSetGroupApiName + '] was successfully verified.');
System.debug(spacer);
System.debug(successLine1);
System.debug(successLine2);
System.debug(successLine3);
System.debug(border);
} else {
// If not -- then let's create one
permSetGroup = new PermissionSetGroup();
// Default the permission-set with the values we've defined
permSetGroup.DeveloperName = permSetGroupApiName;
permSetGroup.MasterLabel = permSetGroupLabel;
permSetGroup.Description = permSetGroupDesc;
permSetGroup.HasActivationRequired = permSetGroupSessionActivationRequired;
// Create the permission-set
try {
// Test to see if we should throw an exception
if (testException) {
throw new DmlException(testExceptionMessage);
// Otherwise, insert the permission-set
} else {
// Insert the permission-set
insert permSetGroup;
// Audit that no exceptions took place
exceptionOccurred = false;
}
} catch (DmlException e) {
caughtException = e;
exceptionOccurred = true;
}
// Output that the permission-set was verified and already exists
System.debug(border);
System.debug(' The Permission-Set Group [' + permSetGroupApiName + '] was not found; attempting to create it.');
if (exceptionOccurred) {
System.debug(spacer + 'An error occurred while attempting to create the Permission-Set Group [' + permSetGroupApiName + '].');
System.debug(spacer + 'Error Message: ' + caughtException.getMessage());
System.debug(border);
System.debug(spacer + caughtException.getStackTraceString());
System.debug(border);
System.debug(spacer + 'DML exceptions often occur because of rights or access permissions. Please verify that');
System.debug(spacer + 'the user executing this script has the necessary rights to create a permission-set.');
System.debug(spacer + 'Try executing this script again once you have addressed the root cause of this exception.');
} else {
System.debug(spacer + 'The Permission-Set Group [' + permSetGroupApiName + '] was successfully created.');
System.debug(spacer);
System.debug(successLine1);
System.debug(successLine2);
System.debug(successLine3);
}
System.debug(border);
}
}
Execute this Anonymous Apex script to create the
Cuneiform for CRM: Administrators
permission-set group. Use our step-by-step guide if you are unfamiliar with executing Apex Scripts via the Salesforce Developer Console.
Assign Permission Sets to the Administrator Permission Set Group
ONE MINUTE
Once the Cuneiform for CRM: Administrators
permission-set group is created, we'll associate the Cuneiform for CRM: Administrative User
and Cuneiform for CRM: Connected App Assignment
permission sets.
Expand the element below to open the Apex script for this configuration step.
// Initialize local variables
String permSetGroupApiName;
List<PermissionSet> adminPermSet;
String adminPermSetApiName;
List<PermissionSet> connectedAppPermSet;
String connectedAppPermSetApiName;
List<PermissionSetGroup> adminPermSetGroup;
List<PermissionSetGroupComponent> adminPermSetGroupComponents;
List<PermissionSetGroupComponent> permSetGroupComponents;
List<Id> adminPermSetIds;
List<Id> sourcePermSetIds;
List<Id> targetPermSetIds;
Boolean exceptionOccurred;
DmlException caughtException;
Boolean hasUser;
// Default constants we use in our script for layout and testing / debugging purposes
Boolean testException = false;
String spacer = ' ';
String bdr = '---------------------------------------------------------------------------------------------------------------------------------';
String testExceptionMessage = 'This is a test exception.';
// Initialize local variables (do not change these values)
adminPermSetApiName = 'Cuneiform_for_CRM_Administrative_User';
connectedAppPermSetApiName = 'Cuneiform_for_CRM_Connected_App_Assignment';
permSetGroupApiName = 'Cuneiform_for_CRM_Administrators';
hasUser = false;
System.debug(bdr);
System.debug(spacer + 'Cuneiform for CRM: Field and Data Management: Configuration Apex Script');
System.debug(spacer + '3. Verify that the Administrative Group\'s required permission-set assignments exist.');
// First, let's verify that our Administrative Permission-Set exists (verifying we're installed)
adminPermSet = [SELECT Id FROM PermissionSet WHERE Name = :adminPermSetApiName];
// Was a valid Permission Set found?
if (adminPermSet.size() == 0) {
// Output that the permission-set was not found -- and needs to be verified
System.debug(bdr);
System.debug(spacer + 'The Administrative Permission-Set "' + adminPermSetApiName + '" was not found.');
System.debug(spacer + 'Please ensure that Cuneiform for CRM is installed and that you have admin-level access.');
System.debug(spacer);
System.debug(spacer + 'You can install Cuneiform for CRM by opening our appexchange listing at');
System.debug(spacer + 'https://peernova.link/cuneiform/install/fdm/appexchange');
System.debug(spacer + 'Click on the "Get It Now" button to install Cuneiform for CRM: Field and Data Management.');
System.debug(bdr);
} else {
// Next, let's see if our Permission-Set Group and ConnectedApp permission-set exists
adminPermSetGroup = [SELECT Id FROM PermissionSetGroup WHERE DeveloperName = :permSetGroupApiName];
connectedAppPermSet = [SELECT Id FROM PermissionSet WHERE Name = :connectedAppPermSetApiName];
// Was the administrative permission-set group found?
if (adminPermSetGroup.size() == 0 || connectedAppPermSet.size() == 0) {
if (adminPermSetGroup.size() == 0) {
System.debug(bdr);
System.debug(spacer + 'The Permission-Set Group "' + permSetGroupApiName + '" was not found.');
System.debug(bdr);
}
if (connectedAppPermSet.size() == 0) {
System.debug(bdr);
System.debug(spacer + 'The Connected App permission-set "' + connectedAppPermSetApiName + '" was not found.');
System.debug(bdr);
}
} else {
// Create the source permission-set collection and add the connected-app / admin ids
sourcePermSetIds = new List<Id>();
sourcePermSetIds.add(connectedAppPermSet[0].Id);
sourcePermSetIds.add(adminPermSet[0].Id);
// Retrieve the permission-set group assignments -- so we can verify the connected app and admin permission-sets
adminPermSetGroupComponents = [SELECT Id, PermissionSetId FROM PermissionSetGroupComponent WHERE PermissionSetGroupId = :adminPermSetGroup[0].Id AND PermissionSetId IN :sourcePermSetIds];
// Initialize the Id list
adminPermSetIds = new List<Id>();
// Create a list of the permission-set Ids that are already associated with the administrative permission-set group
for (PermissionSetGroupComponent thisComponent: adminPermSetGroupComponents) {
adminPermSetIds.add(thisComponent.PermissionSetId);
}
targetPermSetIds = new List<Id>();
// Now, which permission-sets are missing from the administrative permission-set group?
for (Id sourceId: sourcePermSetIds) {
if (adminPermSetIds.contains(sourceId) == false) {
targetPermSetIds.add(sourceId);
}
}
// If we have permission-sets to add -- then let's add them
if (targetPermSetIds.size() != 0) {
// If not -- then let's back-fill the missing associations
permSetGroupComponents = new List<PermissionSetGroupComponent>();
// Create component records for the missing associations
for (Id thisTargetId : targetPermSetIds) {
permSetGroupComponents.add(
new PermissionSetGroupComponent(
PermissionSetId = thisTargetId,
PermissionSetGroupId = adminPermSetGroup[0].Id
)
);
}
}
// Create the permission-set
try {
// Test to see if we should throw an exception
if (testException) {
throw new DmlException(testExceptionMessage);
// Otherwise, insert the permission-set
} else {
// Insert the permission-set group permission-set assignments (if necessary)
if (permSetGroupComponents != null) {
insert permSetGroupComponents;
}
// Audit that no exceptions took place
exceptionOccurred = false;
}
} catch (DmlException e) {
caughtException = e;
exceptionOccurred = true;
}
// Output that the permission-set was verified and already exists
System.debug(bdr);
if (permSetGroupComponents == null) {
System.debug(spacer + 'The permission-set assignments for the Group [' + permSetGroupApiName + '] were successfully verified.');
} else {
System.debug(spacer + 'The permission-set assignments for the Group [' + permSetGroupApiName + '] were not found.');
}
if (exceptionOccurred) {
System.debug(spacer + 'An error occurred while attempting to create the permission-set assignments for the Group [' + permSetGroupApiName + '].');
System.debug(spacer + 'Error Message: ' + caughtException.getMessage());
System.debug(bdr);
System.debug(spacer + caughtException.getStackTraceString());
System.debug(bdr);
System.debug(spacer + 'DML exceptions often occur because of rights or access permissions. Please verify that');
System.debug(spacer + 'the user executing this script has the necessary rights to create a permission-set.');
System.debug(spacer + 'Try executing this script again once you have addressed the root cause of this exception.');
} else {
if (permSetGroupComponents != null) {
System.debug(spacer + 'The missing permission-set assignments were successfully created.');
}
System.debug(spacer);
System.debug(spacer + 'Nice work! Please visit');
System.debug(spacer + 'https://peernova.link/cuneiform/fdm/setup/step-005');
System.debug(spacer + 'to complete the next permission-set configuration step: Assigning-Users.');
}
System.debug(bdr);
}
}
Execute this Anonymous Apex script to create these permission set assignments. Use our step-by-step guide if you are unfamiliar with executing Apex Scripts via the Salesforce Developer Console.
Assign Your User to the Administrator Permission Set Group
ONE MINUTE
Once the Cuneiform for CRM: Administrators
permission-set group is created, we'll associate your user with it, providing you administrative access to Cuneiform for CRM.
Expand the element below to open the Apex script for this configuration step.
// Initialize local variables
String permSetGroupApiName;
List<PermissionSet> adminPermSet;
String adminPermSetApiName;
List<PermissionSetGroup> adminPermSetGroup;
List<PermissionSetAssignment> adminUserAssignment;
Boolean exceptionOccurred;
DmlException caughtException;
Boolean hasPermissionSets;
Boolean hasUser;
// Default constants we use in our script for layout and testing / debugging purposes
Boolean testException = false;
String spacer = ' ';
String bdr = '---------------------------------------------------------------------------------------------------------------------------------';
String testExceptionMessage = 'This is a test exception.';
// Initialize local variables (do not change these values)
adminPermSetApiName = 'Cuneiform_for_CRM_Administrative_User';
permSetGroupApiName = 'Cuneiform_for_CRM_Administrators';
hasPermissionSets = false;
hasUser = false;
System.debug(bdr);
System.debug(spacer + 'Cuneiform for CRM: Field and Data Management: Configuration Apex Script');
System.debug(spacer + '4. Provide User Access to the Administrative Permission-Set Group.');
// First, let's verify that our Administrative Permission-Set exists (verifying we're installed)
adminPermSet = [SELECT Id FROM PermissionSet WHERE Name = :adminPermSetApiName];
// Was a valid Permission Set found?
if (adminPermSet.size() == 0) {
// Output that the permission-set was not found -- and needs to be verified
System.debug(bdr);
System.debug(spacer + 'The Administrative Permission-Set "' + adminPermSetApiName + '" was not found.');
System.debug(spacer + 'Please ensure that Cuneiform for CRM is installed and that you have admin-level access.');
System.debug(spacer);
System.debug(spacer + 'You can install Cuneiform for CRM by opening our appexchange listing at');
System.debug(spacer + 'https://peernova.link/cuneiform/install/fdm/appexchange');
System.debug(spacer + 'Click on the "Get It Now" button to install Cuneiform for CRM: Field and Data Management.');
System.debug(bdr);
} else {
// Next, let's see if our Permission-Set Group and ConnectedApp permission-set exists
adminPermSetGroup = [SELECT Id FROM PermissionSetGroup WHERE DeveloperName = :permSetGroupApiName];
// Audit if we couldn't find the administrative permission-set group
if (adminPermSetGroup.size() == 0) {
System.debug(bdr);
System.debug(spacer + 'The Permission-Set Group "' + permSetGroupApiName + '" was not found.');
System.debug(bdr);
} else {
// Verify that the current user is associated with the administrative permission-set group
adminUserAssignment = [SELECT Id FROM PermissionSetAssignment WHERE AssigneeId = :UserInfo.getUserId() AND PermissionSetGroupId = :adminPermSetGroup[0].Id];
if (adminUserAssignment.size() != 0) {
System.debug(bdr);
System.debug(spacer + 'The current user is already assigned to the Permission-Set Group "' + permSetGroupApiName + '".');
System.debug(spacer);
System.debug(spacer + 'Nice work! Please visit');
System.debug(spacer + 'https://peernova.link/cuneiform/fdm/setup/step-006');
System.debug(spacer + 'to complete the next configuration step: Creating the Connected App.');
System.debug(bdr);
} else {
// Create the permission-set assignment
try {
// Test to see if we should throw an exception
if (testException) {
throw new DmlException(testExceptionMessage);
} else {
// Create the permission-set assignment
insert new PermissionSetAssignment(
AssigneeId = UserInfo.getUserId(),
PermissionSetGroupId = adminPermSetGroup[0].Id
);
// Audit that no exceptions took place
exceptionOccurred = false;
}
} catch (DmlException e) {
caughtException = e;
exceptionOccurred = true;
}
// Output that the permission-set was verified and already exists
System.debug(bdr);
if (exceptionOccurred) {
System.debug(spacer + 'An error occurred while attempting to create the permission-set assignment for the Group [' + permSetGroupApiName + '].');
System.debug(spacer + 'Error Message: ' + caughtException.getMessage());
System.debug(bdr);
System.debug(spacer + caughtException.getStackTraceString());
System.debug(bdr);
System.debug(spacer + 'DML exceptions often occur because of rights or access permissions. Please verify that');
System.debug(spacer + 'the user executing this script has the necessary rights to create a permission-set.');
System.debug(spacer + 'Try executing this script again once you have addressed the root cause of this exception.');
} else {
System.debug(spacer + 'The current user was successfully assigned to the Permission-Set Group "' + permSetGroupApiName + '".');
System.debug(spacer);
System.debug(spacer + 'Nice work! Please visit');
System.debug(spacer + 'https://peernova.link/cuneiform/fdm/setup/step-006');
System.debug(spacer + 'to complete the next configuration step: Creating the Connected App.');
}
System.debug(bdr);
}
}
}
Execute this Anonymous Apex script to create your user assignment. Use our step-by-step guide if you are unfamiliar with executing Apex Scripts via the Salesforce Developer Console.
Create the Cuneiform for CRM: Connected App and Update the Configuration Profile
ONE MINUTE
The most complex and critical parts of configuring Cuneiform for CRM are creating the Connected App to securely access Salesforce APIs and updating the configuration profile to include the certificate and Connected App details.
Expand the element below to open the Apex script for this configuration step.
// Initialize local variables
HttpRequest req;
HttpResponse res;
String connectedAppXML;
String appConsumerKey;
// Initialize the visual formatting variables
String spacer = ' ';
String bdr = '---------------------------------------------------------------------------------------------------------------------------------';
System.debug(bdr);
System.debug(spacer + 'Cuneiform for CRM: Field and Data Management: Configuration Apex Script');
System.debug(spacer + '5. Create the Cuneiform for CRM: Connected App in Your Salesforce Org.');
// Initialize the ConnectedApp details
String selfSignedCertificateApiName = 'Cuneiform_for_CRM_Self_Signed_Certificate';
String appApiName = 'Cuneiform_for_CRM_Connected_App';
String appLabel = 'Cuneiform for CRM: Connected App';
String appEmail = 'cuneiform.sf@peernova.com';
String appPermSet = 'Cuneiform for CRM: Connected App Assignment';
String appDescription = 'This Connected App is leveraged by the PeerNova: Cuneiform for CRM application to provide secure access to Salesforce REST APIs. We leverage REST APIs to monitor and profile data and metadata in this org.';
String appIconUrl = 'https://lh6.googleusercontent.com/3b6tVui747u3ty6d98JfkXTJCF5unEj6W2oOMs-upvatI0-ydNTA3k1b6Z1cJZOXQ8A=w2400';
String appLogoUrl = 'https://lh6.googleusercontent.com/ykSeeKgrxYndJj37ia2PoGx9-czBfgSk4jsTpYfUKG96l9k-Bp-NTfhdsNxyyC4hyIQ=w2400';
// Initialize local variables
List<String> idSegments;
Long epochTime;
String epochTimeString;
String orgId;
String output;
Double reverseCalc;
// Initialize the idList
idSegments = new List<String>();
// First, let's retrieve epochTime for today
epochTime = Datetime.now().getTime();
epochTimeString = String.valueOf(epochTime);
// Next, let's retrieve the orgId for this Org
orgId = [SELECT Id FROM Organization LIMIT 1][0].Id;
// Next, build-out the appConsumerKey for our ConnectedApp
reverseCalc = Math.random();
if (reverseCalc < 0.5) {
idSegments.add(epochTimeString.substring(0, 2).reverse());
} else {
idSegments.add(epochTimeString.substring(0, 2));
}
idSegments.add(EncodingUtil.convertToHex(Crypto.generateAesKey(256)).substring(6));
idSegments.add(orgId.substring(0, 3));
// Section Two of the ConnectedApp's Consumer Key
reverseCalc = Math.random();
if (reverseCalc < 0.5) {
idSegments.add(epochTimeString.substring(3, 5).reverse());
} else {
idSegments.add(epochTimeString.substring(3, 5));
}
idSegments.add(EncodingUtil.convertToHex(Crypto.generateAesKey(256)).substring(6));
idSegments.add(orgId.substring(4, 10));
// Section Three of the ConnectedApp's Consumer Key
reverseCalc = Math.random();
if (reverseCalc < 0.5) {
idSegments.add(epochTimeString.substring(6, 8).reverse());
} else {
idSegments.add(epochTimeString.substring(6, 8));
}
idSegments.add(orgId.substring(11, 13));
idSegments.add(EncodingUtil.convertToHex(Crypto.generateAesKey(256)).substring(6));
// Section Four of the ConnectedApp's Consumer Key
reverseCalc = Math.random();
if (reverseCalc < 0.5) {
idSegments.add(epochTimeString.substring(9, 12).reverse());
} else {
idSegments.add(epochTimeString.substring(9, 12));
}
idSegments.add(orgId.substring(14, 18));
idSegments.add(EncodingUtil.convertToHex(Crypto.generateAesKey(256)).substring(6));
// Join the shuffled elements into a single-string
output = String.join(idSegments, '');
// Automatically trim this string down to 255 characters maximum
appConsumerKey = output.substring(0, 255);
// Initialize the xml document containing our connectedApp metadata
connectedAppXML = '<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><env:Header><SessionHeader xmlns="http://soap.sforce.com/2006/04/metadata"><sessionId>{0}</sessionId></SessionHeader></env:Header><env:Body><createMetadata xmlns="http://soap.sforce.com/2006/04/metadata"><metadata xsi:type="ConnectedApp"><fullName>{1}</fullName><label>{2}</label><contactEmail>{3}</contactEmail><description>{4}</description><iconUrl>{5}</iconUrl><logoUrl>{6}</logoUrl><oauthConfig><callbackUrl>https://cuneiform-crm.peernova.io</callbackUrl><consumerKey>{7}</consumerKey><isAdminApproved>true</isAdminApproved><isClientCredentialEnabled>false</isClientCredentialEnabled><isConsumerSecretOptional>false</isConsumerSecretOptional><isPkceRequired>true</isPkceRequired><isIntrospectAllTokens>false</isIntrospectAllTokens><isNamedUserJwtEnabled>false</isNamedUserJwtEnabled><isSecretRequiredForRefreshToken>true</isSecretRequiredForRefreshToken><oauthClientCredentialUser /><scopes>Api</scopes><scopes>Full</scopes><scopes>RefreshToken</scopes></oauthConfig><oauthPolicy><ipRelaxation>ENFORCE</ipRelaxation><refreshTokenPolicy>specific_lifetime:2:HOURS</refreshTokenPolicy></oauthPolicy><permissionSetName>{8}</permissionSetName><sessionPolicy><sessionTimeout>15</sessionTimeout></sessionPolicy></metadata></createMetadata></env:Body></env:Envelope>';
connectedAppXML = String.format(connectedAppXML, new String[] {
UserInfo.getSessionId(),
appApiName,
appLabel,
appEmail,
appDescription,
appIconUrl,
appLogoUrl,
appConsumerKey,
appPermSet
});
// Initialize the httpRequest to create the ConnectedApp
req = new HttpRequest();
req.setEndpoint(Url.getOrgDomainUrl().toExternalForm() + '/services/Soap/m/60.0');
req.setMethod('GET');
req.setHeader('Content-Type', 'text/xml');
req.setHeader('SOAPAction', '""');
req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
req.setBody(connectedAppXML);
// Process the httpRequest
res = new Http().send(req);
// Check the response and audit / output the results to the end-user
if (!res.getBody().contains('<success>true</success>')) {
System.debug(bdr);
System.debug(spacer + 'We were unable to create the Cuneiform for CRM: Connected App. Please review the error response for details.');
System.debug(spacer + 'Metadata API Error Response:');
System.debug(spacer);
System.debug(res.getBody().mid(res.getBody().indexOf('<errors>'), res.getBody().indexOf('</errors>') - (res.getBody().indexOf('<errors>') - 9)));
System.debug(spacer);
System.debug(spacer + 'Please review Metadata API Error Response, and try again. Verify that you have access');
System.debug(spacer + 'and permission to create Metadata via the Metadata API, and that the Cuneiform for CRM');
System.debug(spacer + 'Connected App does not already exist in your Org.');
System.debug(bdr);
} else {
// Initialize the metadata container properties
Metadata.DeployContainer mdContainer = new Metadata.DeployContainer();
Metadata.CustomMetadata configProfileRecord = new Metadata.CustomMetadata();
// Create the Configuration Wrapper to get the Active Metadata Used for the Environment
pnova__Active_Configuration__mdt activeConfigProfile = [SELECT Id, pnova__Active_Configuration_Profile__r.DeveloperName FROM pnova__Active_Configuration__mdt WHERE DeveloperName = 'Active_Configuration' LIMIT 1];
pnova__Configuration_Profile__mdt configProfile = [SELECT Id, DeveloperName, pnova__Certificate_Unique_Name__c, pnova__ConnectedApp_Label__c, pnova__ConnectedApp_Consumer_Key__c FROM pnova__Configuration_Profile__mdt WHERE DeveloperName = :activeConfigProfile.pnova__Active_Configuration_Profile__r.DeveloperName LIMIT 1];
// Initialize the name of the metadata we're going to process
configProfileRecord.fullName = 'pnova__Configuration_Profile__mdt.pnova__' + configProfile.DeveloperName;
configProfileRecord.label = 'Production';
Metadata.CustomMetadataValue connectedAppLabelField = new Metadata.CustomMetadataValue();
connectedAppLabelField.field = 'pnova__ConnectedApp_Label__c';
connectedAppLabelField.value = appLabel;
configProfileRecord.values.add(connectedAppLabelField);
Metadata.CustomMetadataValue consumerKeyField = new Metadata.CustomMetadataValue();
consumerKeyField.field = 'pnova__ConnectedApp_Consumer_Key__c';
consumerKeyField.value = appConsumerKey;
configProfileRecord.values.add(consumerKeyField);
Metadata.CustomMetadataValue certApiNameField = new Metadata.CustomMetadataValue();
certApiNameField.field = 'pnova__Certificate_Unique_Name__c';
certApiNameField.value = selfSignedCertificateApiName;
configProfileRecord.values.add(certApiNameField);
// Add the metadata to the container -- and process it
mdContainer.addMetadata(configProfileRecord);
Id jobId = Metadata.Operations.enqueueDeployment(mdContainer, null);
System.debug(bdr);
System.debug(spacer + 'The Cuneiform for CRM: Connected App was successfully created.');
System.debug(spacer + '-- see JobId: ' + jobId + ' for metadata deployment details.');
System.debug(spacer);
System.debug(spacer + 'Nice Work! We have updated the Configuration Profile with the Connected App details.');
System.debug(spacer);
System.debug(spacer + 'Please follow our remaining installation instructions via');
System.debug(spacer + 'https://peernova.link/cuneiform/fdm/setup/step-007');
System.debug(spacer + 'to enable digital signatures for your Connected App (the final step).');
System.debug(bdr);
}
Execute this Anonymous Apex script to create the Connected App. Use our step-by-step guide if you are unfamiliar with executing Apex Scripts via the Salesforce Developer Console.
Enable Digital Signatures in the Cuneiform for CRM: Connected App
ONE MINUTE
As the final configuration step – we need to configure our Connected App to use digital signatures. Use this script to automate assigning the self-signed certificate to the Connected App. Once this step is complete, you can verify your configuration via our Control Panel.
Expand the element below to open the Apex script for this configuration step.
// Initialize local variables
HttpRequest req;
HttpResponse res;
String headerXML;
String updateHeaderXML;
String closeXML;
String sessionHeaderXML;
String connectedAppXML;
String certificateXML;
String retrieveCertificateXML;
String retrieveConnectedAppXML;
String updateConnectedAppXML;
String updateContainerXML;
String digitalCertificateXML;
String updateResponseXML;
String certContent;
// Default the ConnectedApp Name
String connectedAppName = 'Cuneiform_for_CRM_Connected_App';
// Initialize the visual formatting variables
String spacer = ' ';
String bdr = '---------------------------------------------------------------------------------------------------------------------------------';
System.debug(bdr);
System.debug(spacer + 'Cuneiform for CRM: Field and Data Management: Configuration Apex Script');
System.debug(spacer + '6. Associate Your Self-Signed Certificate to the Connected App.');
// Initialize the xml document containing the read-request details
headerXML = '<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="http://soap.sforce.com/2006/04/metadata">';
updateHeaderXML = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://soap.sforce.com/2006/04/metadata">';
sessionHeaderXML = '<soapenv:Header><tns:SessionHeader><tns:sessionId>{0}</tns:sessionId></tns:SessionHeader></soapenv:Header><soapenv:Body>';
retrieveCertificateXML = '<tns:readMetadata><type>Certificate</type><fullNames>Cuneiform_for_CRM_Self_Signed_Certificate</fullNames></tns:readMetadata>';
retrieveConnectedAppXML = '<tns:readMetadata><type>ConnectedApp</type><fullNames>' + connectedAppName + '</fullNames></tns:readMetadata>';
updateContainerXML = '<updateMetadata xmlns="http://soap.sforce.com/2006/04/metadata"><metadata xsi:type="ConnectedApp">{0}</metadata></updateMetadata>';
closeXML = '</soapenv:Body></soapenv:Envelope>';
// Seed the user's session for authentication
sessionHeaderXML = String.format(sessionHeaderXML, new String[] {UserInfo.getSessionId()});
retrieveCertificateXML = headerXML + sessionHeaderXML + retrieveCertificateXML + closeXML;
retrieveConnectedAppXML = headerXML + sessionHeaderXML + retrieveConnectedAppXML + closeXML;
// Initialize the httpRequest to verify and retrieve the certificate
req = new HttpRequest();
req.setEndpoint(Url.getOrgDomainUrl().toExternalForm() + '/services/Soap/m/60.0');
req.setMethod('GET');
req.setHeader('SOAPAction', '""');
req.setHeader('Content-Type', 'text/xml');
req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
req.setBody(retrieveCertificateXML);
// Process the httpRequest
res = new Http().send(req);
certificateXML = res.getBody();
// Confirm that the certificate already exists
if (!certificateXML.contains('<fullName>Cuneiform_for_CRM_Self_Signed_Certificate</fullName>')) {
System.debug(bdr);
System.debug(spacer + 'We were unable to verify the [Cuneiform for CRM: Self-Signed Certificate] exists.');
System.debug(spacer + 'Please check [Certificate and Key Management] within Salesforce Setup and');
System.debug(spacer + 'Manually verify that the certificate exists.');
System.debug(spacer);
System.debug(spacer + 'If you need to create the certificate, please visit ');
System.debug(spacer + 'https://peernova.link/cuneiform/fdm/setup/step-000');
System.debug(spacer + 'and execute the Anonymous Apex script to create the certificate.');
System.debug(bdr);
} else {
// Update the request and validate the ConnectedApp was found
req.setBody(retrieveConnectedAppXML);
// Execute the request
res = new Http().send(req);
connectedAppXML = res.getBody();
// Confirm that the connectedApp already exists
if (!connectedAppXML.contains('<fullName>' + connectedAppName + '</fullName>')) {
System.debug(bdr);
System.debug(spacer + 'We were unable to verify the [Cuneiform for CRM: Connected App] exists.');
System.debug(spacer + 'Please check [Connected Apps] within Salesforce Setup');
System.debug(spacer + 'and manually verify that the Connected App exists.');
System.debug(spacer);
System.debug(spacer + 'If you need to create the Connected App, please visit ');
System.debug(spacer + 'https://peernova.link/cuneiform/fdm/setup/step-006');
System.debug(spacer + 'and execute the Anonymous Apex script to create the Connected App.');
System.debug(bdr);
} else {
// Retrieve the certificate content and append it to the connectedApp definition
certContent = certificateXML.substringBetween('<content>', '</content>');
digitalCertificateXML = '<certificate>' + certContent + '</certificate></oauthConfig>';
connectedAppXML = connectedAppXML.replace('</oauthConfig>', digitalCertificateXML).substringBetween('<result><records xsi:type="ConnectedApp">', '</records>');
// Create the connectedApp update details and set the request
updateConnectedAppXML = updateHeaderXML + sessionHeaderXML + updateContainerXML + closeXML;
updateConnectedAppXML = String.format(updateConnectedAppXML, new String[] {connectedAppXML});
req.setBody(updateConnectedAppXML);
// Execute the request
res = new Http().send(req);
updateResponseXML = res.getBody();
// Confirm that we were able to update the ConnectedApp
if (updateResponseXML.contains('<success>true</success>')) {
System.debug(bdr);
System.debug(spacer + 'The Cuneiform for CRM: Connected App was successfully updated. Please');
System.debug(spacer + 'examine the Connected App to verify the digital certificate was added.');
System.debug(spacer);
System.debug(spacer + 'Nice Work! We have updated the Connected App to include the digital');
System.debug(spacer + 'certificate. You are now ready to validate your configuration.');
System.debug(spacer);
System.debug(spacer + 'Please follow our remaining installation instructions via');
System.debug(spacer + 'https://peernova.link/cuneiform/fdm/setup/verify');
System.debug(spacer + 'to verify your configuration via our Control Panel.');
System.debug(bdr);
} else {
System.debug(bdr);
System.debug(spacer + 'We were unable to update the Cuneiform for CRM: Connected App. Please review the error response for details.');
System.debug(spacer + 'Metadata API Error Response:');
System.debug(spacer);
System.debug(updateResponseXML.mid(updateResponseXML.indexOf('<errors>'), updateResponseXML.indexOf('</errors>') - (updateResponseXML.indexOf('<errors>') - 9)));
System.debug(spacer);
System.debug(spacer + 'Please review Metadata API Error Response, and try again. Verify that you have access');
System.debug(spacer + 'and permission to update Metadata via the Metadata API, and that the Cuneiform for CRM');
System.debug(spacer + 'Connected App already exists in your Org.');
System.debug(bdr);
}
}
}
Execute this Anonymous Apex script to assign your self-signed certificate to the Connected App. Use our step-by-step guide if you are unfamiliar with executing Apex Scripts via the Salesforce Developer Console.
Congratulations! You’ve completed the configuration tasks (wasn’t that fast?). Now, please visit Verifying Your Configuration with the Control Panel to confirm that the app is correctly configured.