When Salesforce is life!

Author: Enrico Murru Page 12 of 20

[Salesforce / Certifications] Salesforce Service Cloud Consultant Certification (a Study Guide?)

The last certification I took the past weekend is the Service Cloud Consultant Certification.

I have to admit it’s been the toughest, not because its questions were “heavily hard” but because the exam took part on Salesforce features I was not completely familiar with.

I cannot provide you a complete guide on how to pass this exam but I can tell you the resources I used to pass it.

A prerequisite is the Administrator certification, some hints here from Admin Hero, don’t worry there is plenty of stuff on the web…just Google it!

I may say that being and certified Administrator is also necessary to understand basic features of the platform covered in the Service Cloud cert.

The main part of the exam is centered on Cases, Knowledge, the Service Cloud Console and all related features.

There is also an awesome Service Cloud trailhead module:

So happy study and good luck!

[Salesforce] The Sobject Crusade: AppMenuItem

Source: AppMenuItem

This object hosts the applications you find in the app menù (upper right corner of the CRM) or in the App Launcher and the Connected Apps.
The list of such items are in Setup > Create > Apps

The object contains all application informations such as Name, Label, start URL, but also all the configurations about Canvas apps.

Here an example:

Select Id, Label, StartUrl, Type, ApplicationId, CanvasEnabled, SortOrder from AppMenuItem

[Salesforce] The Sobject Crusade: ApexTrigger

Source: ApexTrigger

The ApexClass is the most beloved object for Salesforce Developers.

It identifies a specific Apex Class, so you can query for Classes runtime.

Note that, even if the describe states that the ApexTrigger is creatable and updatable, an exception is thrown if you try to insert/update via API a trigger: use the tooling API or metadata API instead.

Among the fields, you can query for the Body of the trigger, whether it is valid or not, size in byte without comments.

Here an example:

Select Id, Name, ApiVersion, Body, IsValid, LengthWithoutComments, TableEnumOrId, UsageAfterDelete, UsageAfterInsert From ApexTrigger ORDER BY Name

[Salesforce] The Sobject Crusade: ApexTestQueueItem

Source: ApexTestQueueItem

The ApexTestQueueItem object gives you access to the underlying test item for a given test job for a given class.

If you don’t know what is a test class refer to this article to get started: a test class is a class used to (guess what?) test a set of apex classes / triggers in order to verify and certify the features of your implementation.

Every ORG must have at least 75% of code coverage (here for more details), that is your test classes should cover at least 75% of the lines you have written in your Apex classes.

To execute a test class (or a set of test classes), go to Your Name > Developer Console, click on Test menù, select New Run and then select the test classes you want to execute:

On the lower part of the console you can see the test executing:

You can now query all the single job items (the second level of the tree):

select id, ApexClass.Name, ExtendedStatus, ParentJobId, Status, CreatedDate from ApexTestQueueItem order by CreatedDate desc

You can update a ApexTestQueueItem to change its status if you want to abort the job (setting Status to Aborted), or you can insert a new ApexTestQueueItem object: this will create a new Process Job, e.g.:

ApexTestQueueItem item = new apextestqueueitem();
item.ApexClassId = [select id from apexclass where name = 'CommunitiesLandingControllerTest'].Id;
insert item;

[Salesforce] The Sobject Crusade: ApexTestSuite

Source: ApexTestSuite

String ’16 comes with a handy feature regarding test execution, the Test Suites.

They are a collection of tests ment to be run together organized in a single suite.

To access the test suite open your Developer Console and click Test > New Suite and enter the Suite name:

Then select all test classes you want to run in the suite:

In the Test > Suite Manager menu item you can change a suite’s configuration, while in Test > New Suite Run you can choose which suites to execute.

The Settings button allows to set the maximum number of failuers allowd, so the test suite ends its run immediately after a given number of exceptions are thrown.

Clicking Run the tests are executed and presented on the Tests tab on the lower side of the Developer console:

You can query the Object easily.

Select Id, TestSuiteName From ApexTestSuite

To check for the classes involved in the suite you need to query the TestSuiteMembership that links Apex classes to the Suite, but it is another story.

[Salesforce] The Sobject Crusade: ApexTestResult

Source: ApexTestResult

