When Salesforce is life!

Tag: OAuth

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

Back to the Sobject Crusade list.

Source: ActionLinkGroupTemplate

Action Links are actions you can do in chatter posts (e.g. API call, document download, UI redirect) by clicking a button on the related chatter post.

Action Links are grouped into Action Link Group, so you can choose which action you want to play:

You may think that these actions comes to configuration and some sort of layout, but it is part configuration and part Chatter API call.

Action Links are grouped into Action Link Groups but this is not the object that will be configured.

In fact we talk about Templates (Action Link Group Template and Action Link Template): we define template actions rather than the actual actions we’ll be executing.

The template defines the actions you want to apply to a Chatter feed: when you want to apply the group of actions to a feed, we’ll make an API call to get the “instance” of the actions to be linked to the feed.

Let’s start creating a Group Template (the object we are inspecting here) clicking on Setup > Create > Action Link Templates:

You can define:

  • Name and DeveloperName
  • Category (position of the action buttons in the feed)
  • Number of execution allowed
  • Hours untile the action expires

Once a group is published it cannot be modified anymore.

Let’s query the object:

select id, DeveloperName, IsPublished, MasterLabel  from ActionLinkGroupTemplate

Let’s define the actions:

With the following main fields (more details here):

  • Type of Action (API, API Async, UI, Download)
  • Endpoint of the action (in this example a simple request bin)
  • HTTP Method
  • HTTP Request Body
  • HTTP headers
  • Position among other buttons
  • Label (you can choose between some presets and a custom label)
  • Visibility

Consider that URL, request body and headers can have standard and custom binding values (you can for example pass the current User ID or a custom binding value).

These are the actions defined:

To use them we need a valid Session ID, a call to the /connect/action-link-group-definitions/ API endpoint and a call to Chatter API to send a feed.

We’ll be doing things manually (we could use Workbench, but we like to get our hands dirty!).

First we get a valid OAuth Session ID.

Click on Setuo > Create > Apps click on New button in the Connected Apps section; we only need to setup the middle section:

to get access to Chatter API.

This is what you get:

This application will be online in 10 minutes at most.

Let’s make a REST call to get a valid token using the OAuth Password Flow (details here ):

POST https://login.salesforce.com/services/oauth2/token
    Headers:
        Content-Type: application/x-www-form-urlencoded
    Body:
        grant_type=password&client_id=[APP_CONSUMER_KEY]&client_secret=[APP_CONSUMER_SECRET]&username=[USERNAME]&password=[PASSWORD+TOKEN]

The response returns the Session ID (athorization token) we’ll use to get a valid Action Group Id from this REST call:

POST https://xxx.salesforce.com/services/data/v34.0/connect/action-link-group-definitions/
    Headers:
        Authorization: Bearer [SESSION_ID]
        Content-Type: application/json
    Body:
        {"templateId":"07g24000000CaTc","templateBindings":[]}

Where templateId is the ID of the Action Link Group Template (you can query for it or get it in the configuration section of the template), and templateBindings is a list of objects (with “key” and “value” fields) for custom bindings.

This call will return an instance of the template:

{
  "actionLinks": [
    {
      "actionUrl": "https://requestb.in/11jyqzq1",
      "createdDate": "2015-08-14T20:41:42.566Z",
      "excludedUserId": null,
      "groupDefault": false,
      "headers": [
        {
          "name": "content-type",
          "value": "application/json"
        }
      ],
      "id": "0An240000004FSDCA2",
      "label": "Salute!",
      "labelKey": "None",
      "method": "HttpPost",
      "modifiedDate": "2015-08-14T20:41:42.566Z",
      "requestBody": "{"message": "Hello!", "from":"{!userId}"}",
      "requiresConfirmation": false,
      "templateId": "07l24000000GmbgAAC",
      "type": "Api",
      "userId": null
    },
    {
      "actionUrl": "https://requestb.in/11jyqzq1",
      "createdDate": "2015-08-14T20:41:42.566Z",
      "excludedUserId": null,
      "groupDefault": false,
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        }
      ],
      "id": "0An240000004FSECA2",
      "label": "Insult!",
      "labelKey": "None",
      "method": "HttpPost",
      "modifiedDate": "2015-08-14T20:41:42.566Z",
      "requestBody": "{"message":"Bad Word", "from":"{!userId}"}",
      "requiresConfirmation": true,
      "templateId": "07l24000000GmblAAC",
      "type": "Api",
      "userId": null
    }
  ],
  "category": "Primary",
  "createdDate": "2015-08-14T20:41:42.566Z",
  "executionsAllowed": "Once",
  "expirationDate": null,
  "id": "0Ag240000004FSNCA2",
  "modifiedDate": "2015-08-14T20:41:42.566Z",
  "templateId": "07g24000000CaThAAK",
  "url": "/services/data/v34.0/connect/action-link-group-definitions/0Ag240000004FSNCA2"
}

Now we can use the id to post the action with a feed:

POST https://xxx.salesforce.com/services/data/v34.0/chatter/feed-elements
    Headers:
        Authorization: Bearer [SESSION_ID]
        Content-Type: application/json
    Body:
        {
          "body": {
            "messageSegments": [
              {
                "type": "Text",
                "text": "What do you want to say me?"
               }
            ]
            },
          "subjectId": "me",
          "feedElementType": "FeedItem",
          "capabilities": {
            "associatedActions": {
              "actionLinkGroupIds": ["0AgRR0000004CTr0AM"]
            }
          }
        }

Once you click on a button, the action is executed and no more action is possible:

The same sequence can be done using Apex and the ConnectApi library:

// Get the action link group template Id.
ActionLinkGroupTemplate template = [SELECT Id FROM ActionLinkGroupTemplate WHERE DeveloperName='Say_Hello'];


// Create ActionLinkTemplateBindingInput objects from the map elements.
List bindingInputs = new List();
/*
// Add binding name-value pairs to a map: our action does not have custom bindings
Map bindingMap = new Map();
bindingMap.put('aBinding','aValue');
for (String key : bindingMap.keySet()) {
    ConnectApi.ActionLinkTemplateBindingInput bindingInput = new ConnectApi.ActionLinkTemplateBindingInput();
    bindingInput.key = key;
    bindingInput.value = bindingMap.get(key);
    bindingInputs.add(bindingInput);
}
*/

// Set the template Id and template binding values in the action link group definition.
ConnectApi.ActionLinkGroupDefinitionInput actionLinkGroupDefinitionInput = new ConnectApi.ActionLinkGroupDefinitionInput();
actionLinkGroupDefinitionInput.templateId = template.id;
actionLinkGroupDefinitionInput.templateBindings = bindingInputs;

// Instantiate the action link group definition.
ConnectApi.ActionLinkGroupDefinition actionLinkGroupDefinition = 
ConnectApi.ActionLinks.createActionLinkGroupDefinition(Network.getNetworkId(), actionLinkGroupDefinitionInput);

This is what the bin has received:

Imagine you can use this feature for allowing selected users to partecipate to surveys/polls or make them doing actions to monitor their behavior.

Powered by WordPress & Theme by Anders Norén