Monday, December 31, 2018

Rust: Ownership and Borrowing

The Stack and the Heap

Both the stack and the heap are parts of memory that are available to your code to use at runtime, but they are structured in different ways. The stack stores values in the order it gets them and removes the values in the opposite order. Adding data is called pushing onto the stack, and removing data is called popping off the stack.
All data stored on the stack must have a known, fixed size. Data with an unknown size at compile time or a size that might change must be stored on the heap instead. The heap is less organized: when you put data on the heap, you request a certain amount of space. The operating system finds an empty spot in the heap that is big enough, marks it as being in use, and returns a pointer, which is the address of that location. This process is called allocating on the heap. Because the pointer is a known, fixed size, you can store the pointer on the stack, but when you want the actual data, you must follow the pointer.
Accessing data in the heap is slower than accessing data on the stack because you have to follow a pointer to get there.
When your code calls a function, the values passed into the function (including, potentially, pointers to data on the heap) and the function’s local variables get pushed onto the stack. When the function is over, those values get popped off the stack.

Ownership Rules

  1. Each value in Rust has a variable that’s called its owner.
  2. There can be only one owner at a time.
  3. When the owner goes out of scope, the value will be dropped.
In the case of a string literal, we know the contents at compile time, so the text is hardcoded directly into the final executable. This is why string literals are fast and efficient. But these properties only come from the string literal’s immutability.

let s = "hello";

With the String type, in order to support a mutable, growable piece of text, we need to allocate an amount of memory on the heap, unknown at compile time, to hold the contents. This means:

  • The memory must be requested from the operating system at runtime. This is done when we call String::from.
  • We need a way of returning this memory to the operating system when we’re done with our String. In Rust, the memory is automatically returned once the variable that owns it goes out of scope. A scope is the range within a program for which an item is valid. The variable is valid from the point at which it’s declared until the end of the current scope.

    {
       let mut s = String::from("hello");
       s.push_str(", world!");
       println!("{}", s);
    };

    When a variable goes out of scope, Rust calls a special function for us. This function is called drop, and it’s where the author of String can put the code to return the memory. Rust calls drop automatically at the closing curly bracket.

Multiple variables interacting with the same data

Let’s look at this example:
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);   // Error

A String is made up of three parts, shown as below: a pointer to the memory that holds the contents of the string, a length, and a capacity. This group of data is stored on the stack. On the right is the memory on the heap that holds the contents.
When we assign s1 to s2, the String data is copied, meaning we copy the pointer, the length, and the capacity that are on the stack. We do not copy the data on the heap that the pointer refers to. In other words, the data representation in memory looks like below:
The above figure shows both data pointers pointing to the same location. This is a problem: when s2 and s1 go out of scope, they will both try to free the same memory. Freeing memory twice can lead to memory corruption, which can potentially lead to security vulnerabilities. To ensure memory safety, instead of trying to copy the allocated memory, Rust considers s1 to no longer be valid and, therefore, Rust doesn’t need to free anything when s1 goes out of scope. This is known as a move. In this example, we would say that s1 ptr was moved into s2.

Let’s look at this valid code, which seems to contradict what we just learned.
let x = 5;
let y = x;
println!("x = {}, y = {}", x, y);

The reason is that types such as integers that have a known size at compile time are stored entirely on the stack, so copies of the actual values are quick to make. Rust has a special annotation called the Copy trait that we can place on types like integers that are stored on the stack. If a type has the Copy trait, an older variable is still usable after assignment. Rust won’t let us annotate a type with the Copy trait if the type, or any of its parts, has implemented the Drop trait. Any group of simple scalar values can be Copy, and nothing that requires allocation or is some form of resource is Copy.

References and Borrowing

fn main() {
   let s1 = String::from("hello"); 
   let len = calculate_length(&s1); 
   println!("The length of '{}' is {}.", s1, len); 
} 

fn calculate_length(s: &String) -> usize {
   s.len() 
}

In the above example, we are passing &s1 into calculate_length and, in its definition, we take &String rather than String. These ampersands are references, and they allow you to refer to some value (s1) without taking ownership of it. Because it does not own it, the value it points to will not be dropped when the reference goes out of scope. Likewise, the signature of the function uses & to indicate that the type of the parameter s is a reference. When functions have references as parameters instead of the actual values, we don’t need to return the values in order to give back ownership, because we never had ownership.

