When Salesforce is life!

Tag: Crusade Page 2 of 4

[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: 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] 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: 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: 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)|

[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: ApexClass

Source: ApexClass

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 ApexClass 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 Body of the class, whether it is valid or not, size in byte without comments.

Here an example:

Select Id, Name, ApiVersion, Body, IsValid, LengthWithoutComments From ApexClass ORDER BY Name

[Salesforce] The Sobject Crusade: Announcement

Source: Announcement

This object represents a Chatter Group Announcement.

To create a new announcement, go to the Chatter tab, select (or create) a group:

Click on More > Announcement and select the body of the announcement and an expiration date:

A section will compare on the Group’s sidebar:

The Announcement object is a detail of the FeedItem object that specifies the Expiration Date of the feed. Given the following query:

select FeedItem.Body, ExpirationDate, Parent.Name from Announcement

To create the object programmatically follow these rules:

//query the Collaboration Grouop
CollaborationGroup group = [Select Id From CollaborationGroup Where Name = 'Awesome Group'];

//creates a feed item; type "AdvancedTextPost" is mandatory
FeedItem item = new FeedItem(Type='AdvancedTextPost', ParentId=group.Id, Body='This is an awesome announcement');
insert item;

//create the announcement
Announcement anc = new announcement(FeedItemId=item.Id, ExpirationDate=System.today().addDays(10));
insert anc;

//upate the group
group.AnnouncementId=anc.Id
update group;

[Salesforce] The Sobject Crusade: AllowedEmailDomain

Back to the Sobject Crusade list.

Source: AllowedEmailDomain

To enable this object you need to contact Salesforce.com support: I had no way to enable it quickly so I’m just gonna post what this feature means.

The AllowedEmailDomain object is used to store the allowed email domains for your users.

To configure it go to Setup > Manage Users > : you can add domains (e.g. google.com) or even subdomains (e.g. app.enree.co): from now on every new or updated user’s Email field must match the domain whitelist configure in this section.

Existing users’ emails will still be valid till their update.

This restriction doesn’t apply to external users (portal, Communities or Chatter, or simply every user that has a related Contact/Account).

More info here.

[Salesforce] The Sobject Crusade: AgentWork

Back to the Sobject Crusade list.

Source: AgentWork

This object represents work item assignment that has been routed to an Agent: this is basically a new type of queue management introduced with Summer 15 called Omni-Channel.

Omni-Channel is a comprehensive customer service solution that lets your call center route any type of incoming work item, including cases, chats, phone calls, or leads—to the most qualified, available agents in your organization.
Omni-Channel integrates seamlessly into the Salesforce console.

This release contains a beta version of Omni-Channel that is production quality but has known limitations (Summer 15).

To enable Omni-Channel go to Customize > Omni-Channel > Settings and flag Enable Omni-Channel.

Now let’s create a Service Channel: this is the source of work items for the Omni-Channel, and can be associated to various standard objects and even custom object.

These objects are taken from queues and sent to the agent’s console in real time.

To create a new Service Channel, click on Customize > Omni-Channel > Service Channels:

The Custom Console Footer Component field is optionally used to open a specific component on the Console when that specific work item is selected.

Next step is creating Routing Configurations, which determine how work items are routed to agents; click on Customize > Omni-Channel > Routing Configurations:

Where:

  • Routing Priority: is the priority of the current Service Channel. Other factors are counted when calculating the priority (available capacity of the agent, priority of the queue that the work item came from, amount of time that the work item has been waiting in the queue, members of the queue who are available to receive new work items from the queue). When an agent gets a work item, the owner of the object is set to the agent.
  • Routing Model: reflects the agent’s behavior
  • Capacity Weight: amount of agent’s capacity (set in the Capacity field of the presence configuration) scaled down when the work item is assigned. You can select a Capacity Weight or a Capacity Percentage, but not both.
  • Capacity Percentage: percent of agent’s capacity scaled down when the work item is assigned

Now you can link your ORG queues to the Routing Configurations.

Click on Setup > Manage Users > Queues:

In the Routing Configuration set the configuration you have set up in the previous step.

Next step is to configure Agent’s presence statuses: this way we can set up how much work every agent can handle.

When you enable the Omni-Channel feature Salesforce adds a Default Presence configuration to be applied to all Agent’s by default.

Go to Customize > Omni-Channel > Presence Configurations and click the New:

You can set:

  • Capacity: maximum Agent’s capacity
  • Automatically Accept Requests: automatically assigns a work item to an available agent
  • Allow Agents to Decline Requests: Agents can decline assignments; if “Automatically Accept Requests” is set to true, agents cannot decline
  • Update Status on Decline: update status when declining, only available if “Allow Agents to Decline Requests” is flagged

Finally one or more channels can be associated with different Presence Statuses, on Setup > Customise > Omni-Channel > Presence Statuses:

To assign Presence Statuses to users go to the User’s Profile on the Enabled Service Presence Status Access and click edit:

This could be done as well using Permission Sets. Take a Case and set the Owner to be the previously created queue (so we can test it). Now configure the Cloud Console App (Setup > Apps > [YOUR_CONSOLE_APP_NAME]) to use the Omni-Channel component:

Now open the Console App (Developer Orgs come with a Sample Console app):

Go online by clicking on the presence status link to see the magic start:

You can change the object’s compact layout (for Case go to Setup > Cases > Compact Layout) to show other informations of the object:

You can integrate with an already configured Live Agent. For more details read the official docs. Click “Accept” and the new AgentWork will be stored in the CRM:

select id,lastmodifieddate,CapacityPercentage,CapacityWeight,Name,OriginalQueue.Name,status,WorkItemId,ServiceChannelId,UserId from agentwork order by lastmodifieddate DESC

It shows all the Work Items assigned to the agents (this is basically the history of plyaing with a Case and looping over assigning to a queue and accepting/declining with the current User as Agent).

Page 2 of 4

Powered by WordPress & Theme by Anders Norén