Salesforce Elevate – Bilbao ’17

During Dreamforce ’17 Erika Kuhl, Holly Firestone and their team showed us how they are working really hard to get more content ready for our Developer and User Groups, and some other amazing ideas that can help us to get more attendees, like Global gathering events.

But in the meantime their ideas take effect, I had to think about something to do it now, as every single day, the number of guests at North Spain Developer User Group, is lower. And to run a second Elevate in Bilbao didn’t sound like a bad idea. And that’s what I did, prepare a 1 day event to talk about Salesforce.

What did I do?

First thing to keep in mind, the main objective is to get audience that could be interested to continue attending SDG talks … and where can I get this?

Facultad IngenieriaUniversity could be a good option. There are lot of students keen to know more about new technologies, solutions, and furthermore, that could be a good scenario to promote the open door that Salesforce can give them in their job career. And that is what I did, talked with the University of Deusto in order to run the event there.

Secondly, I needed some sponsors that could help me to support the event.

Salesforce Iberia, helped me with catering and Jon Ibáñez came to the event in order to talk about Salesforce platform. I really appreciated this introduction talk as it was really useful, even for me, that I’m on this world since almost 10 years ago.

 Jon1

NTS with Carlos Polo and Esteban Ugarte, was really helpful with catering expenses too, and also running a couple of sessions, like the one that Gorka run about Salesforce and Chatbots. Looking forward to know more about this topic, that allows you to create chats able to answer questions.

But I also needed some more help with sessions. FinancialForce was also an amazing help. Miguel Moro and Agustín Jimenez came from our Granada office to talk about the company itself, and also run a really good talk about Separation of Concerns (SoC) #qualitycode guide, that can help us to enhance our code and make it more readable and useful via the FinancialForce open source ApexLib.

Also, I wanted to provide a success case to the audience. Not only explain all benefit that Salesforce could bring to us. I wanted to show a real use case and I got it closer than expected. University of Deusto is a Salesforce customer too, and we had 2 good examples. First of all how they use Salesforce as a solution provider to the needs that the University can have. And secondly how they use and configure Salesforce without technical knowledge, with just #clicksnotcode.

Finally, the main goal of the event, promote other events, but we needed to start talking about Salesforce Community, our #ohana spirit , how we help each others, that we need some others to continue with this job, and how events like SDG or dreamin events could help. And guess what? As part of dreamOlé organizers, obviously I talked about this event that will be run in Barcelona next 27th of April.

 After a nice lunch…

We moved to the afternoon sessions, Chatbots one run by NTS and Process Automation with and without Code that I also ran.

And finally, time to dirty our hands working with Salesforce via Trailhead. Thanks Christie and your team to help me with this section and sending to me such nice stickers!!

What have I learnt on this event?

2 years ago I run a similar event but Trailhead just appeared few months ago and sessions we run were workshops to learn how to starts. Because of the low level of the content, some attendees complained. They were expecting something more than how to create an App with #clicksnotcode.

That made me think to prepare this Elevate with sessions with higher level, and Trailhead allowed newbies to start from scratch.

However, this year feedback was that it would be useful to have a quick overview of the platform, a demo or something similar as a first session just for starters, and then move to other sessions.

So keep in mind my feedback if you are thinking on running something similar for your group.

If you want to take a look at slides, find them here as well as some others. And don’t forget to join us at North Spain Developer User Group.  Next talks about Data Mining and Einstein, Javascript and Salesforce and working on a Salesforce DX workshop.

Thanks to everyone that helped me to make this Elevate event real

Eventos, Eventos y Eventos

El otoño va llegando y aunque el segundo semestre del año es más tranquilo en cuanto a eventos de Salesforce se refiere, mi calendario se está llenando poco a poco.

22 de Septiembre

Hace un mes celebré uno de los últimos Salesforce Developer Group de Bilbao donde intentaba acercar el evento Trailhead DX que se celebró en San Francisco a finales de Junio. Fue una tarde tranquila dónde vimos alguno de los videos publicados por Salesforce e hicimos algunos hacks.

Al final un SDG es una reunión para hablar de manera informal sobre Salesforce. En qué estamos trabajando, qué problemas nos encontramos, cómo podemos solucionarlos, si alguien se anima puede hacer una presentación sobre algún tema actual y todo aderezado con pizza, bebidas y regalos.

¿Todavía no te has unido a nuestro grupo? No lo dudes y apúntate. Estamos preparando nuevas quedadas para hablar de JavaScript y Salesforce, Salesforce DX, Einstein y otros.

2 de Octubre

A principios de mes, Laura Díaz me invitó a participar en el SDG de Ginebra que comparte con Gnana. Como Salesforce MVP, es normal que otros grupos te inviten para dar una charla, pero para mi era la primera vez así que me hizo mucha ilusión. ¿Y de que hablé? Sobre Visual Workflow. A principios de año asistí a London’s Calling dónde hice una presentación sobre ese tema, y realmente se ha convertido en un hot topic porque lo presenté junto con Alva R. Azcona en London World Tour y en breve también en Dreamforce ’17.