After you execute a test class (ApexTestQueueItem), you’ll get the test results of a given test method execution.

To query this information use the ApexTestResult object.

The object conveys the following fields:

  • ApexClassId: the test class
  • ApexLogId: the ApexLog object (if log is enabled)
  • AsyncApexJobId: the main aync job (this is the same as ApexTestQueueItem.ParentJobId) to which the given test execution belongs
  • Message: the exception message, only if an exception occurs
  • MethodName: the test method name of the given log
  • Outcome: the result of the test method execution (Fail, Pass, CompileFail)
  • QueueItemId: lookup to the ApexTestQueueItem the result belogs to
  • StackTrace: exception stack trace (if any)
  • TestTimestamp: test execution date/time

To launch a test, see the instructions on the ApexTestQueueItem object page.

Now you can query for the results:

Select Id, ApexClass.Name, MethodName, ApexLogId, Outcome, TestTimestamp, AsyncApexJobId, QueueItemId From ApexTestResult order by TestTimestamp DESC

Given this failing test class:

@isTest
private class ExceptionTestClass {
    
    private static testmethod void failMethod(){
     update new Case(Subject = 'Test');
    }
}

The execution will lead to a test error:

[Salesforce / Lightning] #Cool Modal Dialog Component

Here we go with another simple UI component, made up with the new Lightning Design System.

Here you go a simple Modal dialog box.

For those of the TL;DR tribem here is the GitHub repository.

The component is ment to be placed in your app in this way:

<c:ModalDialog type="{!v.dialogType}" 
                   title="{!v.dialogTitle}" 
                   content="{!v.dialogContent}" 
                   showDialog="{!v.showDialog}"
                   context="{!v.dialogContext}"
                   inputValue="{!v.dialogInputValue}"
                   inputPlaceholder="{!v.dialogInputPlaceholder}"
                   onClose="{!c.dialogCallback}" />

And to be waken up by a boolean variable (in this case by v.showDialog).

When you switch this variabile to true/false the modal behaves as expected (shows up or closes).

This kind of architecture allow for a single callback after the modal closes, so you need a way to know what to do after this close event.

That is to say that you need to be aware of the context that triggered the modal.

The context parameter is a Javascript object that can contain wathever you want (I used an “action” string for instance, but you can put wathever variables you want) and is passed through to the onClose callback, here is an example:

{
    //ModalDialogTestAppController.js
    //. . .
    showAlert: function(component, event, helper) {
        $A.run(function(){
            helper.showDialog(component, 
                              'ALERT', //dialog type
                              'THIS IS AN ALERT!', //modal title
                              'Hello world!', //modal content
                              null, //input default string (only for "INPUT" type)
                              null, //input placeholder (only for "INPUT" type)
                              {
                                  action: helper.C.ACTIONS.SHOW_ALERT,
                                  anotherValue: Date.now(),
                              }, //context object
                              true); //show the dialog
        });
    },

The component supports the following parameters:

  • type:this is the type of dialog you want to open. Supports: ALERT, CONFIRM, INPUT, LOADER
  • title: title of the modal dialog
  • content: content text of the modal dialog
  • showDialog: boolean value that indicates if the modal is shown/hidden
  • context: Javascript object containing the context you want to be passed back to the callback invocation
  • inputValue: default value of the input field (only for INPUT type)
  • inputPlaceholder: placeholder for the input field (only for INPUT type)
  • onClose: callback called after the ModalDialogCloseEvt event is fired by the modal dialog component

The onClose callback is fired upon the ModalDialogCloseEvt event that supports the following parameters:

  • dialogType: the type of the calling dialog
  • confirmResult: true if the user pressed YES/OK buttons (always false for ALERT, LOADER)
  • inputResult: input written by the user (set only if type is INPUT and user pressed the OK button)
  • context: context sent to the component

These are the different kinds of dialogs:

ALERT

CONFIRM

INPUT

LOADER

And here it is the test app in action:

[Salesforce] The Sobject Crusade: ApexPage

Source: ApexPage

The ApexComponent, as you would expect, is the record for the Visual Force Component.

Note that, even if the describe states that the ApexPage is creatable and updatable, an exception is thrown if you try to insert/update via API a class: use the tooling API or metadata API instead.

Among the fields, you can query for the markup of the component, its Apex controller.

Here an example:

Select Id, Name, ApiVersion, Markup, ControllerKey, ControllerType, isAvailableInTouch, IsConfirmationTokenRequired From ApexPage ORDER BY Name

THe IsConfirmationTokenRequired flag can be updated in the standard UI or in the Developer Console (under Settings button) and is used to allow Secure Cross Site Request Forgery.

What is it?

This is a way to secure your pages when using GET requests, i.e. when you make changes to the Data Model that should not be looped with, e.g., subsequent GET requests.

How does it work?

Salesforce appends a unique token (per user / session), that is a 128 bit (16 chars) string, in the page URL with the _CONFIRMATIONTOKEN parameter: this token can be used only once, and the VisualForce engine automatically handles the parameter validity.

Unfortunately this mechanism is provided only when overriding the delete standard button-

Let’s make a try.

Let’s create a new Visual Force page (no action indeed):

<apex:page standardController="Case">
 <b>THIS ACTION WILL BE EXECUTED ONCE</b>
</apex:page>

This is a page with a standard controller that actually does nothing, but it is just to prove what we are trying to do.

Click on the Settings button on the developer console to enable the Require CSRF protection on GET requests flag:

If you try to load the page manually, you’ll get this wonderful error:

If you append the _CONFIRMATIONTOKEN parameter with a random value, the message changes:

If we find a way to pass the correct token, we’ll see the page.

As said, the only way to make Salesforce generate that token, is to override the delete button action.

Go to Setup > Customize > Cases > Buttons, Links and Actions and click Edit next to the Delete action:

Now try to delete a Case:

Salesforce will be releasing in the next future this feature for other actions.

[Salesforce] The Sobject Crusade: ApexComponent

Source: ApexComponent

The ApexComponent, as you would expect, is the record for the Visual Force Component.

Note that, even if the describe states that the ApexComponent is creatable and updatable, an exception is thrown if you try to insert/update via API a class: use the tooling API or metadata API instead.

Among the fields, you can query for the markup of the component, its Apex controller.

Here an example:

Select Id, Name, ApiVersion, Markup, ControllerKey, ControllerType, From ApexComponent ORDER BY Name

[Salesforce] The Sobject Crusade: ApexLog

Source: ApexLog

The ApexLog gives access to the Debug Log.

To enable log monitoring for a user, go to Setup > Logs > Debug Logs and click the New button to add an user.

These rows can be queried:

select id, StartTime, Application, DurationMilliseconds, Location, LogLength, LogUser.Name, Operation, Status from apexlog order by StartTime DESC

You can read all the infos regarding the given apex log, such as the User, kind of request, the size of the log, its status, but you cannot access the raw content: to get to the raw content you have to make a Tooling API request to the endpoint:

GET https://xxx.salesforce.com/services/data/v34.0/sobjects/ApexLog/[APEXLOG_ID]/Body/
 Headers:
  Authorization: Bearer [SESSION_TOKEN]

The session token could be grabbed with an anonymous Apex call to UserInfo.getSessionId() or using the OAuth Password flow described in the ActionLinkGroupTemplate object.

This is what you get:

34.0 APEX_CODE,FINEST;APEX_PROFILING,INFO;CALLOUT,INFO;DB,INFO;SYSTEM,DEBUG;VALIDATION,INFO;VISUALFORCE,INFO;WORKFLOW,INFO
17:13:22.020 (20181893)|EXECUTION_STARTED
17:13:22.020 (20210501)|CODE_UNIT_STARTED|[EXTERNAL]|06624000003BOE1|VF: /apex/ConfirmationTokenRequired
17:35:30.036 (36842637)|CODE_UNIT_FINISHED|execute_anonymous_apex
17:35:30.038 (38198449)|EXECUTION_FINISHED
17:13:22.040 (40498571)|CODE_UNIT_STARTED|[EXTERNAL]|ApexDataSource:MongoDBDataSrouceProvider
17:13:22.040 (40523619)|CODE_UNIT_STARTED|[EXTERNAL]|ApexDataSource:MongoDBDataSrouceProvider
17:13:22.043 (43448400)|HEAP_ALLOCATE|[71]|Bytes:3
17:13:22.043 (43497592)|HEAP_ALLOCATE|[76]|Bytes:152
17:13:22.043 (43514859)|HEAP_ALLOCATE|[272]|Bytes:408
17:13:22.043 (43534730)|HEAP_ALLOCATE|[285]|Bytes:408
17:13:22.043 (43553779)|HEAP_ALLOCATE|[379]|Bytes:48
17:13:22.043 (43582592)|HEAP_ALLOCATE|[131]|Bytes:6
17:13:22.043 (43605101)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:6
17:13:22.043 (43624664)|METHOD_ENTRY|[1]|01p2400000AgxCj|MongoDBDataSrouceProvider.MongoDBDataSrouceProvider()
17:13:22.043 (43630791)|STATEMENT_EXECUTE|[1]
17:13:22.043 (43636403)|STATEMENT_EXECUTE|[1]
17:13:22.043 (43641248)|METHOD_EXIT|[1]|MongoDBDataSrouceProvider
17:13:22.043 (43660705)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:4
17:13:22.043 (43671735)|VARIABLE_SCOPE_BEGIN|[1]|this|MongoDBDataSrouceProvider|true|false
17:13:22.043 (43713117)|VARIABLE_ASSIGNMENT|[1]|this|{}|0x74896a31
17:13:22.043 (43758665)|SYSTEM_CONSTRUCTOR_ENTRY|[EXTERNAL]|()
17:13:22.043 (43866237)|SYSTEM_CONSTRUCTOR_EXIT|[EXTERNAL]|()
17:13:22.043 (43880333)|STATEMENT_EXECUTE|[1]
17:13:22.043 (43892018)|CODE_UNIT_FINISHED|ApexDataSource:MongoDBDataSrouceProvider
17:13:22.044 (44048812)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:4
17:13:22.044 (44059804)|SYSTEM_MODE_ENTER|false
17:13:22.044 (44067540)|VARIABLE_SCOPE_BEGIN|[7]|this|MongoDBDataSrouceProvider|true|false
17:13:22.044 (44087738)|VARIABLE_ASSIGNMENT|[7]|this|{}|0x74896a31
17:13:22.044 (44101508)|STATEMENT_EXECUTE|[7]
17:13:22.044 (44104497)|STATEMENT_EXECUTE|[8]
17:13:22.044 (44112485)|HEAP_ALLOCATE|[8]|Bytes:4
17:13:22.044 (44182135)|SYSTEM_CONSTRUCTOR_ENTRY|[8]|()
17:13:22.044 (44204845)|SYSTEM_CONSTRUCTOR_EXIT|[8]|()
17:13:22.044 (44210268)|VARIABLE_SCOPE_BEGIN|[8]|capabilities|List|true|false
17:13:22.044 (44237205)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:4
17:13:22.044 (44259684)|VARIABLE_ASSIGNMENT|[8]|capabilities|{"s":1,"v":[]}|0x3ce93902
17:13:22.044 (44264466)|STATEMENT_EXECUTE|[9]
17:13:22.044 (44381336)|SYSTEM_METHOD_ENTRY|[9]|List.add(Object)
17:13:22.044 (44408889)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:4
17:13:22.044 (44420907)|SYSTEM_METHOD_EXIT|[9]|List.add(Object)
17:13:22.044 (44426463)|STATEMENT_EXECUTE|[10]
17:13:22.044 (44439376)|SYSTEM_METHOD_ENTRY|[10]|List.add(Object)
17:13:22.044 (44453626)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:4
17:13:22.044 (44462615)|SYSTEM_METHOD_EXIT|[10]|List.add(Object)
17:13:22.044 (44466873)|STATEMENT_EXECUTE|[11]
17:13:22.044 (44476051)|SYSTEM_MODE_EXIT|false
17:13:22.044 (44490717)|CODE_UNIT_FINISHED|ApexDataSource:MongoDBDataSrouceProvider
17:13:22.061 (61085594)|CUMULATIVE_LIMIT_USAGE
17:13:22.061 (61085594)|LIMIT_USAGE_FOR_NS|(default)|

Page 12 of 20

Powered by WordPress & Theme by Anders Norén