We call having references as function parameters borrowing. As in real life, if a person owns something, you can borrow it from them. When you’re done, you have to give it back.



Share This:    Facebook Twitter

Saturday, December 29, 2018

Breadcrumb navigation in VSCode


Recently I have started working on VSCode for my Salesforce development. Earlier I was leveraging the Illuminated Cloud plugin of Webstorm for all my development activities.

So in Webstorm, there is a Structure window using which you can examine the structure of the file currently opened in the editor, be it an Apex class or trigger, Visualforce or Lightning markup file, or JavaScript, CSS, or XML metadata file. It was also easy to navigate between related files of a Lightning component without using the file or project explorer, whatever you say.

VSCode has a navigation bar above its contents known as Breadcrumbs. It shows the current location and allows you to quickly navigate between symbols and files. By default, this is disabled. To start using breadcrumbs, enable it with the View > Toggle Breadcrumbs command or via the breadcrumbs.enabled setting like below:
"breadcrumbs.enabled": true

Once enabled, you will be able to see a navigation bar. So if you check the image below, on clicking the name of the file on navigation bar, you will be able to see the related files of that Lightning component.



Similarly, you can examine the structure of the file currently in focus as below:



For code reformatting, Prettier works like a charm for JS files. But I hope that in the near future, Salesforce add code reformatting capability for Apex files. As of now, there is a plugin Uncrustify which can be used to format Apex files. You can setup this plugin with VSCode, the instructions of which can be found in the link below:
https://salesforce.stackexchange.com/questions/212382/format-apex-code-visual-studio-code

Share This:    Facebook Twitter

Friday, July 20, 2018

Options and settings when building relationship fields in Salesforce


Lookup relationship options

When you create a lookup field on an object, you can choose whether the lookup field is required or optional. If the lookup field is optional, you can specify one of three behaviors to occur if the lookup record is deleted:
  1. Clear the value of this field: This is a default option and is a good choice when the field does not have to contain a value from the associated lookup record.

  2. Don’t allow deletion of the lookup record that’s part of a lookup relationship: This option prevents the lookup record from being deleted and is a good choice for restricting deletions if you have dependencies, such as workflow rules, based on the lookup relationship.

  3. Delete this record also: This option works similar to the master-detail relationship and deletes the record whenever the lookup record is deleted. Choosing this option can result in a cascade-delete. A cascade-delete bypasses security and sharing settings, which means users can delete records when the target lookup record is deleted even if they don’t have access to the records. To prevent records from being accidentally deleted, cascade-delete is disabled by default. Contact Salesforce to get the cascade-delete option enabled for your organization.

    This option is only available within custom objects and is not available for standard objects. However, the lookup field object can be either a standard or custom object.

Master-detail relationship options

  1. Allow reparenting option: By default, records in master-detail relationships cannot be reparented. However, you can allow child records in a master-detail relationship to be reparented to a different parent by selecting Allow reparenting option in the master-detail relationship definition.

Other links for references:
Share This:    Facebook Twitter

Wednesday, July 18, 2018

Salesforce Enterprise Territory Management: running assignment rules via API


Enterprise Territory Management is an account-sharing model that grants access to accounts based on the attributes of the accounts. A territory is a collection of the account records and business users where the users have minimum read access to the accounts, irrespective of who owns the account records. By enabling territory settings, users can get read, read/write, or owner-like access to the accounts in that territory. For comprehensive instructions for implementing this feature, see the Enterprise Territory Management Implementation Guide.

Salesforce allows you to add accounts manually to territories or define account assignment rules that assign accounts to territories for you. If you want the rule to run automatically when a user creates or updates an account, then you need to understand how the dynamic assignment will work when an account record is updated via UI or API.

1. If the account is updated via the UI, "Evaluate this account against territory rules on save" checkbox needs to be selected. You can refer the article below to add this checkbox to the page layout:
https://help.salesforce.com/articleView?id=000004725&type=1

2. If the account is updated via API, AssignmentRuleHeader must be specified in the update() call of an Account for the territory assignment rules to be applied as per the SOAP API Developer Guide.

Make sure that the Active checkbox is selected on the rule edit page, or else rules won't run automatically.

To test the automatic assignment of territories using the 2nd approach, I created a territory assignment rule DEMO_TERRITORY. I configured this rule in such a way that it will assign an account record to a territory with the same name, i.e. DEMO_TERRITORY, in my territory hierarchy.