Del 4 al 9 de Noviembre

Dreamforce!!!! Después de un año esperando, la fecha ya está aquí y este año para mi es especial. No sólo vamos 5 miembros de la oficina de FinancialForce de Granada (por fin no viajo sola!!) sino que algunos de mis amigos de dreamOlé también estarán allí. ¡Este año promete!

Pero no penséis que sólo vamos de pingo. Lo vamos a dar todo durante la conferencia y después, y como prueba, os dejo información sobre las sesiones que Alba y yo vamos a dar.

1. Be a Big Data Champion With Big Objects and Async SOQL

Enlace

Captura de pantalla 2017-10-16 a las 12.39.32

Imagen de dreamforce.com website

2. Process Automation With and Without Code

Enlace

Captura de pantalla 2017-10-16 a las 12.37.52

Imagen de dreamforce.com website

23 de Noviembre

Todavía hay mucha gente que no conoce Salesforce y menos la Comunidad alrededor de la plataforma, sin la cual muchos de nosotros no estaríamos aquí. Y para promocionar Salesforce y nuestros Developer Groups, voy a organizar junto con NTS y la colaboración de la Universidad de Deusto, un evento de 1 día entero en el que tendremos presentaciones de Saleforce Iberia, NTS y Deusto, para posteriormente comenzar con charlas sobre Salesforce para Admin y Developers y zona de Trailhead. No te lo pierdas y apúntate cuánto antes.

Si eres miembro del grupo, regístrate a través de él y del formulario oficial. Si no eres miembro del SDG y no quieres apuntarte, usa únicamente el formulario.

Platform Encryption

Summer time, but I do not want to miss the opportunity so share something even if this post is sorter than usual.

A year ago I completed Platform Encryption Trailhead  but I have started working with it few months ago.

What is Platform Encryption and Why do we need it?

You can find more information here, but basically it would allow us to protect some data, so that, it would not be visible. However this does not mean that the data is stored in database with a mask “****”. It is going to be saved as usual, but if your user doesn’t have certain rights, it will not be visible, and he would have a mask “****” as view effect .

Ok, let’s explain a bit better. If I need a field encrypted, because it is a password, I will use the existing Text (encrypted) field type. If I need to have my data visible in my organization, but if this is stoles, for instance, it should not be visible, then, I need to use existing field types, but enable Platform Encryption on those that have sensible data.

How to enable Platform Encryption?

First of all, you need to have rights, to manage encrypted data. For that, the first thing to do is to create a Permission Set, under System Mode, select Manage Encryption Keys.  Remember to assign it to your user.

If you need to see also encrypted data, you need to add to the same Permission Set, or a new one, View Encrypted Data right.

Then, you need to go to Setup, under Administration Setup you can find Platform Encryption

Captura de pantalla 2017-08-11 a las 18.36.28

The very first time you also need to generate a Tenant Secret just clicking on the button.

Captura de pantalla 2017-08-14 a las 11.40.38

Captura de pantalla 2017-08-14 a las 11.44.24

 

The last step is to decide what you need to encrypt. Right now Salesforce allows you to encrypt certain fields on certain Standard Objects.

Leads fields are in beta

And you can also encrypt custom fields on Standard and Custom objects as long as their field type are: Email, Phone, Text, Text Area, Long Text Area, URL, Date or Date/Time.

But before encrypt any field you need to be aware that there are certain limitations. Maybe one of the most important one is that you cannot filter, order or group by an encrypted field, so that your code can fail once you enable this feature in your organization.

 

Reviewing the code

Now it is time to review your current code to check if there is something failing now. The easiest example of failure is this SOQL in your code.

Select Id, Name From Account Where Name = :accName;

But now it fails at compilation time. And if you turn Encryption off, it continues failing … so what should you do at this point?

If your next step is to move to dynamic SOQL, it’s a good try. Now it saves, but it would fail at run time.

You need to find another way to get the information. Salesforce suggests to use SOSL, and this is an easy solution, as we only need to move to this piece of code. So let’s go for it and solve this issue.

Account acc = new Account();Account acc = new Account();        
acc.Name = 'PE Blog Test';        
insert acc;         

List<List<Account>> accList = [FIND 'PE Blog Test' 
                               IN ALL FIELDS 
                               RETURNING Account(Id, Name)];        

System.debug('Testing: ' + accList);

Captura de pantalla 2017-08-14 a las 12.08.35

Reviewing Unit Tests

Ok, this section sound similar to the previous one. Yes, you are right, but I wanted to highlight it here, because I was not used to work with SOSL and I found something interesting to share.

If I have below method:

public static testMethod void testAcountsWithSOSL() 
{ 
   List<Account> accList = [Select Id from Account]; 
   System.assertEquals(0, accList.size(), 'Error: There should not be any account.');
   
   Account acc = new Account(Name = 'Test'); 
   insert acc;
   accList = [Select Id from Account]; 
   System.assertEquals(1, accList.size(), 'Error: There should be an account.'); 

   List<List<Account>> accList2 = [FIND 'Test' IN ALL FIELDS RETURNING Account];
   System.assertEquals(1, accList2.get(0).size(), 'Error: There should be an account. And result is = ' + accList2); 
}

