Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PDF_TDX_2023_Apex__What_s_New_and_What_s_Coming.pdf

 PDF_TDX_2023_Apex__What_s_New_and_What_s_Coming.pdf

In this session, we take a deep dive into recently released and upcoming features on the Apex roadmap.

Recording available at:
https://www.salesforce.com/plus/experience/trailblazerdx_2023/series/Developers_for_TrailblazerDX_2023/episode/episode-s1e1

Daniel Ballinger

March 09, 2023
Tweet

More Decks by Daniel Ballinger

Other Decks in Programming

Transcript

  1. Apex
    What’s New and What’s
    Coming
    Daniel Ballinger, PM Apex
    Chris Peterson, PM

    View full-size slide

  2. Apex: What’s New and What’s Coming
    Chris Peterson
    Sr Director, Product
    [email protected]
    @ca_peterson
    Daniel Ballinger
    Director Product, Apex
    [email protected]
    @fishofprey

    View full-size slide

  3. Forward Looking Statements
    Updated: September 28, 2022
    This presentation contains forward-looking statements about, among other things, trend analyses and future events, future financial performance, anticipated growth, industry prospects,
    environmental, social and governance goals, and the anticipated benefits of acquired companies. The achievement or success of the matters covered by such forward-looking statements involves
    risks, uncertainties and assumptions. If any such risks or uncertainties materialize or if any of the assumptions prove incorrect, Salesforce’s results could differ materially from the results expressed or
    implied by these forward-looking statements. The risks and uncertainties referred to above include those factors discussed in Salesforce’s reports filed from time to time with the Securities and
    Exchange Commission, including, but not limited to: impact of, and actions we may take in response to, the COVID-19 pandemic, related public health measures and resulting economic downturn
    and market volatility; our ability to maintain security levels and service performance meeting the expectations of our customers, and the resources and costs required to avoid unanticipated
    downtime and prevent, detect and remediate performance degradation and security breaches; the expenses associated with our data centers and third-party infrastructure providers; our ability to
    secure additional data center capacity; our reliance on third-party hardware, software and platform providers; the effect of evolving domestic and foreign government regulations, including those
    related to the provision of services on the Internet, those related to accessing the Internet, and those addressing data privacy, cross-border data transfers and import and export controls; current and
    potential litigation involving us or our industry, including litigation involving acquired entities such as Tableau Software, Inc. and Slack Technologies, Inc., and the resolution or settlement thereof;
    regulatory developments and regulatory investigations involving us or affecting our industry; our ability to successfully introduce new services and product features, including any efforts to expand
    our services; the success of our strategy of acquiring or making investments in complementary businesses, joint ventures, services, technologies and intellectual property rights; our ability to
    complete, on a timely basis or at all, announced transactions; our ability to realize the benefits from acquisitions, strategic partnerships, joint ventures and investments, including our July 2021
    acquisition of Slack Technologies, Inc., and successfully integrate acquired businesses and technologies; our ability to compete in the markets in which we participate; the success of our business
    strategy and our plan to build our business, including our strategy to be a leading provider of enterprise cloud computing applications and platforms; our ability to execute our business plans; our
    ability to continue to grow unearned revenue and remaining performance obligation; the pace of change and innovation in enterprise cloud computing services; the seasonal nature of our sales
    cycles; our ability to limit customer attrition and costs related to those efforts; the success of our international expansion strategy; the demands on our personnel and infrastructure resulting from
    significant growth in our customer base and operations, including as a result of acquisitions; our ability to preserve our workplace culture, including as a result of our decisions regarding our current
    and future office environments or work-from-home policies; our dependency on the development and maintenance of the infrastructure of the Internet; our real estate and office facilities strategy
    and related costs and uncertainties; fluctuations in, and our ability to predict, our operating results and cash flows; the variability in our results arising from the accounting for term license revenue
    products; the performance and fair value of our investments in complementary businesses through our strategic investment portfolio; the impact of future gains or losses from our strategic
    investment portfolio, including gains or losses from overall market conditions that may affect the publicly traded companies within our strategic investment portfolio; our ability to protect our
    intellectual property rights; our ability to develop our brands; the impact of foreign currency exchange rate and interest rate fluctuations on our results; the valuation of our deferred tax assets and
    the release of related valuation allowances; the potential availability of additional tax assets in the future; the impact of new accounting pronouncements and tax laws; uncertainties affecting our
    ability to estimate our tax rate; uncertainties regarding our tax obligations in connection with potential jurisdictional transfers of intellectual property, including the tax rate, the timing of the transfer
    and the value of such transferred intellectual property; uncertainties regarding the effect of general economic and market conditions; the impact of geopolitical events; uncertainties regarding the
    impact of expensing stock options and other equity awards; the sufficiency of our capital resources; the ability to execute our Share Repurchase Program; our ability to comply with our debt
    covenants and lease obligations; the impact of climate change, natural disasters and actual or threatened public health emergencies; and our ability to achieve our aspirations, goals and projections
    related to our environmental, social and governance initiatives.

    View full-size slide

  4. Apex Roadmap
    Coming Soon Longer Term
    Spring ’23
    ● [GA] User-Mode DB Ops
    ● [Open Beta] DataWeave in Apex
    ● [GA] Flexible Queueable Delays
    ● [GA] ApexTypeImplementor
    ● Dynamic Label Resolution
    ● Flexible Queueable Depth
    ● Queueable Deduplication
    ● DataWeave in Apex
    ● Set implements Iterable
    ● User-Mode + PermSet Escalation
    ● Better Testing for PostInstall
    ● Further Queueable Enhancements
    ● List Sorting via Comparator
    ● Generics!

    View full-size slide

  5. Security
    User-Mode and Beyond

    View full-size slide

  6. Manual
    FLS/CRUD
    Checks
    ~2006
    ESAPI Library
    ~2010
    WITH
    SECURITY_
    ENFORCED
    ~2019
    Evolution of Apex Security Best Practices
    User-Mode
    DB Ops
    ~2023
    Security.
    stripInaccessible
    ~2019
    And lessons we learned along the way

    View full-size slide

  7. “System mode can do
    whatever the highest privilege
    user in that org can do, full
    stop”
    - Andrew Waite, the original Apex PM
    Circa 2006

    View full-size slide

  8. Security.stripInaccessible
    if (Schema.SObjectType.Account.isAccessible() &&
    Schema.SObjectType.Account.fields.Name.isAccessible() &&
    Schema.SObjectType.Account.fields.OwnerId.isAccessible() &&
    Schema.SObjectType.Account.fields.OwnerId.getReferenceTo()[0].getDescribe().isAccessible()
    /* even more checks for contact access */) {
    return [SELECT Name, Owner.Name, (SELECT Name FROM Contacts) FROM Account];
    } else {
    throw new AuraHandledException('Access Denied');
    }
    return Security.stripInaccessible(
    AccessType.READABLE,
    [SELECT Name, Owner.Name, (SELECT Name FROM Contacts) FROM Account]
    ).getRecords();
    Simplifies to

    View full-size slide

  9. WITH SECURITY_ENFORCED
    Add it to SOQL and we enforce security for you!
    ● Dead simple!
    ● Automated FLS/CRUD on queries!
    ● Everybody will love it!
    [SELECT Name, Account.Owner FROM
    Account WITH SECURITY_ENFORCED]
    and
    ● totally a mistake in hindsight

    View full-size slide

  10. WITH SECURITY_ENFORCED
    A mistake, in hindsight!
    ● Can only handle CRUD/FLS,
    ○ not anything more complex
    ○ no restriction rules compatibility
    ● Doesn't apply to the WHERE clause
    ● Edge cases with polymorphic lookups
    ● Doesn't gracefully degrade
    ● Will likely be versioned out someday

    View full-size slide

  11. “We really have to stop trying to
    re-implement platform security
    on top of system mode”
    - Chris Peterson, the fifth Apex PM
    Circa 2017

    View full-size slide

  12. User-Mode DB Ops
    Now AppExchange Security Review Approved!
    insert as user new Account(Name = 'GenWatt Throwback');
    Account a = [SELECT Id, Name FROM Account
    WHERE Support__c = 'Premier' WITH USER_MODE];
    a.Rating = 'Hot';
    update as system a;
    Spring `23
    GA
    Deeper Dive from TDX22:
    bit.ly/tdx22-user-mode

    View full-size slide

  13. User-Mode DB Ops
    Supported dynamically via method overloads
    Database.insert(
    new Account(Name = 'GenWatt Throwback'),
    AccessLevel.USER_MODE
    );
    Account a = Database.query('SELECT Id, Name FROM Account
    WHERE Rating = \'Hot\'',
    AccessLevel.USER_MODE
    );

    View full-size slide

  14. Why this is Different
    ● Historical focus on easier CRUD/FLS checks
    ● Describes don’t scale well
    ● Instead of re-implementing CRUD/FLS on top of system mode
    We actually, for real, drop out of system mode for the operation
    ○ Then we re-enter system mode.
    This means that as Salesforce launches new features, like Restriction Rules,
    that operate on unique security models we can support them fully.

    View full-size slide

  15. User-Mode: The Next Generation
    GA is only the start
    ● User-Mode with developer specified PermissionSet for
    privilege escalation
    ● User does NOT need to have the PermissionSet assigned
    ● Apply bounded, admin-visible escalations instead of using
    full system mode
    ● Can use different PermissionSets for different DB ops in the
    same request
    ● Mix and match with regular user-mode and system mode as
    needed

    View full-size slide

  16. Planned: User-Mode + PermSet
    Major enhancements to flexibility of user-mode.
    AccessLevel appEscalated =
    AccessLevel.USER_MODE.withPermissionSetId('0PSxx00000006xQ');
    Database.insert(
    new Account(Name = 'GenWatt Throwback'),
    appEscalated
    );
    Account a = Database.query('SELECT Id, Name FROM Account
    WHERE Rating = \'Hot\'',
    appEscalated
    );
    Currently in
    Dev
    Method names and API contract are in a draft state and likely to change

    View full-size slide

  17. An Interlude for Quality
    of Life
    IdeaExchange

    View full-size slide

  18. QoL: Dynamic Bind Variables in SOQL
    // Needs to be in scope
    string acctName = someAccount.Name;
    List accts =
    Database.query(
    'SELECT Id FROM Account WHERE Name = :acctName',
    AccessLevel.USER_MODE);
    Map acctBinds =
    new Map{'acctName' => someAccount.Name};
    List accts =
    Database.queryWithBinds(
    'SELECT Id FROM Account WHERE Name = :acctName',
    acctBinds,
    AccessLevel.USER_MODE);
    Spring `23
    GA

    View full-size slide

  19. QoL: Assert Methods
    try {
    methodUnderTest();
    System.assert(false, 'Exception failure is always an option');
    } catch (Exception ex) {
    system.assertEquals(true, ex instanceof DmlException);
    // Additional assertions
    }
    Winter `23
    GA
    try {
    methodUnderTest();
    Assert.fail('Exception failure is always an option');
    } catch (Exception ex) {
    Assert.isInstanceOfType(ex, DmlException.class);
    // Additional assertions
    }

    View full-size slide

  20. Previously
    System.assert(condition);
    System.assertEquals(expected, actual);
    System.assertNotEquals(expected, actual);
    Now
    Assert.isTrue(condition);
    Assert.isFalse(condition);
    Assert.isNull();
    Assert.isNotNull();
    Assert.areEqual(expected, actual);
    Assert.areNotEqual(expected, actual);
    Assert.isInstanceOfType();
    Assert.isNotInstanceOfType();
    Assert.fail();
    QoL: Assert Methods
    Winter `23
    GA
    Winter `23
    GA

    View full-size slide

  21. QoL: Label Translation
    // Visualforce page with explicit apex:page language attribute
    PageReference frLabelPageRef = new Page.LabelValueFr;
    string translatedLabelValue = frLabelPageRef.getContent();
    Summer `23
    GA
    string namespace = null;
    string labelName = 'surprise';
    string language = 'fr';
    string frLabel = System.Label.get(namespace, labelName, language);
    Assert.areEqual('Ah, la vache!', frLabel);

    View full-size slide

  22. QoL: List sorting via Comparator
    public class OpportunityWrapper implements Comparable {
    public Opportunity oppy;
    public Integer compareTo(Object compareTo) {
    // implementation
    }
    }
    List oppList = new List();
    // Populate list with wrapper instances
    oppList.sort();
    Coming
    Soon
    public class OpportunityNameCompare implements Comparator {
    public Integer compare(Opportunity o1, Opportunity o2) {
    // implementation
    }
    }
    List existingOppList; // Existing populated list
    existingOppList.sort(new OpportunityNameCompare());

    View full-size slide

  23. QoL: Interface Implementer Discovery
    global interface RoundingStratergy {
    decimal round(decimal toRound);
    }
    public abstract class RoundingStrategies {
    public class Ceiling implements RoundingStratergy {
    public decimal round(decimal toRound) {
    return toRound.round(System.RoundingMode.CEILING);
    }
    }
    public class HalfDown implements RoundingStratergy {
    public decimal round(decimal toRound) {
    return toRound.round(System.RoundingMode.HALF_DOWN);
    }
    }
    public class TwoDecimalPlaces implements RoundingStratergy {
    public decimal round(decimal toRound) {
    return toRound.setScale(2, System.RoundingMode.HALF_UP);
    }
    }
    }
    Spring `23
    GA
    Spring `23
    GA

    View full-size slide

  24. QoL: Interface Implementer Discovery
    List interfaceImpls = [
    SELECT ClassName, ClassNamespacePrefix
    FROM ApexTypeImplementor
    WHERE InterfaceName = 'RoundingStratergy'
    and IsConcrete=true];
    // Simulated selection of 2 decimal places
    ApexTypeImplementor selectedRoundingStratergy =
    interfaceImpls[2];
    // Create an instance of the class that implements the
    interface
    RoundingStratergy rs = (RoundingStratergy)
    Type.forName(selectedRoundingStratergy.ClassNamespacePrefix,
    selectedRoundingStratergy.ClassName).newInstance();
    Decimal rounded = rs.round(7.1459);
    Assert.areEqual(7.15, rounded);
    Spring `23
    GA
    Spring `23
    GA

    View full-size slide

  25. Where Are We Going
    with Async
    Queueables

    View full-size slide

  26. Controlling the job rate
    Integer minimumDelayInMinutes = 5;
    ID jobID = System.enqueueJob(
    new MyDelayedQueueableJob(),
    minimumDelayInMinutes);
    // Does *not* contribute to the scheduled job limit
    // Run asap
    minimumDelayInMinutes = 0;
    ID importantJobID = System.enqueueJob(
    new TimeSensitiveJob(),
    minimumDelayInMinutes);
    Footer
    Spring `23
    GA
    + Apex Settings:
    Default minimum enqueue delay

    View full-size slide

  27. Runaway chained job protection
    AsyncOptions queueableOptions = new AsyncOptions();
    queueableOptions.maximumQueueableStackDepth = 20;
    queueableOptions.minimumDelayInMinutes = 5;
    ID jobID = System.enqueueJob(new RecursiveJob(), queueableOptions);
    public class RecursiveJob implements Queueable {
    public void execute(QueueableContext context) {
    // Do some work
    AsyncInfo queueableInfo = new AsyncInfo(context.getJobId());
    integer stackDepth = queueableInfo.getCurrentQueueableStackDepth();
    integer maxStackDepth = queueableInfo.getMaximumQueueableStackDepth();
    if(stackDepth < maxStackDepth) {
    ID jobID = System.enqueueJob(new RecursiveJob());
    } else {
    // Handle max depth
    }
    }
    }
    Method names and API contract are in a draft state and likely to change
    Coming
    Soon
    Coming
    Soon

    View full-size slide

  28. Deduplication control
    Method names and API contract are in a draft state and likely to change
    Event Source Async Backend
    System.enqueueJob(
    new RecursiveJob(),
    dedupKey);
    Key Match
    Future

    View full-size slide

  29. public class QueryChunkingQueuable implements Queueable {
    private Database.Querylocator locator;
    public QueryChunkingQueuable() {
    locator =
    Database.getQueryLocator('SELECT Id FROM Contact WHERE LastActivityDate = LAST_N_DAYS:400');
    }
    public void execute(QueueableContext ctx) {
    List scope = locator.queryMore(200);
    //do something, like archive or delete them
    if(locator.hasMore()) {
    //process the next chunk
    System.enqueueJob(this);
    }
    }
    }
    Extended QueryLocators
    Method names and API contract are in a draft state and likely to change
    fully Serializable as Apex job state
    new method to grab next chunk of records
    new method to check for more records waiting
    Coming
    Soon
    Coming
    Soon

    View full-size slide

  30. What Are We Doing for
    Data Processing
    DataWeave in Apex

    View full-size slide

  31. DataWeave in Apex
    classes\csvToContacts.cls
    String inputCsv = 'first_name,last_name,email\n'
    + 'Codey,"The Bear",[email protected]';
    DataWeave.Script dwscript =
    new DataWeaveScriptResource.CsvToContacts();
    DataWeave.Result result =
    dwscript.execute(new Map{
    'payload' => inputCsv}
    );
    List results =
    (List)dwresult.getValue();
    dw\csvToContacts.dwl
    %dw 2.0
    input records application/csv
    output application/apex
    ---
    records map(record) -> {
    FirstName: record.first_name
    ,
    LastName: record.last_name
    ,
    Email: record.email
    } as Object {class: "Contact"}
    Summer `23
    Beta
    Dynamic DataWeave Script
    Reference

    View full-size slide

  32. How do I get started with DataWeave in Apex?
    * Spring `23 Beta
    Q: What do I need to access this in
    Apex?
    A: It will be available everywhere Apex is
    currently*
    You don’t need to be a Mulesoft customer
    Q: How do I get started today?
    https://sfdc.co/DataWeaveInApex

    View full-size slide

  33. Generics in Apex

    View full-size slide

  34. Why add Generics to Apex?
    ● Bulkification Example
    ○ Repacking Maps by Keys
    ● The Problem?
    ○ Repeating pattern
    ○ Hard to abstract solution without
    excessive casting or Object
    ● How would Generics help?
    ○ Reusability
    ○ Type safety
    List accounts = [Select Fields(Standard) from Account order by
    BillingCountry];
    Map> result = new Map>();
    for(Account a : accounts) {
    string billingCountry = (string)a.get(Account.field.BillingCountry);
    List accountsForBillingCountry = null;
    if(result.containsKey(billingCountry)) {
    accountsForBillingCountry = result.get(billingCountry);
    } else {
    accountsForBillingCountry = new List();
    result.put(billingCountry, accountsForBillingCountry);
    }
    accountsForBillingCountry.add(a);
    }
    Coming
    Soon
    Future

    View full-size slide

  35. What might Generics in Apex look like?
    public with sharing class MapAggregator {
    public static Map> getSObjectsByField (List input, Schema.SObjectField keyField) {
    Map> outputMap = new Map>();
    for(V a : input) {
    K keyFieldValue = (K)a.get(keyField);
    List sObjectsForKey = null;
    if(outputMap.containsKey(keyFieldValue )) {
    sObjectsForKey = outputMap.get(keyFieldValue );
    } else {
    sObjectsForKey = new List();
    outputMap.put(keyFieldValue , sObjectsForKey );
    }
    sObjectsForKey .add(a);
    }
    return outputMap;
    }
    }
    Method names and API contract are in a draft state and likely to change
    List accounts = [SELECT
    Fields(Standard)
    FROM Account
    ORDER BY BillingCountry];
    Map> result =
    MapAggregator.getSObjectsByField
    (
    accounts,
    Account.field.BillingCountry);
    Usage
    Defining

    View full-size slide

  36. Call to Action
    Share your Apex Generics feedback:
    https://sfdc.co/ApexGenerics
    ● Use cases
    ○ Trigger Handler Framework
    ○ Test Data Factory Framework
    ○ Iterator Implementation
    ○ Non-Nullable class
    ○ Case Insensitive Map

    ● Include:
    ○ Existing Apex
    ○ Idealized Apex with Generics

    View full-size slide