Study Notes for PDII Certification
I was tasked with taking the PDII Salesforce certification in 24hrs, and making it a top priority. The following are notes taken while cram studying and taking practice exams.
Localization & Multi Currency
General Guidelines
- Dynamic Apex and SOQL can be used for code that is language / locale-dependent
System.UserInfohas some methods that you can use for more dynamic and flexible codegetDefaultCurrency()getLocale()getLanguage()isMultiCurrencyOrganization()
Multi Currency Support
CurrencyTypeobject: stores currencies used by orgsDatedConversionRateobject: stores dated exchange ratesCurrencyIsoCodefield: defines which currency is used in a recordconvertCurrency()function: performs numeric conversion of a record’s currency value into the user’s currency value in SOQL
SELECT Name, format(convertCurrency(Purchase_Cost__c)) FROM Bike_Part__c
FORMAT()function: displays a currency value both in the original record source and int he currency of the current user in SOQL
SELECT Name, format(Purchase_Cost__c) FROM Bike_Part__c
- Visualforce
<apex:outputField />: Used to automatically format currency data & supports multiple currencies
Multi Language Support
- Translation Workbench
- Custom Labels: Can add translations to a single custom label, you don’t need multiple labels
System.Label.translationExists(namespace, label, language)- Checks if the translation existsSystem.Label.get(namespace, label [language])- Dynamically retrieve labels
- Be cognizant about text field lengths to support translations into other languages that might result in longer string lengths
- E.g., 3 words in English translated to Russian might be 15 words
ObjectType.[object].fields.name.label: can be used to display labelstoLabel()function can be used in SOQL to translate labelsSystem.Label: Apex method for translation-friendly error messages- Consider using Visualforce email templates instead of standard email templates for locale-aware & translation-friendly templates
LWCs & Multi Currency / Locales
@salesforce/i18n/locale&@salesforce/i18n/currencymodules for locale and multi currency support
import LOCALE from '@salesforce/i18n/locale'
import CURRENCY from `@salesforce/i18n/currency`
...
const date = new Date(2025, 1, 2)
this.localDate = new Intl.DateTimeFormat(LOCALE).format(date)
const setting = Intl.NumberFormat(LOCALE, { style: 'currency', currency: CURRENCY })
Platform Event-based Trigger maintenance
- An Apex trigger can suspend its subscription to an event stream, which in this state doesn’t invoke the trigger to process any published messages
- It can be resumed to either start with the earliest unprocessed event message when the the subscription was suspended, or process new event messages only
- Unlike standard SObject triggers, which you simply “deactivate,” Platform Event triggers have a specific “Resume” or “Suspend” state. You manage this in the Setup menu:
- Navigate to Platform Events.
- Click on the specific Event name.
- Scroll down to the Subscriptions related list.
- Next to your Apex Trigger, you will see the Manage link or the current status (e.g., “Running”).
- Click Suspend.
- When suspended, the trigger stops executing. However, the events are still being published and stored in the Event Bus.
- When you resume, you have 2 options:
- Resume from Tip (New Events): If you choose this, the trigger ignores everything that happened during the suspension. It starts processing only the events that arrive after you click Resume.
- Resume from Last Processed (Replay): This is the “graceful” approach. If you choose to resume from the last processed event, the platform will “replay” the missed events to your trigger in the order they were received.
- Important Note: Salesforce stores events for 72 hours (in standard high-volume events).
- Consider how governor limits might play into your code when it’s processing large amounts of events in a “catch-up” scenario
EventBus.RetryableException: If your trigger was suspended because it was failing, simply resuming it might cause it to fail again on the exact same message.- If your code uses
EventBus.RetryableException, the system will attempt to re-process the batch up to 9 times. - If it keeps failing, the trigger might become automatically suspended by the system to prevent a loop of failures.
- If your code uses
SOQL Query Selectiveness & Query Performance
- When do you need query selectiveness?
- When you hit LDV (large data volumes) with records in the millions, you can get
Non-selective queryexceptions hit with standard SOQL queries
- When you hit LDV (large data volumes) with records in the millions, you can get
- What makes a query “selective”?
- If it uses a filter in the
WHEREclause that is powerful enough to narrow down the results to a small fraction of the total records - You only need one highly selective indexed field in your
WHEREclause to make the entire query selective
- If it uses a filter in the
- Threshold rules - There is a “tipping” point
- For objects with 1M or fewer:
- Standard Index: Less than 30% of records
- Custom Index: Less than 10% of records
- For objects with more than 1M records, the “selective vs. not selective” outcome is no longer about percentages, but about the total number of records returned
- Standard Index: Max 300,000
- Custom index: Max 100,000
- For objects with 1M or fewer:
- Standard Indexes: Created automatically by the platform on the following fields:
- ID
- Name
- OwnerId
- CreatedDate
- LastModifiedDate
- Lookup fields
- Master-Detail fields
- Custom Indexes: Created by marking a field as
Uniqueor asExternal ID- Note: You can also contact SFDC customer support to request that they add a custom index on a different field that isn’t unique
- SFDC support can also create composite indexes, combining two fields together
- Note: You can also contact SFDC customer support to request that they add a custom index on a different field that isn’t unique
- Gotcha’s: Times when a full table scan is run and will ignore indexes
- Using negative operators:
!=,NOT LIKE,EXCLUDES - Leading Wildcards:
LIKE '%Acme'- Note: Trailing wildcards are actually still ok
- Comparisons to Null:
WHERE Custom_Field__c = null - Complex Logic: Using
ORcan often break selectivity if one side of theORisn’t indexed- If you use
OR, both sides of theORmust be selective and indexed. - E.g.,
WHERE Indexed_Field__c = 'A' OR Non_Indexed_Field__c = 'B'- This query will almost always be Non-Selective because the second part forces a full table scan to find records that might not meet the first criteria.
- If you use
- Formula Fields: Standard indexes don’t cover formula fields
- There is an exception to this for “deterministic” formula fields, see below
- Using negative operators:
- Formula Fields: “Deterministic” vs. “Non-deterministic”
- Deterministic (Indexable): The value only changes when the record itself changes (e.g., FirstName + ” ” + LastName).
- Non-Deterministic (NOT Indexable): The value changes based on “outside” factors like time or other records.
- Example: TODAY(), NOW(), or a formula that references a field on a different object via a lookup.
- “Skinny” Tables
- Behind-the-scenes performance tool to speed up reports, list views, and SOQL queries for objects with millions of records
- CREATED BY SALESFORCE SUPPORT upon your request
- Can store up to 100 fields
- No formula fields or Long Text Area fields
- No soft-deleted (in Recycle Bin) records are stored in them
- Automatically updated from the standard table
- You do NOT have to explicitly point SOQL queries to the Skinny Table
- The SFDC SOQL Query optimizer will do this for you automatically
- Supported Objects:
- Standard Objects: Account, Contact, Opp, Lead, & Case
- All custom objects
Salesforce Big Objects
- Standard, normal objects might crawl when you hit 10-20M records
- Big Objects are built to store billions of records
- Provide consistent perf for massive datasets
- Stored in a NoSQL DB (HBase) apart from the traditional relational database that stores the rest of your data
- Used for archiving, historical tracking, or storing “log-style” data that you don’t need to edit frequently, but might need to query for compliance or analytics
- 2 types:
- Standard Big Objects: Defined by Salesforce - E.g.,
FieldHistoryArchive - Custom Big Objects: Defined by you - ending in
__binstead of__c
- Standard Big Objects: Defined by Salesforce - E.g.,
- When to consider using Big Objects:
- Auditing & Compliance: Storing 10 years of login history or field changes.
- Historical Archive: Moving “Closed/Lost” Opportunities from 5 years ago out of the standard database to save on storage costs.
- 360-Degree View: Storing billions of rows of raw “clickstream” data from a website to show a customer’s activity in a Lightning Component.
- If you need to query millions of rows from a Big Object and the query is too complex for standard SOQL, the answer is to use Async SOQL.
- This allows you to run a query in the background and pipe the results into a standard custom object for viewing.
How are they different?
- You cannot report on these using standard SFDC reports or dashboards
- Basically to visualize this data at all you’ll need custom UI (LWC, Aura, VF) or Tableau / Tableau CRM
- The Index (“Index Case”): requires an upfront-defined index (called an Index Case) the moment you create the Big Object
- Composed of 1 or more fields
- Order of the fields in the index matters
- This will determine how you query the data
- Index fields must be marked as Required
- No Sharing Rules or Record-triggered automations
- Do not support Apex Triggers, Flows, Process Builders, or standard Sharing Rules
- They are flat data stores
- If you need to process data as it comes in, you usually use a normal object or platform event first, then move the data to a Big Object later
- Asynchronous Insert or Updates
- You don’t use standard
insertorupdateDML - Use instead
Database.insertImmediate(listOfBigObjects)- Async operation
- Doesn’t support rollbacks
- Doesn’t return errors the way normal DML does
- You don’t use standard
- Querying
- SOQL is restricted by your Index Case
- Must include fields in the ORDER that they appear in your Index
- E.g., if the index case is
Account__c,Year__c,Month__c:- ✅
WHERE Account__c = ... - ✅
WHERE Account__c = ... AND Year__c = 2024 - ❌
WHERE Year__c = 2024<— Fails b/c we skipped the first field in the index
- ✅
- E.g., if the index case is
- You can only use comparison operators (
<,>,<=,>=) on the LAST field in your query- All preceding fields must use the equals sign (=)
- Deletes are permanent, no Recycle bin here
Salesforce Connect & External Objects
- Used essentially as a “virtual data integration”
- Allows you to view, search, and modify data stored outside of SFDC in external systems
- Connect to these systems using either an OData adapter or a custom Apex adapter
- Data is never imported, it’s fetched in real time when a user clicks on a record
- When a user views an External Object record:
- SFDC sends request to external system using OData or custom Apex adapter
- External system sends back the data in JSON or XML
- SFDC renders the data on a standard Page Layout or in a SOQL result
- When a user views an External Object record:
- These object definitions end in
__xinstead of__c - When to use Salesforce Connect:
- You have Large Data Volumes that you don’t want to pay to store in Salesforce.
- You need Real-Time Data (you can’t wait for a nightly sync).
- You only need to see small amounts of data at a time (e.g., one customer’s order history).
- Avoid Salesforce Connect if:
- You need to run Complex Reports (Standard reports on External Objects are limited).
- You need to run Triggers, Flows, or Workflow Rules on the data (Salesforce Connect doesn’t support them).
- The external system is slow (because Salesforce has to wait for it to respond before loading the page).
- Do not support Triggers, standard Dashboards (reporting is limited), and no rollup summary fields
- When syncing an external data source using Salesforce Connect:
- Will fail if it causes the org to exceed 200 external objects
- Does not allow for executing the sync operation in batches
- Not possible pause / resume the sync BUT it can be run asynchronously in the background
- If a naming conflict occurs between an external object name and an existing name in the org, the name can be edited inline to save time and clicks
- Indirect Lookup: Field used to link a child external object to a parent standard or custom object
- Custom unique, external ID field on the parent object can be selected to match against the child’s indirect lookup relationship field, with values determined by the specified External Column Name
- External Lookup: Field for a relationship linking a child standard, custom, or external object to a parent external object
- External lookup relationships are created automatically between parent and child objects mapped to the same external system schema
- Standard Lookup Relationships in Salesforce Connect: Can only be used if the external object includes a column that IDs related SFDC records by their 18-character IDs
- NO SUCH THING AS AN Internal Lookup FIELD
Lightning Usage App for Lightning Perf Testing
- Lightning Usage app can be accessed from the App Launcher
- Displays perf of Lightning pages in terms of page load times or Experienced Page Time (EPT)
- EPT: measure of how long it takes for a page to load so that a user can meaningfully interact with it
- “Page” section displays the performance of top-viewed pages as well as single page performance
- Page load times of a selected page are displayed for the previous 30 days
- “Performance” pane displays page load times in both the ‘Browser’ and the ‘Page’ sections of the app
Advanced SOQL Usage
- SOQL
WITH SECURITY_ENFORCEDwill throw an exception if the field & object-level security permissions are not met. - SOQL
HAVINGclause = allows for filtering based on an aggregate result
SELECT MailingCountry, COUNT(Id)
FROM Contact
GROUP BY MailingCountry
HAVING COUNT(Id) >= 100
- SOQL
TYPEOFkeyword - allowing for polymorphic relationship queries
SELECT Id, Status, Subject, ActivityDate, OwnerId, Owner.Name,
TYPEOF What
WHEN Account THEN Id, Name, AnnualRevenue
WHEN Custom_Object__c THEN Id, Custom_Field_1__c, Custom_Field_2__c
ELSE Id, Name
END
FROM Task
WHERE Owner.Name = 'John Turner'
LIMIT 50
- SOQL
ROLLUPCommand - Used to calculate subtotals and grand totals in a single query- Example code for seeing the sum of the Amount of all closed deals, then summary rows underneath by Sales person
SELECT Owner.Name, FORMAT(SUM(Amount))
FROM Opportunity
WHERE IsWon = true
GROUP BY ROLLUP (Owner.Name)
ORDER BY SUM(Amount) DESC
Aura Components
- Async server-side call uses
$A.enqueueAction()- Specified with a callback function
- Aura best practices for performance
- Use
<aura:if>to avoid loading components until a specific condition is met - Records loaded in Lightning Data Service are cached and shared across components
- Use components starting with
<lightning:>- Components starting with this are known as Base Lighting Components which provide better perf b/c they are loaded on the client side and do not require additional download or processing
- Use
$Browser: Global value provider returns device info of the current user$Browser.formFactorproperty returns the type of the device$Browser.isPhone&$Browser.isTabletproperties return boolean values depending on whether the user is using a tablet or phone- Important Note:
$Browser.isTabletonly works on Android tablets, not on iOS / iPads
- Loading static resources:
<ltng:require
scripts="{!join(',', $Resource.d3, $Resource.uikitJs)}"
styles="{!$Resource.uikitCss}"
afterScriptsLoaded="{!c.afterScriptsLoaded}"
/>
- Testing Aura Components
- No such thing as
getAttribute()orinvokeAction()methods - Code example:
- No such thing as
$T.createComponent("c:TrafficAnalytics", null)
.then(function (cmp) {
cmp.displaySettings(); // Example of calling a method directly on the component
expect(cmp.get("v.isDisplaySettings")).toBe(true); // Retrieve value of <aura:attribute>
})
.catch(function (e) {
done.fail(e);
});
Emitting Events
- No such thing as “Bubble Events” or “Capture Events”
Application Event- Use this when you need an entire “application” to be able to listen for an event- E.g., Sibling to sibling communication
Component Event- Use this when you need parent / child component communication (either top-down or bottom-up)Defaultphase: application event is handled in a non-deterministic manner from the root component through its subtree- Useful for handling events that affect components in different subtrees
- I.e., Broadcasts the event to all listening components and invokes event handlers in a non-deterministic order
Bubblephase: bubbles up from a source component to the root component and can be handled by a component in the containment hierarchyCapturePhase: application event behaves top-down manner, trickling down from root component to source component
Aura Bundle Files
- Design File: specifies which attributes are exposed to the Lightning App Builder
- Component File: contains the markup for the component
- Controller File: has JS functions which handle events
- The Controller should be very short. Its only job is to hear the event and tell the Helper what to do.
- Use it for the entry point of an event
- Helper File: contains common JS functions that can be called by other JS functions in the bundle
- The
helper.jsfile is meant for reusable business logic and complex data processing. Unlike the Controller, where each function is isolated, functions within the Helper can call one another easily.- Use this for the calling Apex, sharing code between functions, data manipulation
- Performance: Aura creates a single instance of the Helper for all instances of a component. If you have a list of 100 “Row” components, there is only 1 Helper instance in memory, but 100 Controller instances. Moving logic to the Helper reduces the memory footprint of your application.
- The
Apex Sharing
- Apex sharing properties:
AccessLevel-Read,Edit,AllParentID- Identifies the actual record that you’re sharing- Note: For standard objects, this is named differently. E.g.,
AccountSharefor the Account object, orCaseSharefor the Case object
- Note: For standard objects, this is named differently. E.g.,
RowCause- The reason for the sharing, has to be accessed a specific way- Can default to something like
Schema.OBJECT__Share.RowCause.Manual
- Can default to something like
UserOrGroupId
- Advantages of Apex Sharing
- Record can be shared multiple times with a user or group using different Apex sharing reasons
- Sharing records created by Apex managed sharing are maintained across record owner changes
- Sharing records created by User Managed Sharing are deleted when the record owner changes
- Only users with the
Modify All Datapermission can add, edit, or delete sharing records created by Apex managed sharing
Visualforce Concepts
- Visualforce tags are NOT responsive and are not usually suitable for multi-device use
- It’s usually considered less performant and more computationally expensive
Test.setCurrentPage()- Used in a testing context to set the right page when testing a VF controller- A custom controller CAN be referenced in multiple Visualforce pages
- You don’t need to use a “controller extension” for this
- When do you use a controller extension?
- When you want to extend a given controller with additional logic, and/or overwrite specific pieces of logic in another standard or custom controller
- Async Visualforce options
@futureannotation (seems to be referenced a lot in the example test still)- Continuation class -
Continuationis an actual class to use in Apex- Continuation: The user clicks a button and waits (sees a “Loading…” spinner). The page refreshes with the data once the callout finishes.
- The most important takeaway is this: Continuation is the only asynchronous tool designed for the User Interface.
@future/ Queueable / Batch: These are “Fire and Forget.” The user clicks a button, the process runs in the background, but the user doesn’t wait for the result on their screen.
transientkeyword in Apex Controller- When you mark a variable as transient in an Apex controller, you are telling Salesforce: “Keep this variable in memory while the page is loading, but do not save it in the encrypted hidden form field (the View State) when the page is sent to the user’s browser.”
- You should use transient for any data that is re-creatable or read-only.
- Large Query Results: If you are displaying a massive list of records but don’t need to edit them and send them back to the database, make them transient.
- Lookup/Reference Data: Lists used to populate picklists or decorative data that doesn’t change based on user input.
- Large Formats: Strings that store large chunks of JSON, XML, or base64-encoded blobs that are only needed for a single processing step.
- PDII Scenario Tip: If a question asks how to fix a “View State Limit Exceeded” error on a page that displays a large, non-editable table of historical data, the answer is to mark that data list as
transient.
Async VF Using JS
- JavaScript Remoting: a feature that allows you to call Apex controller methods directly from a JavaScript function within a Visualforce page.
- Works asynchronously
- Helps to not reload the entire VF page like View State & Action Functions
- Benefits:
- No View State: Since it doesn’t pass the Visualforce component tree back and forth, you avoid the dreaded “View State Limit Exceeded” error.
- Asynchronous: It doesn’t refresh the page. It behaves like a modern AJAX call.
- High Performance: It is significantly faster than
<apex:actionFunction>because it has much lower overhead on the Salesforce application server.
- Options:
buffer(Default: true): Salesforce tries to group multiple remoting calls into a single request to save on round-trips. If you need a call to go out immediately, you set buffer: false.escape(Default: true): This determines if the response is HTML-escaped. You should almost always leave this as true to prevent Cross-Site Scripting (XSS) attacks.timeout(Default: 30,000ms): The maximum time to wait for a response. The max you can set this to is 120,000ms (2 minutes).
- Code example:
global with sharing class AccountRemoter {
@RemoteAction // Required for Remoting
global static String getAccountName(String accId) { // Must be global and static
Account acc = [SELECT Name FROM Account WHERE Id = :accId];
return acc.Name;
}
}
function fetchAccount() {
var accountId = "001...";
// Syntax: ControllerName.MethodName(param, callback, options)
AccountRemoter.getAccountName(
accountId,
function (result, event) {
if (event.status) {
console.log("Account Name: " + result);
} else {
console.log("Error: " + event.message);
}
},
{ escape: true }, // Security setting to prevent XSS
);
}
- VF Remote Objects - More declarative (still needs code) means to have CRUD operations in JS without needing a full Apex controller with
@RemoteActionrequired- Useful if all you need to do is perform basic CRUD operations on a specific object in VF, and don’t need to do any other types of logic
- Code example:
<apex:remoteObjects>
<apex:remoteObjectModel name="Account" jsId="RemoteAcc" fields="Name,Phone,Industry">
<apex:remoteObjectField name="BillingCity" jsAlias="City" />
</apex:remoteObjectModel>
</apex:remoteObjects>
// Create a new Account using the jsId defined above
var acc = new SObjectModel.RemoteAcc();
// Set some values
acc.set("Name", "New Tech Corp");
acc.set("City", "San Francisco");
// Save to the database
acc.create(function (error, result, event) {
if (error) {
alert(error.message);
} else {
console.log("New Account ID: " + result[0]);
}
});
AJAX-Like Refreshing of Content using VF Tags
<apex:actionPoller action="{}" reRender="" interval=" />- component that can be used to automatically refresh an element to reflect updated data based on a certain intervalreRender: ID of the element on the page to rerender<apex:actionSupport />- can be used for the same, but in response to a DOM event, user engagement- See below for more info on this tag
<apex:actionFunction />- used to invoke a controller method using JavaScript
<apex:actionSupport />tag example & details- Best for: Adding behavior to a specific component (e.g., “when this checkbox is checked, show that section”).
- Trigger: Triggered by a standard browser DOM event on its parent.
- Usage: Nested inside the component it supports.
- Performance Tip:
actionRegion- If your page is large, using
<apex:actionSupport>can sometimes be slow because Salesforce still tries to process the entire form’s View State. - To optimize this: wrap your
<apex:actionSupport>(and the fields it needs) in an<apex:actionRegion>.- This tells Salesforce: “Only process the fields inside this region; ignore the rest of the form for this specific request.” This significantly reduces server-side processing time.
- If your page is large, using
- Code Example:
<apex:page controller="MyController">
<apex:form>
<apex:selectList value="{!selectedPriority}" size="1">
<apex:selectOption itemValue="Low" itemLabel="Low" />
<apex:selectOption itemValue="High" itemLabel="High" />
<apex:actionSupport event="onchange" action="{!checkPriority}" reRender="msgPanel" status="loadingStatus" />
</apex:selectList>
<apex:outputPanel id="msgPanel">
<apex:outputText value="Warning: High Priority Selected!" rendered="{!isHighPriority}" />
</apex:outputPanel>
<apex:actionStatus id="loadingStatus" startText=" (processing...)" stopText="" />
</apex:form>
</apex:page>
<apex:actionFunction />tag example & details- Best for: Calling Apex from custom JavaScript.
- Trigger: Creates a named JavaScript function that you call manually from your own script (e.g.,
myJSFunction()). - Usage: Can be called from anywhere on the page (even multiple places).
<apex:page action="{!InitializePage}">- CallInitializePage()controller method when the page first loads
Custom Metadata vs. Custom Settings
- Custom settings support the use of Currency field types, Custom Metadata Types do not
CustomMetadata__mdt.getAll()- returns a map, not a list with the keys of the map being the Developer Names of the records- NO method called
getQualifiedInstance()
- NO method called
Apex Concepts Review
WITH SHARINGkeyword on an Apex class only enforces record-level sharing, not field-level security@futureannotations can be called in test methods to help with mixed DML, but it’s not advisable nor can it be relied on for the unit testSystem.runAsblocks should be used mostly
- In Apex, a SOQL
FORloop can be used to help reduce heap size limit issues- Uses internal calls to
queryandqueryMore - The platform will chunk large results into batches of 200 records
- Uses internal calls to
- Dynamic SOSL -
Search.query(soslString)- Used when you don’t know the fields that you’ll be querying, but user-editable filters should be applied
Process.Plugin- legacy Apex interface for exposing functionality to Flow- If a question asks for the most modern way to call Apex from Flow, the answer is almost always
@InvocableMethod. - If the question specifically mentions describing parameters dynamically or refers to the
describe()method, they are likely pointing you towardProcess.Plugin. - Code example:
- If a question asks for the most modern way to call Apex from Flow, the answer is almost always
global class DiscountCalculatorPlugin implements Process.Plugin {
// 1. The Invoke method: This is where the logic happens
global Process.PluginResult invoke(Process.PluginRequest request) {
// Pull the input parameter 'Amount' from the Flow
Decimal amount = (Decimal)request.inputParameters.get('Amount');
// Simple Logic: 10% discount
Decimal discountedAmount = amount * 0.90;
// Map the result back to the 'FinalPrice' output parameter
Map<String, Object> result = new Map<String, Object>();
result.put('FinalPrice', discountedAmount);
return new Process.PluginResult(result);
}
// 2. The Describe method: This tells Flow Builder what to show the Admin
global Process.PluginDescribe describe() {
Process.PluginDescribe describe = new Process.PluginDescribe();
describe.Tag = 'Commerce Tools';
describe.Name = 'Apply Standard Discount';
describe.Description = 'Calculates a 10% discount for the provided amount.';
// Define Input Parameters
describe.inputParameters = new List<Process.PluginDescribe.InputParameter>{
new Process.PluginDescribe.InputParameter('Amount',
Process.PluginDescribe.DataCategory.CURRENCY, true)
};
// Define Output Parameters
describe.outputParameters = new List<Process.PluginDescribe.OutputParameter>{
new Process.PluginDescribe.OutputParameter('FinalPrice',
Process.PluginDescribe.DataCategory.CURRENCY)
};
return describe;
}
}
@futuremethod considerations- No more than 50 future method calls are allowed in a single transaction, so pass bulk parameters to it
- By default
callout=false
- How to skip collecting code coverage for Apex tests
- “New Run” menu in the developer console, then the Settings option
- “Application Test Execution” page in Setup
- Apex REST
- All methods are
global static - Remember the
@RestResource(urlMapping=...) HttpGet,HttpPost,HttpPut, etc…
- All methods are
- Starting a Flow from Apex:
Flow.Interview.start() - Performance testing & debugging in the Developer Console
- When analyzing the timeline in the Execution Overview panel, multiple short bursts of Apex events can easily add up to a significant amount of time; it generally indicates inefficient Apex loop logic
- Performance tree provides a top-down view of events and their duration
- Should be analyzed events starting from the one that takes the MOST amount of time
- Performance tree can be used in conjunction with the Execution Log to filter & view specific events
- Log lines in Execution Log can be analyzed for further details about events
- Executed Units - tab in the Execution Overview provides a quick preview of the number of rows returned by a SOQL query
- Can also provide information about the number of methods, queries, workflows, callouts, DML statements, validations, triggers, and VF pages
Other Misc. Notes from Practice Exam
- LWC emitting a custom event:
const event = new CustomEvent('nameofevent', { detail: data })`
- Showing errors in various ways to users
- LWC:
lighting-platform-show-toast-event - Aura:
lightning:notificationsLibrary - VF:
apex:PageMessages
- LWC:
- LWC Jest test - how to clean up after each test:
afterEach(() => {
while (document.body.firstChild) {
document.body.removeChild(document.body.firstChild);
}
});
AuraHandledExceptioncan contain a custom error message and be gracefully handled via a Lightning page- LWC or Aura components still need to check the status / catch the error coming back and then display it how they’d like
- No such thing as the “standard
<error>tag” oronErrorattribute on the<aura:component>tag
- LWC
supportedFormFactortag =Smallfor mobile devices
<targetConfigs>
<targetConfig targets="lightning__RecordPage">
<property name="prop1" type"String" />
<supportedFormFactors>
<supportedFormFactor type="Small" /> <!-- Can also be "Large" -->
</supportedFormFactors>
</targetConfig>
</targetConfigs>
- AppExchange Partner Apps:
- Checkout Management App: Provides dashboard that visually displays AppExchange checkout data like subscriptions & revenue of paid apps published by an AppExchange partner
- License Management App: used to manage leads & licenses of published apps
- Channel Order App: Used by partners for creating, submitting, and tracking orders of customers like product purchases, adding users, or renewal of contracts