And run the test, the result is the below one:

Captura de pantalla 2017-08-14 a las 12.14.31

Exactly, it fails because accList2 is empty although I was expecting to get an element.

Doing some researches, I found this article about unit test. So it seems they provide a Test method that allows you to insert the Id of the record you need to look for and get it back. So that, the solution would be this:

public static testMethod void testAcountsWithSOSLFollowingGuide() 
{ 
   List<Account> accList = [Select Id from Account]; 
   System.assertEquals(0, accList.size(), 'Error: There should not be any account.');

   Account acc = new Account(Name = 'Test'); 
   insert acc;
   accList = [Select Id from Account]; 
   System.assertEquals(1, accList.size(), 'Error: There should be an account.'); 

   Id [] fixedSearchResults= new Id[1]; 
   fixedSearchResults[0] = acc.Id;
   Test.setFixedSearchResults(fixedSearchResults); 
   List<List<SObject>> searchList = [FIND 'Test' IN ALL FIELDS RETURNING Account(Id, Name)]; 
   System.assertEquals(1, searchList.get(0).size(), 'Error: There should be an account. And result is = ' + searchList);    
}

Captura de pantalla 2017-08-14 a las 12.19.46

Ok, so doing in this way, you are saved. However, be aware that your tests have the SeeAllData=false and you do not expect to get data from your org, other wise, it would fail too.

I hope this post help you to be closer to understand what you need to do before enabling Platform Encryption in your org.

Picklist dinámicos en Lightning Components

Mis últimos post técnicos en Español eran bastante básicos. Explicaba cómo crear páginas visualforce, controllers, etc. Sin embargo, desde entonces, Lightning Experience y Lightning Components han cogido el protagonismo en Salesforce.

Así que en esta entrada voy a explicar como crear una lista desplegable y poder determinar su valor por defecto desde App Builder, de forma que cada usuario puede decidir qué valor le conviene.

Caso de Uso:

Como usuario, me gustaría tener un lightning component que me muestre los eventos de Salesforce a partir de una ciudad elegida y decidir el valor por defecto de esa lista desplegable.

Captura de pantalla 2017-08-02 a las 18.28.06

Pasos:

Lista desplegable con valores fijos

Comencemos creando un componente con determinadas ciudades pero sus valores son fijos. Para poder testearlo, lo mostraré en la pestaña Home de mi App Salesforce Events.

Para no alargar el post pondré una breve explicación de lo más importante en el código

Captura de pantalla 2017-08-03 a las 16.58.42

SalesforceEventsComponent.cmp

<aura:component controller="SalesforceEventsController" 
      implements="flexipage:availableForAllPageTypes" 
      access="global">
    
   <!-- Lista de ciudades sobre la que iterar -->
   <aura:attribute name="cities" type="List" />  
   
   <!-- Valor seleccionado para poder usarlo más adelante.-->
   <!-- Indicamos que el valor por defecto es "Madrid" mediante hardcode -->
   <aura:attribute name="selectedValue" type="String" default="Madrid"/> 
   
   <!-- handler donde llamamos al método que me devuelve los valores de la lista -->
   <aura:handler name="init" value="{!this}" action="{!c.loadCities}" />    
   
   <!-- Usando Lightning Design System, creamos la lista desplegable -->
   <lightning:select name="mySelect" label="Select a city:" aura:id="mySelect" value="{!v.selectedValue}">    
      <aura:iteration items="{!v.cities}" var="theCity">        
         <option text="{!theCity}" value="{!theCity}" selected="{!theCity}"/>    
      </aura:iteration> 
   </lightning:select>
   
</aura:component>

SalesforceEventsComponentController.js

({
   loadCities: function (component, event, helper)    
   {
      //asigno a la variable cities de mi component los nombres de las ciudades
      component.set('v.cities',['London','Madrid','Paris','San Francisco']);
   }
})

Lista desplegable con valores dinámicos

El siguiente paso es recuperar de la base de datos los valores de la ciudades que tenemos. Según la imagen de más arriba, de la lista desplegable desaparecería San Francisco.

Captura de pantalla 2017-08-03 a las 17.10.17

Para ello modificaría el Controlador JavaScript que llamaría a un Controlador en Apex dónde hacemos la búsqueda en base de datos.

SalesforceEventsController.cls

//Esta clase puede ser también global
public with sharing class SalesforceEventsController
{
   //Recuerda añadir la anotación @AuraEnabled 
   //para el que método sea visible en el componente
   @AuraEnabled public static List<String> getCityNames()
   {        
      List<String> cityNames = new List<String>();               
      
     //TODO: Recuerda SOC por lo que una SOQL debería hacerse en otra clase 
     //destinada a ello.
     for(City__c c : [Select Id, Name From City__c])        
      {            
         cityNames.add(c.Name);        
      }                
      return cityNames;    
   }
}

SalesforceEventsComponentController.js