I also created a custom field "Account Territory" on Account object; when this field is populated with the name of the territory, a trigger is invoked which will then call a future method.

trigger AccountAfterUpdate on Account (after update) { List<Id> accountIds = new List<Id>(); for (Account acc : Trigger.new) { if (acc.Run_Territory__c) { accountIds.add(acc.Id); } } if (accountIds.size() > 0) { Territory2Controller.runTerritoryRulesFuture(accountIds, UserInfo.getSessionId()); } }
This future method contains the main logic to run the assignment rules. It will create a soap envelope, populate the sessionId and relevant account Ids for which assignment rules need to run. Finally using HTTP Request and Response objects, I invoked this SOAP web service.

All territory assignment rules are applied for the account(s) if for the element name useDefaultRule, value is true. If the value is false, then no territory assignment rules are applied.

This is the source code of the future method.

I also updated Run_Territory__c to false in the webservice call to prevent recursive future calls as I was getting System.CalloutException: Callout loop not allowed exception.

Please remember that the maximum records in an update request can be 200. You can't just dump in thousands of records in an API call.
Share This:    Facebook Twitter

Tuesday, June 26, 2018

Asynchronous Apex: Using Queueable Apex


Queueable jobs are similar to future methods, but they provide you with these additional benefits.
  • Getting an ID for your job: When you submit your job by invoking the System.enqueueJob method, the method returns the ID of the new job. This ID corresponds to the ID of the AsyncApexJob record. You can use this ID to identify your job and monitor its progress.
  • Using non-primitive types: Your queueable class can contain member variables of non-primitive data types, such as sObjects or custom Apex types.
  • Chaining jobs: You can chain one job to another job by starting a second job from a running job. Chaining jobs is useful if you need to do some processing that depends on another process to have run first.
public class AsyncExecutionExample implements Queueable {
    public void execute(QueueableContext context) {
        Account a = new Account(Name='Acme',Phone='(415) 555-1212');
        insert a;        
    }
}

To add this class as a job on the queue, call this method:
ID jobID = System.enqueueJob(new AsyncExecutionExample());

Chaining Jobs

To chain a job to another job, submit the second job from the execute() method of your queueable class. You can add only one job from an executing job, which means that only one child job can exist for each parent job. You can refer the code below for chaining jobs:

...
...
...
for (Case caseRecord : cases)
 caseRecord.Status = 'Closed';

for (Lead lead : leads)
 lead.Status = 'Inactive';

Map<Integer, List<sObject>> indexToObjects = new Map<Integer, List<SObject>>();
indexToObjects.put(0, cases);
indexToObjects.put(1, leads);

Integer startingIndex = 0;
System.enqueueJob(new Worker(indexToObjects, startingIndex));

Worker.cls
public without sharing class Worker implements Queueable {
 Map<Integer, List<sObject>> indexToObjectUpdates;
 Integer index;

 public Worker(Map<Integer, List<sObject>> indexToObjectUpdates, Integer index) {
  this.indexToObjectUpdates = indexToObjectUpdates;
  this.index = index;
 }

 public void execute(QueueableContext context) {
  if (!indexToObjectUpdates.containsKey(index))
   return;

  update indexToObjectUpdates.get(index);

  Integer nextIndex = index + 1;
  if (indexToObjectUpdates.containsKey(nextIndex))
   System.enqueueJob(new Worker(indexToObjectUpdates, nextIndex));
 }
}

Points to remember:
  • Apex allows HTTP and web service callouts from queueable and chained queueable jobs, if they implement the Database.AllowsCallouts marker interface.
  • You can monitor the status of your job programmatically by querying AsyncApexJob or through the user interface in Setup by entering Apex Jobs in the Quick Find box, then selecting Apex Jobs.
Share This:    Facebook Twitter

Sunday, May 13, 2018

A simple Node.js web application


const express = require("express");
const app = express();

const Joi = require("joi");

app.use(express.json());

const genres = [
  { id: 1, name: "Action" },
  { id: 2, name: "Horror" },
  { id: 3, name: "Romance" }
];

app.get("/api/genres", (req, res) => {
  res.send(genres);
});

app.get("/api/genres/:id", (req, res) => {
  const genre = genres.find(c => c.id === parseInt(req.params.id));
  if (!genre)
    return res.status(404).send("The genre with the given ID was not found.");
  res.send(genre);
});