({
   loadCities: function (component, event, helper)
   {        
      var action = component.get("c.getCityNames");
      
      //asigno a la variable cities de mi component 
      //los nombres de las ciudades que he recuperado del controlador apex
      action.setCallback(this, 
                         function(a){
                            component.set("v.cities", a.getReturnValue());
                         });
      $A.enqueueAction(action);    
   }
})

Lista desplegable con valores dinámicos y Tabla con información

El siguiente paso es mostrar los valores de los eventos de Salesforce a partir de una selección en la lista desplegable.

SalesforceEventsController.cls

@AuraEnabled  
public static List<SalesforceEvent__c> getEventsByCity(String cityName)
{
    City__c theCity = [Select Id From City__c Where Name = :cityName Limit 1];        
    Id theCityId = theCity.Id;                
    List<SalesforceEvent__c> sEvents = new List<SalesforceEvent__c>();        
    
    //TODO: Recuerda SOC por lo que una SOQL debería hacerse en otra clase 
    //destinada a ello.
    for(SalesforceEvent__c e : [Select Id,EventName__c,EventDate__c From SalesforceEvent__c Where City__c = :theCityId])        
    {            
        sEvents.add(e);        
    }        
    return sEvents;    
}

En este punto me di cuenta que el refresco iba a ser más complicado de lo que tenía pensado ya que tuve que crear un componente hijo para la parte de los Salesforce Events, y un evento para poder hacer el refresco. Os dejo el enlace que usé como referencia.

SalesforceEventsEvent.evt

<aura:event type="APPLICATION">
   <!-- A partir de una ciudad seleccionada, calculo los eventos -->
   <aura:attribute name="citySelected" type="String" />
</aura:event>

SalesforceEventsComponent.cmp

<aura:component controller="SalesforceEventsController" implements="flexipage:availableForAllPageTypes" access="global">
   <!-- Attributes set on Design Page-->    
   <aura:attribute access="global" name="headerText" type="String" /> 
   <aura:attribute access="global" name="defaultCity" type="String" />        
   
   <aura:attribute name="cities" type="List" />    
   <aura:attribute name="selectedValue" type="String" default="Madrid"/>    

   <aura:handler name="init" value="{!this}" action="{!c.loadCities}" />

   <!-- onchange no funciona sin un handler relacionado con value el tag option -->
   <aura:handler name="change" value="{!v.value}" action="{!c.cityChange}"/>        

   <!-- Usaremos onChange para llamar a la función js que refrescará la lista -->
   <lightning:select name="mySelect" label="Select a city:" aura:id="mySelect" value="selectedValue" onchange="{!c.cityChange}"> 
      <aura:iteration items="{!v.cities}" var="theCity">        
         <option text="{!theCity}" value="{!theCity}" selected="{!theCity}"/>    
      </aura:iteration> 
   </lightning:select>        

   <h1>Salesforce Events:</h1>    
   <!-- Registro el evento y llamo al nuevo componente hijo para listar los eventos de Salesforce -->
   <aura:registerEvent name="appEvent" type="c:SalesforceEventsEvent"/>    
   <c:SalesforEventList />

</aura:component>

SalesforceEventsComponentController.js

({
   loadCities: function (component, event, helper)    
   { 
      var action = component.get("c.getCityNames"); 
      action.setCallback(this, function(a) 
      { 
         component.set("v.cities", a.getReturnValue());        
      });        
      $A.enqueueAction(action);    
   },        
   cityChange : function(component, event)    
   {        
      //Determino el nuevo valor por defecto que es el elegido
      component.set('v.selectedValue', component.find("mySelect").get("v.value"));        
      component.find("mySelect").get("v.value");
      
      //Paso al atributo citySelected del event el nuevo valor seleccionado
      var appEvent = $A.get("e.c:SalesforceEventsEvent");        
      appEvent.setParams({"citySelected" : component.get("v.selectedValue")});        
      appEvent.fire();    
   }
})

SalesforEventList.cmp

<aura:component controller="SalesforceEventsController" implements="flexipage:availableForAllPageTypes" access="global">

   <aura:handler event="c:SalesforceEventsEvent" action="{!c.loadEvents}"/>
   <aura:attribute name="cityToFilter" type="String" />    
   <aura:attribute name="sevents" type="SalesforceEvent__c[]" />         

   <aura:iteration items="{!v.sevents}" var="theEvent">        
      <li>{!theEvent.EventDate__c} - {!theEvent.EventName__c }</li>    
   </aura:iteration> 
</aura:component>

SalesforEventList.cmp

({ 
   loadEvents : function(component, event)    
   {        
      var citySentFromEvent = event.getParam("citySelected");
      component.set("v.cityToFilter", citySentFromEvent);            

      var action = component.get("c.getEventsByCity");        
      action.setParams({ cityName : component.get("v.cityToFilter") }); 
      action.setCallback(this, function(a) 
      { 
         component.set("v.sevents", a.getReturnValue());        
      });        
      $A.enqueueAction(action);                
   }
})

Determinar un valor por defecto desde App Builder

La idea aquí es dar la opción de poner el valor por defecto cómo parte de la configuración. Para ello usaremos la funcionalidad Dynamic PickList for Lightning Components.

Añadiremos 2 atributos a nuestro componente. Uno sera un texto con información y el segundo nuestra lista desplegable.

SalesforceEventsComponent.cmp

<aura:component controller="SalesforceEventsController" 
                implements="flexipage:availableForAllPageTypes" 
                access="global">
   
   <!-- Atributos para la Design Page--> 
   <!-- Este primer atributo no es requerido para la lista desplegable -->
   <aura:attribute access="global" name="headerText" type="String" /> 
   <!-- En vez de tener el valor por defecto con hardcode, usamos el atributo defaultCity -->
   <aura:attribute name="selectedValue" type="String" default="{!v.defaultCity}"/>

   <aura:attribute name="cities" type="List" />     
   <aura:attribute name="selectedValue" type="String"/>    
   <aura:handler name="init" value="{!this}" action="{!c.loadCities}" />    

   <!-- Utilizo como valor para value el atributo defaultCity -->
   <lightning:select name="mySelect" label="Select a city:" aura:id="mySelect" value="selectedValue">
      <aura:iteration items="{!v.cities}" var="theCity">        
         <option text="{!theCity}" value="{!theCity}" selected="{!theCity}"/>    
      </aura:iteration>
   </lightning:select>
...
</aura:component>

A continuación debemos crear nuestro design para añadir los nuevos campos a la Design Page.

SalesforceEventsComponent.design

<design:component label="Salesforce Events Cities">
   <design:attribute name="headerText" 
                     label="Header Text" 
                     description="Text that will appear in the header when the component is displayed" 
                     default="City" />    
   <!-- datasource nos ayuda a lincar 
        el atributo con una clase apex de dónde extraeremos 
        la información que necesitamos -->
   <design:attribute name="defaultCity" 
                     label="Default City PickList" 
                     datasource="apex://SalesforceEventsCityValuePickListDefault" />
</design:component>

Por último, debemos crear la clase apex lincada a nuestro atributo.

Aunque en la documentación aparece como global, si la hacemos pública funciona igualmente.

Esta clase extiende VisualEditor.DynamicPickList y aunque tiene cuatro métodos para sobre escribir, sólo son requeridos dos. Uno determina el valor por defecto en la lista desplegable, y el otro nos muestra los valores.

SalesforceEventsCityValuePickListDefault.cls

public class SalesforceEventsCityValuePickListDefault extends VisualEditor.DynamicPickList
{ 
   public override VisualEditor.DataRow getDefaultValue()    
   {        
      VisualEditor.DataRow defaultValue = new VisualEditor.DataRow('defaultLabel', 'defaultValue');        
      return defaultValue; 
   }        

   public override VisualEditor.DynamicPickListRows getValues()    
   { 
      VisualEditor.DataRow defaultRow = new VisualEditor.DataRow('defaultLabel', 'defaultValue'); 
      VisualEditor.DynamicPickListRows myRows = new VisualEditor.DynamicPickListRows(); 
      myRows.addRow(defaultRow);              

      List<String> cities = SalesforceEventsController.getCityNames();
      for(Integer i=0; i<cities.size(); i++)
      {
         VisualEditor.DataRow newRow = new VisualEditor.DataRow(cities.get(i), cities.get(i));
         myRows.addRow(newRow);
      }        
      return myRows; 
   }
}

Como muestro en la imagen de abajo, si abrimos el App Builder y seleccionamos nuestro Lightning Component, veremos los dos atributos que definimos en SalesforceEventsComponent.design siendo uno de ellos la lista desplegable con los valores que hemos recuperado de base de datos.

Captura de pantalla 2017-08-03 a las 20.03.46.png

Un último apunte antes de terminar el post. No estoy cubriendo error handling en mi código pero échale un vistazo a la ayuda de Salesforce para ello. Por ejemplo este y este.

Como has podido comprobar, es muy sencillo. Si eres nuevo en este tema, los pasos son fáciles de seguir. Si ya eres un experto, y has encontrado algo en el código que se puede mejorar. Dímelo y así otros podrán beneficiarse de tus conocimientos también.

Salesforce BigObjects

What is a BigObject?

Two years ago I started working with Big Objects, a Salesforce pilot that looked very interesting for me. And now that it seems that General Available time is closer, I would like to write about them.

Please, take into account that a Salesforce pilot could be discarded like Data Pipeline that it would not be GA at the end.

Big Objects is a new type of objects that Salesforce provides. Maybe the most important thing I can tell is that they do not count against the storage limit. Well, you could say that External Objects already do.

You can learn more about External Objects here.

Yes, you are right. But this time, data lives in your organisation instead of in an external storage and this means that you can work with them like with Custom / Standard objects, but if you go to Salesforce Storage screen, you would not find them.

Big Objects let you store and manage large amounts of data.

Use Case

But before starting with the explanation, let’s talk about the Use Case.

As a company that creates software, we have stories in order to develop new functionality. Before delivering them, we need to pass a code review, so that, we ensure the best to our end users.

Captura de pantalla 2017-07-07 a las 12.21.47

But how much storage should I use to keep code review records in our Production org? Should I remove them? If so, what about auditing?