app.post("/api/genres", (req, res) => {
  const { error } = validateGenre(req.body);
  if (error) return res.status(400).send(error.details[0].message);

  const genre = {
    id: genres.length + 1,
    name: req.body.name
  };
  genres.push(genre);
  res.send(genre);
});

app.put("/api/genres/:id", (req, res) => {
  const genre = genres.find(c => c.id === parseInt(req.params.id));
  if (!genre)
    return res.status(404).send("The genre with the given ID was not found.");

  const { error } = validateGenre(req.body);
  if (error) return res.status(400).send(error.details[0].message);

  genre.name = req.body.name;
  res.send(genre);
});

app.delete("/api/genres/:id", (req, res) => {
  const genre = genres.find(c => c.id === parseInt(req.params.id));
  if (!genre)
    return res.status(404).send("The genre with the given ID was not found.");

  const index = genres.indexOf(genre);
  genres.splice(index, 1);

  res.send(genre);
});

function validateGenre(genre) {
  const schema = {
    name: Joi.string()
      .min(3)
      .required()
  };

  return Joi.validate(genre, schema);
}

const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}...`));

Share This:    Facebook Twitter

Monday, April 16, 2018

Salesforce Lightning: How to deal with cookies



You can use the below helper method to create a cookie.


So as an example, to create a cookie that stores the name of a visitor, you can call the helper method like below:
helper.createCookie('username', 'John Doe', 7);

To create a session cookie, don't pass any value for days. So you will be calling the helper method like below:
helper.createCookie('username', 'John Doe');

After executing the code above, the name of the cookie generated is LSKey[c]username. The name of the cookie is prefixed with LSKey[<c>] where c is the namespace. So to retrieve the value of this cookie, use the below code:


So as an example, to retrieve the cookie value, you can call the helper method like below:
helper.getCookie('username');

Check the link in the references section to check how to delete the cookie.

References: https://www.sitepoint.com/how-to-deal-with-cookies-in-javascript/
Share This:    Facebook Twitter

Saturday, April 14, 2018

Testing the performance of Salesforce Lightning components


Unlike regular timestamps created with Date.now(), a high resolution timestamp is precise to a thousandth of a millisecond. Having this level of precision can be very useful when testing code that needs to run really fast. For those that work in environments that demand lightning fast code execution, this new level of accuracy is incredibly useful. To generate a high resolution timestamp, you need to use the now method that is available on the performance object.

Please note that while Date.now() returns the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC, performance.now() returns the number of milliseconds, with microseconds in the fractional part, from performance.timing.navigationStart(), the start of navigation of the document, to the performance.now() call.

How to use performance.now() ?
You need to create a variable before and after the piece of code you wish to test, and populate those variables with the value returned by performance.now(). Subtracting the first variable from the second variable will then give you the amount of time that your code took to execute.

The code below shows a simple example of how to do this. I am calling a server-side controller action from a client-side controller of a Lightning component. Before adding the server-side controller action to the queue of actions to be executed, I have declared startTime variable, populating with the value returned by performance.now(). After the server-side action is completed, I am checking the amount of time the code took to execute.
callServer: function (component, method, params, helper, callback) {

    var action = component.get(method);
    if (params) {
        action.setParams(params);
    }

    action.setCallback(this, function (response) {
     console.log('Time taken:', (performance.now() - startTime));
        var state = response.getState();
        if (state === "SUCCESS") {
            callback.call(this, response.getReturnValue());
        } else if (state === "ERROR") {
         console.log(response.getError());
        }
    });
    var startTime = performance.now();
    $A.enqueueAction(action);

}


Share This:    Facebook Twitter

Sunday, April 8, 2018

Salesforce Lightning: Fetch data from an external API using Ajax


We will be using a very simple API which takes a GET request formatted as a certain URL, returning some random Chuck Norris jokes...lol, without requiring Apex code. So user will enter a number, and on click of Search button, will make a request to http://api.icndb.com/jokes/random/ API, get the response, parse it and display it on the page.

<aura:application extends="force:slds">

    <aura:attribute name="number" type="Integer" default="5"/>
    <aura:attribute name="html" type="String"/>

    <div class="slds-p-left_large">

        <h2 class="slds-text-heading_large">Chuck Norris Jokes Generator</h2>

        <lightning:input type="number"
                         aura:id="number"
                         label="Enter a number"
                         class="slds-m-around_small slds-size_1-of-8"/>

        <lightning:button variant="brand"
                          label="Get Jokes"
                          class="slds-m-around_medium"
                          onclick="{!c.handleClick }"/>

        <aura:unescapedHtml value="{!v.html}"/>

    </div>

</aura:application>
({
    handleClick : function(component, event, helper) {
        const number = component.find('number').get('v.value');

        if (number) {
            component.set('v.number', number);
            helper.handleAjaxRequest(component, event, helper);
        }

    }
})
({
    handleAjaxRequest : function(component, event, helper) {
        const xhr = new XMLHttpRequest();

        const number = component.get('v.number');

        const url = 'http://api.icndb.com/jokes/random/' + number;

        xhr.open('GET', url, true);

        xhr.onload = function() {
            if(this.status === 200) {
                const response = JSON.parse(this.responseText);

                let output = '<ul class=\'slds-list--dotted\'>';

                if(response.type === 'success') {
                    response.value.forEach(function(joke){
                        output += `<li>${joke.joke}</li>`;
                    });
                }

                output += '<ul>';
                component.set('v.html', output);
            }
        };

        xhr.send();
    }
})

The XHR object has properties and methods associated with it, one of which is open, where we specify type of request we want to make and the URL we want to make it to. The 3rd parameter is set as true as we want to make it asynchronous. The rest of the code is self-explainable.

As a last step, we need to add http://api.icndb.com as a trusted site or else our Lightning component will not be able to make a request and you will notice an error message in console something like this: Refused to connect to 'https://api.icndb.com/jokes/random/6' because it violates the following Content Security Policy directive:....."

So navigate to Setup → CSP Trusted Sites, and add the following entry.


That's it. Enjoy the jokes.

Now authentication is required for a lot of external APIs which are intricate ones using OAuth, and that can get complicated. So just remember that not all APIs give you as much freedom as they do here. You normally have to register your application with their systems and then you will have to authenticate. Different rules, different APIs.
Share This:    Facebook Twitter

Wednesday, March 28, 2018

Customize lightning:combobox (picklist) component using only CSS

The code below will create a dropdown list of selectable options. This code is taken from the Lightning documentation:
https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/aura_compref_lightning_combobox.htm

<aura:application extends="force:slds">
    <aura:attribute name="statusOptions" type="List" default="[]"/>

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

    <div class="slds-size--1-of-4 slds-p-around--large">

        <lightning:combobox aura:id="selectItem" name="status" label="Status"
                            placeholder="Choose Status"
                            value="new"
                            onchange="{!c.handleOptionSelected}"
                            options="{!v.statusOptions}"/>

    </div>

</aura:application>
({
    loadOptions: function (component, event, helper) {
        var options = [
            {value: "new", label: "New"},
            {value: "in-progress", label: "In Progress"},
            {value: "finished", label: "Finished"}
        ];
        component.set("v.statusOptions", options);
    },

    handleOptionSelected: function (component, event) {
        var selectedOptionValue = event.getParam("value");
        console.log(selectedOptionValue);
    }
})

This is how it looks:

To hide the text label (Status), override this css class slds-form-element__label, and set display property as none. You can also set the variant attribute of this component as label-hidden.

As per the current implementation, this component doesn't support selection of multiple options. So there is no need of displaying the check-icon besides the option values. To hide this icon, override this css class slds-listbox__icon-selected and set display property as none. This is how the picklist looks now:

Now when the dropdown list is visible, we notice that the arrow icon is still pointing downwards. Let's manipulate this icon so that it points upwards in this scenario. For this, add a transform property to this element and rotate 180 degrees, like below:
.THIS .slds-dropdown-trigger.slds-is-open .slds-combobox__form-element .slds-icon {
    transform: rotate(180deg);
}

This is how it looks now:

Now let's change the background color of selectable options on hover.
.THIS .slds-dropdown-trigger.slds-is-open .slds-listbox__option.slds-has-focus {
    background-color: cyan;
}

This is how it looks now:

Notice the blue border glow around the read-only inputbox. You can change this color to cyan like below:
.THIS .slds-input:focus, .THIS .slds-input:active {
    border-color: cyan;
    box-shadow: 0 0 3px cyan;
}

This is how the lightning:combobox looks now, quite a simple one:

Finally, this is how the CSS style resource looks like:
.THIS .slds-form-element__label, .THIS .slds-listbox__icon-selected  {
    display: none;
}

.THIS .slds-dropdown-trigger.slds-is-open .slds-combobox__form-element .slds-icon {
    transform: rotate(180deg);
}

.THIS .slds-dropdown-trigger.slds-is-open .slds-listbox__option.slds-has-focus {
    background-color: cyan;
}

.THIS .slds-input:focus, .THIS .slds-input:active {
    border-color: cyan;
    box-shadow: 0 0 3px cyan;
}


Share This:    Facebook Twitter

Tuesday, February 13, 2018

Salesforce Lightning: What browser am I using? What version is my browser?


You can use $Browser global value provider to return information about the hardware and operating system of the browser accessing the application as described in the link below:
https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/expr_browser_value_provider.htm

But it cannot be used to detect the browser type or version number.

You can use the below code to detect the browser on which you are running your Lightning application. Here, I am checking whether I am on Internet Explorer or not:

checkBrowser: function (component) {

    var browserType = navigator.sayswho= (function(){
        var ua= navigator.userAgent, tem,
            M= ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
        if(/trident/i.test(M[1])){
            tem=  /\brv[ :]+(\d+)/g.exec(ua) || [];
            return 'IE '+(tem[1] || '');
        }
        if(M[1]=== 'Chrome'){
            tem= ua.match(/\b(OPR|Edge)\/(\d+)/);
            if(tem!= null) return tem.slice(1).join(' ').replace('OPR', 'Opera');
        }
        M= M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];
        if((tem= ua.match(/version\/(\d+)/i))!= null) M.splice(1, 1, tem[1]);
        return M.join(' ');
    })();
    if (browserType.startsWith("IE")) {
        component.set("v.isIE", true);
    }
}

If the value of isIE attribute is true, then you are running on Internet Explorer. Similarly you can check for Chrome, Firefox, Opera and other browsers.
Share This:    Facebook Twitter

Thursday, January 4, 2018

Lightning: Star rating component


This is a simple, highly customizable star rating component.

Attributes:
value: this is the actual value which represents the value of rating
readonly: when set to true, the rating cannot be edited.

To use this component:
<c:StarRating value="2" readonly="false"/>

Refer the github link for the code:
https://github.com/iamsonal/Star-Rating
Share This:    Facebook Twitter

Total Pageviews

My Social Profiles

View Sonal's profile on LinkedIn

Blog Archive

Tags

__proto__ $Browser Access Grants Accessor properties Admin Ajax AllowsCallouts Apex Apex Map Apex Sharing AssignmentRuleHeader AsyncApexJob Asynchronous Auth Provider AWS Callbacks Connected app constructor Cookie CPU Time CSP Trusted Sites CSS Custom settings CustomLabels Data properties Database.Batchable Database.BatchableContext Database.query Describe Result Destructuring Dynamic Apex Dynamic SOQL Einstein Analytics enqueueJob Enterprise Territory Management Enumeration escapeSingleQuotes featured Flows geolocation getGlobalDescribe getOrgDefaults() getPicklistValues getRecordTypeId() getRecordTypeInfosByName() getURLParameters Google Maps Governor Limits hasOwnProperty() Heap Heap Size IIFE Immediately Invoked Function Expression Interview questions isCustom() Javascript Javascript Array jsForce Lightning Lightning Components Lightning Events lightning-record-edit-form lightning:combobox lightning:icon lightning:input lightning:select LockerService Lookup LWC Manual Sharing Map Modal Module Pattern Named Credentials NodeJS OAuth Object.freeze() Object.keys() Object.preventExtensions() Object.seal() Organization Wide Defaults Override PDF Reader Performance performance.now() Permission Sets Picklist Platform events Popup Postman Primitive Types Profiles Promise propertyIsEnumerable() prototype Query Selectivity Queueable Record types Reference Types Regex Regular Expressions Relationships Rest API Rest Operator Revealing Module Pattern Role Hierarchy Salesforce Salesforce Security Schema.DescribeFieldResult Schema.DescribeSObjectResult Schema.PicklistEntry Schema.SObjectField Schema.SObjectType Security Service Components Shadow DOM Sharing Sharing Rules Singleton Slots SOAP API SOAP Web Services SOQL SOQL injection Spread Operator Star Rating stripInaccessible svg svgIcon Synchronous this Token Triggers uiObjectInfoApi Upload Files VSCode Web Services XHR
Scroll To Top