BigObjects is your solution, so that, you can move old records to a new Code Review History BigObject record. Let’s focus for now on this new History object.

How to Create a BigObject?

Unfortunately you cannot do it in a declarative way. You need to define them via Metadata API and then do a deployment via cmd or workbench.

Before doing it, take into account:

  1. Indexes are required and in order to include them, you need to be on API 39.0 onwards.
  2. Once you deploy one, you cannot make any modification like amend a field type, change label, etc. However in a declarative way you can do small changes like the label and API name.
<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
  <deploymentStatus>Deployed</deploymentStatus>
  <fields>
    <fullName>CanBeMerged__c</fullName>
      <label>Can Be Merged</label>
      <length>80</length>
      <type>Text</type>
    </fields> 
  <fields>
  <fullName>CodeReviewDate__c</fullName>
    <label>Code Review Date</label>
    <type>DateTime</type>
    <required>true</required>
  </fields>
  <fields>
    <fullName>Comments__c</fullName>
    <label>Comments</label>
    <length>255</length>
    <type>Text</type>
  </fields>
  <fields>
    <fullName>Employee__c</fullName>
    <label>Employee</label>
    <referenceTo>Employee__c</referenceTo>
    <relationshipName>Employees</relationshipName>
    <required>true</required>
    <type>Lookup</type>
  </fields>
  <fields>
    <fullName>Score__c</fullName>
    <label>Score</label>
    <precision>1</precision>
    <scale>0</scale>
    <type>Number</type>
  </fields>
  <fields>
    <fullName>Story__c</fullName>
    <label>Story__c</label>
    <referenceTo>Story__c</referenceTo>
    <relationshipName>Stories</relationshipName>
    <required>true</required>
    <type>Lookup</type>
  </fields>
  <indexes>
    <type>PRIMARY</type> 
    <fullName>Test1CodeReviewPK</fullName>
    <fields>
      <name>Story__c</name>
      <sortDirection>DESC</sortDirection> 
    </fields>
    <fields>
      <name>Employee__c</name>
      <sortDirection>DESC</sortDirection>
    </fields>
    <fields>
      <name>CodeReviewDate__c</name>
      <sortDirection>DESC</sortDirection>
    </fields>
  </indexes>
  <label>Test1 Code Review History</label>
  <pluralLabel>Test1 Code Reviews History</pluralLabel>
</CustomObject>

Once you deploy it into your organization, you would find something like this under BigObject entry:

I decided to name is as Test1 Code Review History because once it is created I cannot modify it, and I didn’t want to use Code Review History label for now.

Captura de pantalla 2017-07-14 a las 13.03.54.png

What can we highlight?

  1. The API name ends with __b instead of __c or __x like custom and external objects.
  2. We will not find Standard fields.
  3. We can create as many custom fields as we want but right now it only supports these field types: DateTime, Lookup, Number, Text and Long TextArea. What about the rest? Try to be creative. For instance a Checkbox can be converted into number or text.
  4. You will not find any other section like PageLayout or Buttons for now.
  5. You will not find Triggers section. It is not allowed for now.
  6. You will not be allowed to create a custom tab related to this object. That is related to previous point and the PageLayout. To visualise Big Object records, you need to create a visualforce page or a lightning component.
  7. You can determine its CRUD and FLS via Profile or Permission Sets during the deploy process or in a declarative way. However for now BigObjects can be only created and read.

Captura de pantalla 2017-07-14 a las 13.44.36

How Can I Create Records?

There are different ways to create BigObject record, like using a csv file, use APIs like Bulk API or even Async SOQL, another Pilot I will talk about in a future entry.

In any case, here, I will focus on Apex.

As I mentioned above, you can only Read or Create records, and nowadays, you cannot use simple DML operations, there is a new one: database.insertImmediate(record)

Test1CodeReview__b cr = new Test1CodeReview__b();
cr.CanBeMerged__c = 'False';
cr.CodeReviewDate__c = System.today();
cr.Comments__c = 'I found a SOQL inside of a loop.';
cr.Employee__c = 'a6j24000000fxSL';
cr.Score__c = 0; 
cr.Story__c = 'a6k24000000k9bN';
database.insertImmediate(cr);

But something pretty cool that it was not available at the beginning, it is the fact we can use this method, to also update records. Actually it works like upsert. If I make a call where the record has a primary key that doesn’t exist, it would create a new record as we can see in the below image.

Captura de pantalla 2017-07-14 a las 14.28.04

However if the value already is in the platform, it will make a modification.

Test1CodeReview__b cr = new Test1CodeReview__b();
cr.CanBeMerged__c = 'False';
cr.CodeReviewDate__c = System.today();
cr.Comments__c = 'I found a SOQL inside of a loop.';
cr.Employee__c = 'a6j24000000fxSL';
cr.Score__c = 1; //Change to 1
cr.Story__c = 'a6k24000000k9bN';
database.insertImmediate(cr);

Captura de pantalla 2017-07-14 a las 14.35.49

If I come back to the original use case, I would like to move CodeReview__c records to Test1CodeReviewHistory__b records. In order to do that I only need to retrieve the record I need and create a new BigObject entry.

CodeReview__c cr = [Select Id, CanBeMerged__c, 
                           CodeReviewDate__c, Comments__c, 
                           Employee__c, Score__c, Story__c 
                    From CodeReview__c 
                    Limit 1];

String canBeMerged = cr.CanBeMerged__c == true ? 'True' : 'False';

Test1CodeReview__b crh = new Test1CodeReview__b();
crh.CanBeMerged__c = canBeMerged;
crh.CodeReviewDate__c = cr.CodeReviewDate__c;
crh.Comments__c = cr.Comments__c;
crh.Employee__c = cr.Employee__c;
crh.Score__c = cr.Score__c; 
crh.Story__c = cr.Story__c;
database.insertImmediate(crh);

How Can I Visualise Records?

As we have already mentioned, we cannot create a custom tab related to a BigObject and show all records. However we can query BigObjects, so what about if we create a custom page for that?

First of all we would need a controller. Really simple one. We have a method to retrieve all records and a get and set to show in a list what we retrieve.

public with sharing class CodeReviewController
{
   private static List<Test1CodeReview__b> codeReviewHistoryList; 
   
   public CodeReviewController()
   {
      setCodeReviewHistoryList(calculateCodeReviewHistoryList());
   } 

   public List<Test1CodeReview__b> calculateCodeReviewHistoryList()
   {
      return [SELECT Id, CanBeMerged__c, CodeReviewDate__c, 
                     Comments__c, Employee__c, Score__c, 
                     Story__c                
               FROM Test1CodeReview__b];
   }

   public static List<Test1CodeReview__b> getCodeReviewHistoryList()
   {
      return codeReviewHistoryList;
   }

   public static void setCodeReviewHistoryList(List<Test1CodeReview__b> value)
   {
      codeReviewHistoryList = value;
   }
}

Secondly, we have the visualforce page. Something to highlight is that right now, the standardController attribute doesn’t work, so we can only create a page with a customController. 

<apex:page showHeader="true" sidebar="true" controller="CodeReviewController">
   <apex:form>
      <apex:pageBlock title="Code Review History Records">
         <apex:pageBlockSection title="Code Review History" columns="1" collapsible="false">
	    <apex:pageBlockTable value="{!codeReviewHistoryList}" var="crHistory">
               <apex:column headerValue="Ready To Merge?">
	          <apex:outputField value="{!crHistory.CanBeMerged__c}"/>
               </apex:column>
	       <apex:column headerValue="Score">
	          <apex:outputField value="{!crHistory.Score__c}"/>
	       </apex:column>
               <apex:column headerValue="Comments">
	          <apex:outputField value="{!crHistory.Comments__c}"/>
               </apex:column>
               <apex:column headerValue="Story">
	          <apex:outputField value="{!crHistory.Story__c}"/>
               </apex:column>
               <apex:column headerValue="Reviewer">
                  <apex:outputField value="{!crHistory.Employee__c}"/>
               </apex:column>
               <apex:column headerValue="Code Review Date">
                  <apex:outputField value="{!crHistory.CodeReviewDate__c}"/>
               </apex:column>
            </apex:pageBlockTable>
         </apex:pageBlockSection>
      </apex:pageBlock>
   </apex:form>
</apex:page>

And the result is this one that includes the record created from scratch and the one that is retrieved from CodeReview__c object.

Captura de pantalla 2017-07-17 a las 9.14.52

Summary

I would like to share this table that summarise BigObjects and compare with Custom Objects. But before, I would like to also highlight that this new object type can be included in a package. You would find under CustomObject section:

Captura de pantalla 2017-07-17 a las 9.16.06

Summary Table:

Feature Custom Object Big Object
Creation Manual
Metadata
Metadata
API name myObject__c myObject__b
Track Activities
Track Field History, etc.
Options Available Options No Available
Field Types All Text ; Date/Time ; Lookup
Number ; Long Text Area
Able to edit fields Yes Yes (with restrictions)
Able to delete fields Yes No
Triggers; Field Sets; etc Options Available Options No Available
Reports Yes No
How to Populate records All CSV file
API (Bulk, SOAP)
Apex
Async SOQL
Can I amend a record? Yes Yes (with restrictions)
Can I see data creating a Tab Yes No
For free? Yes No — Talk with Salesforce
Storage It count against storage It doesn’t count against storage

Después de dreamOlé ’17

Y después de tanto preparativo llegó el día y pasó igual de rápido que un día de Reyes.

Laura ya había creado un flow impresionante así que todos sabíamos dónde teníamos que estar y qué hacer en cada minuto, pero aún así, corríamos de un lado para otro terminado de organizar los últimos detalles antes de que empezaran a llegar nuestros “invitados”.

2 horas más tarde de entrar por la puerta de OpenTalk, el equipo comenzaba el evento y presentaba a nuestra oradora principal Erica Kuhl que nos habló sobre la familia de Salesforce y el importante significado de palabras como Comunidad, Ohana, Trailblaizer, etc.

Estamos a la espera de la encuesta de satisfacción del evento, pero muchos de los asistentes nos dieron la enhorabuena por las sesiones. Variadas, interesantes, en ambos idiomas … pero en realidad el agradecimiento es para nuestros ponentes que hicieron un trabajo excelente sabiendo sortear contratiempos como los problemas de red que tuvimos e incluso un corte de luz!! Mil perdones a todos por estos fallos.

También teníamos un rinconcito para preguntar a los expertos con nuestro #WTFAQ

Captura de pantalla 2017-06-04 a las 21.54.53

Gracias Inés por la foto @Inescapinezka

Joshua y Julio hicieron un trabajo excelente y divertido presentando a los participantes de la DemoJam que finalmente ganó nuestro sponsor Copado. Enhorabuena!!

Y hablando de sponsors, volver a remarcar que todo esto no hubiera sido posible sin el apoyo de ellos. Gracias por creer en nosotros y lanzaros al vacío patrocinando un evento pionero en España y que nadie conocía.

Captura de pantalla 2017-06-04 a las 21.48.28

Nuestro amigo Eugenio Roldán cerró el evento hablando sobre Certificaciones y es que tuvimos la gran suerte de contar con él, primer Technical Architect Certified en España. Intentaré seguir tus consejos aunque estoy viendo que este examen es como sacarte unas oposiciones así que me temo que cuando lo tenga, ya habrá menos astronautas que certificados.

Y finalmente After-Party!! Más comida y bebida (la sangria riquísima) foto-matón (si, era el de First Dates!!) música, risas. Era el momento en que el relax llegaba al equipo y por fin podíamos disfrutar del día.

Captura de pantalla 2017-06-04 a las 21.54.30

Gracias J-Michel por la foto @jmmougeolle

Inés, Laura, Carlos, Julio, Yasmina, Roger, Alba, Carolina, ha sido un verdadero placer trabajar con vosotros durante estos últimos 9 meses – dreamOlé adictos!!

Y el año que viene más y mejor!!! – Sigue dreamOlé en twitter y se el primero en enterarte cuando abrimos el “speakers submission”, y el registro para no quedarte sin tú entrada.

London’s Calling ’17 and Visual Flow

Last 10th of February, I attended London’s Calling ’17 and I can say it was a great and funny day.

Captura de pantalla 2017-03-12 a las 19.22.17

And obviously, we had lot of opportunities to learn more about Salesforce. Actually I had the chance to run a session about Visual Flow where I got nice questions that I promised to review and answer, and here is the time for it:

1. Did you find any issue related to Communities?

When I created a new Community in my org and tried to add elements, I was not able to find the Flow Lightning Component that allows me to include visualflows in Lightning Experience. However, I can add visualforce pages, so if you have your flow embebed in a visualforce page and you set this page up to be used in Communities, then, it seems you can see your flow in the Community.

2. Did you find any issue during deployment?

Although my daily job tasks do not include deploying flows, I could say that those times that I tried I did not get any special issue to highlight. We just need to keep in mind few things, like also include those objects that flow references but this is like some other components. In addition, although in your org you have several versions, in change set you can only include the latest active one. And as I said in the session, in order to include a flow in package, it should be active, otherwise, it will not appear as an element to be included. You can find more information here.

3. What is coming in Spring ’17 release?

I talked about some Spring’17 features, but I also promised to review, the new FLS feature. But let start checking the behaviour if I do not have access to the object itself.

So having a user which profile only allows to Read and View All reservations:

Captura de pantalla 2017-03-12 a las 11.11.03.png

if I try to run the BookingSystem application, by the time I try to save the Reservation, I get an unhandled error (that’s why Salesforce encourage to cover these in the same way we do in apex) And the administrator receives an email with the log and the error:

Captura de pantalla 2017-03-12 a las 11.14.24Captura de pantalla 2017-03-12 a las 11.15.04

Let’s double check new FLS for Flow in Spring’17

Captura de pantalla 2017-03-12 a las 12.06.57

In this scenario, I have Filter Inaccessible Fields from Flow Request enabled in Process Automation Settings, and also, I have defined a Field Level Security of No Visible for Facility Price in my custom profile

Captura de pantalla 2017-03-12 a las 11.28.42.png

So, having this configuration, I have created also this flow in order to insert a new Facility via a Fast Create screen

Captura de pantalla 2017-03-12 a las 19.01.51.png

So in order to do that, I create a SObject and I use the input fields from the first screen in order to make an assignment to the SObject fields and finally pass the SObject to the Fast Create

Now, if I run the flow with the profile that doesn’t have price visibility, I can create a new record, but having an empty price.

Captura de pantalla 2017-03-12 a las 19.11.19.png

However, if I disable Filter Inaccessible Fields from Flow Request enabled in Process Automation Settings, and run the flow again, the result is different. The record is not half created and the administrator receives and email with the error.

So now, what should I do? Up to you really. You can handle the exception and provide a nice error message and don’t allow the end user to create a Facility record without the prices. However, if the field is not required for the business, you can leave this half creation.

Any other question about Visual Flow? Leave it as a comment and I will try to check it and reply you.