diff --git a/docs/BatchSchedulable.md b/docs/BatchSchedulable.md index b68a0c9..e59929b 100644 --- a/docs/BatchSchedulable.md +++ b/docs/BatchSchedulable.md @@ -18,9 +18,9 @@ error. ### Constructors ```apex | Interface class BatchSchedulable implements Schedulable { - BatchSchedulable(Type batchClass) {} - BatchSchedulable(Type batchClass, Map params) {} - BatchSchedulable(Type batchClass, Map params, Integer batchSize) {} + BatchSchedulable(Type batchClass) {} + BatchSchedulable(Type batchClass, Map params) {} + BatchSchedulable(Type batchClass, Map params, Integer batchSize) {} } ``` @@ -32,7 +32,7 @@ class BatchSchedulable implements Schedulable { ### Usage ```apex | Usage | The job will run everyday at 12:00 and execute SObjectCleanerBatch batch. Scheduler.scheduleDaily('SObject Cleaner', 12, 00, - new BatchSchedulable(SObjectCleanerBatch.class) + new BatchSchedulable(SObjectCleanerBatch.class) ); ``` diff --git a/docs/Callout.md b/docs/Callout.md index f22bb36..55a333e 100644 --- a/docs/Callout.md +++ b/docs/Callout.md @@ -29,68 +29,68 @@ All calls to the API should have streamlined workflow - callouts should be autho To implement this configuration, we could derive a new class from Callout class and configure it as follows: ```apex public class AcmeApiCallout extends Callout { - private AcmeAPIAuthHandler authorizationHandler = new AcmeAPIAuthHandler(); - - protected override void setupHandlers() { - onBeforeCallout() - .add(match.once(), authorizationHandler); - - onAfterCallout() - .add(match.onUnauthorized(), authorizationHandler) - .add(match.onUnauthorized(), action.retry(1)) - .add(match.onTimeout(), action.retry(1)) - .slot('beforeValidation') - .add(match.onAnyErrorCode(), action.logCallout(LoggingLevel.ERROR)) - .add(match.onAnyErrorCode(), action.throwEx()) - .add(match.onSuccess(), action.logCallout(LoggingLevel.INFO)) - .add(match.onSuccess(), action.returnJSON(responseType)); - } + private AcmeAPIAuthHandler authorizationHandler = new AcmeAPIAuthHandler(); + + protected override void setupHandlers() { + onBeforeCallout() + .add(match.once(), authorizationHandler); + + onAfterCallout() + .add(match.onUnauthorized(), authorizationHandler) + .add(match.onUnauthorized(), action.retry(1)) + .add(match.onTimeout(), action.retry(1)) + .slot('beforeValidation') + .add(match.onAnyErrorCode(), action.logCallout(LoggingLevel.ERROR)) + .add(match.onAnyErrorCode(), action.throwEx()) + .add(match.onSuccess(), action.logCallout(LoggingLevel.INFO)) + .add(match.onSuccess(), action.returnJSON(responseType)); + } } ``` Let's break this down: - Before Callout: - 1. Runs custom authorization handler once. This is example class that would generate Oauth token for us if Named Credential can't be used. - It's just an example of custom handler, it's not necessary to write any in most cases. + 1. Runs custom authorization handler once. This is example class that would generate Oauth token for us if Named Credential can't be used. + It's just an example of custom handler, it's not necessary to write any in most cases. - After Callout: - 1. If response returned 401 Unauthorized, run authorization handler again - 1. Retry callout once again with new authorization token - 1. On timeout, retry once again - 1. Slot named "beforeValidation" - this does nothing, but can be used for injecting handlers later in this place - 1. If webservice responded with error codes (400-599), creates log record with ERROR severity - 1. If webservice responded error code, throw CalloutResponseException - 1. If webservice responded with success code, log callout with INFO severity - 1. If webservice responded with success code, deserialize response body to given apex type. + 1. If response returned 401 Unauthorized, run authorization handler again + 1. Retry callout once again with new authorization token + 1. On timeout, retry once again + 1. Slot named "beforeValidation" - this does nothing, but can be used for injecting handlers later in this place + 1. If webservice responded with error codes (400-599), creates log record with ERROR severity + 1. If webservice responded error code, throw CalloutResponseException + 1. If webservice responded with success code, log callout with INFO severity + 1. If webservice responded with success code, deserialize response body to given apex type. ## Usage in API class The callout class can then be used in methods which expose particular endpoints. ```apex public class AcmeCustomerAPI { - public List getCustomers(List accountIds) { - Callout c = new AcmeApiCallout(); - c.setMethod('GET'); - c.setEndpoint('callout:MyCredential/api/Customer'); - c.setParam('id', accountIds, true); - c.setResponseType(List.class); - c.onAfterCallout() - .addToSlot('beforeValidation', - c.match.onNotFound(), c.action.returns(new List()) - ); - - return (List) c.execute(); - } - - public Customer updateCustomer(Customer customer) { - Callout c = new AcmeApiCallout(); - c.setMethod('POST'); - c.setEndpoint('callout:MyCredential/api/Customer'); - c.setBodyJSON(account); - c.setResponseType(Account.class); - return (Account) c.execute(); - } + public List getCustomers(List accountIds) { + Callout c = new AcmeApiCallout(); + c.setMethod('GET'); + c.setEndpoint('callout:MyCredential/api/Customer'); + c.setParam('id', accountIds, true); + c.setResponseType(List.class); + c.onAfterCallout() + .addToSlot('beforeValidation', + c.match.onNotFound(), c.action.returns(new List()) + ); + + return (List) c.execute(); + } + + public Customer updateCustomer(Customer customer) { + Callout c = new AcmeApiCallout(); + c.setMethod('POST'); + c.setEndpoint('callout:MyCredential/api/Customer'); + c.setBodyJSON(account); + c.setResponseType(Account.class); + return (Account) c.execute(); + } } ``` In the above example, **slot** functionality was utilized to return an empty list when webservice responds with 404 Not Found. @@ -106,16 +106,16 @@ Consider the following setup: ```apex | API-specific Callout configuration public class AcmeApiCallout extends Callout { - private AcmeAPIAuthHandler authorizationHandler = new AcmeAPIAuthHandler(); - - protected override void setupHandlers() { - onAfterCallout() - .add('authorize', match.onUnauthorized(), authorizationHandler) - .add('authorizeRetry', match.onUnauthorized(), action.retry(1)) - .add('timeoutRetry', match.onTimeout(), action.retry(1)) - .add(match.onSuccess(), action.logCallout(LoggingLevel.INFO)) - .add(match.onSuccess(), action.returnJSON(responseType)); - } + private AcmeAPIAuthHandler authorizationHandler = new AcmeAPIAuthHandler(); + + protected override void setupHandlers() { + onAfterCallout() + .add('authorize', match.onUnauthorized(), authorizationHandler) + .add('authorizeRetry', match.onUnauthorized(), action.retry(1)) + .add('timeoutRetry', match.onTimeout(), action.retry(1)) + .add(match.onSuccess(), action.logCallout(LoggingLevel.INFO)) + .add(match.onSuccess(), action.returnJSON(responseType)); + } } ``` @@ -123,15 +123,15 @@ In one of the calls, we will remove an `authorizeRetry` step and replace retry w ```apex | Client Code public class AcmeCustomerAPI { - public List getCustomers(List accountIds) { - Callout c = new AcmeApiCallout(); - c.onAfterCallout() - .remove('authorize') - .replace('authorizeRetry', c.action.logCallout(LoggingLevel.ERROR)) - .replace('timeoutRetry', c.action.retry(5)); + public List getCustomers(List accountIds) { + Callout c = new AcmeApiCallout(); + c.onAfterCallout() + .remove('authorize') + .replace('authorizeRetry', c.action.logCallout(LoggingLevel.ERROR)) + .replace('timeoutRetry', c.action.retry(5)); - return (List) c.execute(); - } + return (List) c.execute(); + } } ``` @@ -140,15 +140,15 @@ List of handlers can be defined with a slot - placeholder in which we can later ```apex | API-specific Callout configuration public class AcmeApiCallout extends Callout { - private AcmeAPIAuthHandler authorizationHandler = new AcmeAPIAuthHandler(); - - protected override void setupHandlers() { - onAfterCallout() - .slot('beforeValidation') - .add(match.onAnyErrorCode(), action.logCallout(LoggingLevel.ERROR)) - .add(match.onAnyErrorCode(), action.throwEx()) - .add(match.onSuccess(), action.returnJSON(responseType)); - } + private AcmeAPIAuthHandler authorizationHandler = new AcmeAPIAuthHandler(); + + protected override void setupHandlers() { + onAfterCallout() + .slot('beforeValidation') + .add(match.onAnyErrorCode(), action.logCallout(LoggingLevel.ERROR)) + .add(match.onAnyErrorCode(), action.throwEx()) + .add(match.onSuccess(), action.returnJSON(responseType)); + } } ``` @@ -158,15 +158,15 @@ Add handler to the slot which does following: ```apex public class AcmeCustomerAPI { - public List getCustomers(List accountIds) { - Callout c = new AcmeApiCallout(); - c.onAfterCallout() - .addToSlot('beforeValidation', - c.match.onNotFound(), c.action.returns(new List()) - ); + public List getCustomers(List accountIds) { + Callout c = new AcmeApiCallout(); + c.onAfterCallout() + .addToSlot('beforeValidation', + c.match.onNotFound(), c.action.returns(new List()) + ); - return (List) c.execute(); - } + return (List) c.execute(); + } } ``` @@ -178,23 +178,23 @@ Callout Framework can be easily extended by implementing two interfaces for matc ```apex | Condition | Generic Condition interface is used to check if Callout satisfies the condition for associated action. public interface Condition { - Boolean isTrue(Object item); + Boolean isTrue(Object item); } ``` ```apex | Callout.Handler | Represents action to perform. public interface Handler { - Object handle(Callout c); + Object handle(Callout c); } ``` The Framework works as follows - when callout is executed(): 1. Iterate through pairs of Condition-Handler 1. If the Condition returns true: - 1. Execute Handler and check return value: - 1. If `null` - continue iteration over actions. - 1. If not null - return this immediately as response from callout `execute` method. - 1. If throws exception, breaks the code execution - this exception has to be handled in client code. + 1. Execute Handler and check return value: + 1. If `null` - continue iteration over actions. + 1. If not null - return this immediately as response from callout `execute` method. + 1. If throws exception, breaks the code execution - this exception has to be handled in client code. Callout has two lists of handlers - one executed before and one after the callout. @@ -205,36 +205,36 @@ Callout has two lists of handlers - one executed before and one after the callou * Matches Response body that contains substring */ private class SubstringMatcher implements Condition { - private String substring; + private String substring; - private SubstringMatcher(String substring) { - this.substring = substring; - } + private SubstringMatcher(String substring) { + this.substring = substring; + } - public Boolean isTrue(Object item) { - Callout c = (Callout) item; + public Boolean isTrue(Object item) { + Callout c = (Callout) item; - return c.getResponse()?.getBody()?.containsIgnoreCase(substring) == true; - } + return c.getResponse()?.getBody()?.containsIgnoreCase(substring) == true; + } } ``` ```apex | Example of Handler class private class RetryHandler implements Callout.Handler { - private Integer attempt = 0, maxAttempts; + private Integer attempt = 0, maxAttempts; - public RetryHandler(Integer howManyTimes) { - maxAttempts = howManyTimes; - } + public RetryHandler(Integer howManyTimes) { + maxAttempts = howManyTimes; + } - public Object handle(Callout c) { - if (attempt < maxAttempts) { - attempt++; - return c.execute(); - } + public Object handle(Callout c) { + if (attempt < maxAttempts) { + attempt++; + return c.execute(); + } - return null; - } + return null; + } } ``` @@ -243,7 +243,7 @@ private class RetryHandler implements Callout.Handler { ## Callout
- Methods + Methods | Method | Description | |-----------------------------------------------------------------------|--------------------------------------------------------------------------------------| @@ -267,7 +267,7 @@ private class RetryHandler implements Callout.Handler { ## CalloutHandlersList
- Methods + Methods | Method | Description | |-----------------------------------------------------------------------------------------------|-----------------------------------------------------------------| @@ -284,14 +284,14 @@ private class RetryHandler implements Callout.Handler { ## Condition ```apex | Condition public interface Condition { - Boolean isTrue(Object item); + Boolean isTrue(Object item); } ``` ## Callout.Handler ```apex | Callout.Handler public interface Handler { - Object handle(Callout c); + Object handle(Callout c); } ``` @@ -300,33 +300,33 @@ public interface Handler { - It's not required to extend Callout class. It can be used as is or configured without inheritance: ```apex public Callout getAcmeCallout() { - Callout c = new Callout(); - c.onBeforeCallout() - .add(c.match.once(), authorizationHandler); - - c.onAfterCallout() - .add(c.match.onUnauthorized(), authorizationHandler) - .add(c.match.onUnauthorized(), c.action.retry(1)) - .add(c.match.onTimeout(), c.action.retry(1)) - .slot('beforeValidation') - .add(c.match.onAnyErrorCode(), c.action.logCallout(LoggingLevel.ERROR)) - .add(c.match.onAnyErrorCode(), c.action.throwEx()) - .add(c.match.onSuccess(), c.action.logCallout(LoggingLevel.INFO)) - .add(c.match.onSuccess(), c.action.returnJSON(responseType)); - - return c; + Callout c = new Callout(); + c.onBeforeCallout() + .add(c.match.once(), authorizationHandler); + + c.onAfterCallout() + .add(c.match.onUnauthorized(), authorizationHandler) + .add(c.match.onUnauthorized(), c.action.retry(1)) + .add(c.match.onTimeout(), c.action.retry(1)) + .slot('beforeValidation') + .add(c.match.onAnyErrorCode(), c.action.logCallout(LoggingLevel.ERROR)) + .add(c.match.onAnyErrorCode(), c.action.throwEx()) + .add(c.match.onSuccess(), c.action.logCallout(LoggingLevel.INFO)) + .add(c.match.onSuccess(), c.action.returnJSON(responseType)); + + return c; } ``` - Callout has some handlers implemented by default: ```apex | Default Handlers onAfterCallout() - .add(match.onUnauthorized(), action.retry(1)) - .add(match.onTimeout(), action.retry(1)) - .slot('beforeValidation') - .add(match.onAnyErrorCode(), action.logCallout(LoggingLevel.ERROR)) - .add(match.onAnyErrorCode(), action.throwEx()) - .add(match.onSuccess(), action.logCallout(LoggingLevel.INFO)) - .add(match.onSuccess(), action.returnJSON(responseType)); + .add(match.onUnauthorized(), action.retry(1)) + .add(match.onTimeout(), action.retry(1)) + .slot('beforeValidation') + .add(match.onAnyErrorCode(), action.logCallout(LoggingLevel.ERROR)) + .add(match.onAnyErrorCode(), action.throwEx()) + .add(match.onSuccess(), action.logCallout(LoggingLevel.INFO)) + .add(match.onSuccess(), action.returnJSON(responseType)); ``` - Client code can remove or replace particular handlers. Name can be added to the handler, which then can be used to remove/replace. \ No newline at end of file diff --git a/docs/Collection.md b/docs/Collection.md index ec2c19c..3a549bb 100644 --- a/docs/Collection.md +++ b/docs/Collection.md @@ -29,16 +29,16 @@ Additionally, it provides utility methods operating on lists. ```apex // Get first 10 won opportunities ordered by expected revenue List wonOpportunities = (List) - Collection.of(opportunities) - .filter(Opportunity.StageName).equals('Won') - .orderByDesc(Opportunity.ExpectedRevenue) - .slice(0, 10) - .get(); + Collection.of(opportunities) + .filter(Opportunity.StageName).equals('Won') + .orderByDesc(Opportunity.ExpectedRevenue) + .slice(0, 10) + .get(); // Map opportunities by Account Id Map opportunityByAccountId = (Map) - Collection.of(opportunities).mapBy(Opportunity.AccountId); + Collection.of(opportunities).mapBy(Opportunity.AccountId); ``` ## Mapping SObjects @@ -58,24 +58,24 @@ Mapping expects the key to be unique, if it's not - the last item in the list wi Map collection of sObjects by any field. Framework will check field's type and dynamically construct map with correct key and value type. ```apex Map opportunityByAccountId = (Map) - Collection.of(opportunities).mapBy(Opportunity.AccountId); + Collection.of(opportunities).mapBy(Opportunity.AccountId); Map opportunityByName = (Map) - Collection.of(opportunities).mapBy(Opportunity.Name); + Collection.of(opportunities).mapBy(Opportunity.Name); ``` #### Mapping between two fields Selected fields will be mapped as a key to value map. ```apex Map ownerByAccountId = (Map) - Collection.of(opportunities).mapBy(Opportunity.AccountId, Opportunity.OwnerId); + Collection.of(opportunities).mapBy(Opportunity.AccountId, Opportunity.OwnerId); ``` #### Mapping by concatenation Map's key will be a concatenation of two fields. There's no separator between fields. ```apex Map mapByParents = (Map) - Collection.of(junctions).mapByConcatenation(JunctionObject__c.Parent1__c, JunctionObject__c.Parent2__c); + Collection.of(junctions).mapByConcatenation(JunctionObject__c.Parent1__c, JunctionObject__c.Parent2__c); ``` #### Mapping by custom mapper class @@ -83,20 +83,20 @@ Sometimes organizations will have very specific data translations that are often In that case, it's possible to implement Mapper interface and reuse it in many places. ```apex Map accountsByDay = (Map) - Collection.of(accounts).mapBy(new MapperByCreatedDay()); + Collection.of(accounts).mapBy(new MapperByCreatedDay()); public class MapperByCreatedDay implements Collection.Mapper { - // Should return value derived from Collections item - public Object value(Object item) { - SObject so = (SObject) item; - Datetime createdDate = (Datetime) so.get('CreatedDate'); - return createdDate.day(); - } + // Should return value derived from Collections item + public Object value(Object item) { + SObject so = (SObject) item; + Datetime createdDate = (Datetime) so.get('CreatedDate'); + return createdDate.day(); + } - // Should return type of value - this will also become Map's key or value type, depending where mapper is used. - public Type valueType() { return Integer.class; } + // Should return type of value - this will also become Map's key or value type, depending where mapper is used. + public Type valueType() { return Integer.class; } } ``` @@ -111,25 +111,25 @@ Grouping provides the same methods as mapping. Group collection of sObjects by any field. ```apex Map opportunitiesByAccountId = (Map) - Collection.of(opportunities).groupBy(Opportunity.AccountId); + Collection.of(opportunities).groupBy(Opportunity.AccountId); Map opportunitiesByOwner = (Map) - Collection.of(opportunities).mapBy(Opportunity.OwnerId); + Collection.of(opportunities).mapBy(Opportunity.OwnerId); Map opportunitiesByStage = (Map) - Collection.of(opportunities).mapBy(Opportunity.StageName); + Collection.of(opportunities).mapBy(Opportunity.StageName); ``` #### Grouping between two fields Selected fields will be mapped as key to list of values map. ```apex Map accountIdsByOwner = (Map) - Collection.of(opportunities).groupBy(Opportunity.OwnerId, Opportunity.AccountId); + Collection.of(opportunities).groupBy(Opportunity.OwnerId, Opportunity.AccountId); ``` #### Group by concatenation of 2 fields Map's key will be the concatenation of 2 fields. There's no separator between fields ```apex Map mapByParents = (Map) - Collection.of(junctions).groupByConcatenation(JunctionObject__c.Parent1__c, JunctionObject__c.Parent2__c); + Collection.of(junctions).groupByConcatenation(JunctionObject__c.Parent1__c, JunctionObject__c.Parent2__c); ``` @@ -188,35 +188,35 @@ Collection filterAlike(SObject prototype); #### Filtering sObjects by value of the field. ```apex Opportunity opp = (Opportunity) Collection.of(opportunities) - .filter(Opportunity.Stage).equals('Won') - .getFirst(); + .filter(Opportunity.Stage).equals('Won') + .getFirst(); ``` #### Filtering with complex logic ```apex CollectionConditions c = new CollectionConditions(); List filtered = (List) Collection.of(opportunities) - .filter( - c.ORs( - c.ANDs( - c.field(Opportunity.NextStep).contains('Analysis'), - c.field(Opportunity.HasOpenActivity).equals(true), - c.field(Opportunity.LastActivityDate).lessEqualsThan(Date.today()), - c.field(Opportunity.LastActivityDate).greaterThan(Date.today().addDays(-2)) - ), - c.field(Opportunity.NextStep).notEquals('Analysis') - ) - ) - .get(); + .filter( + c.ORs( + c.ANDs( + c.field(Opportunity.NextStep).contains('Analysis'), + c.field(Opportunity.HasOpenActivity).equals(true), + c.field(Opportunity.LastActivityDate).lessEqualsThan(Date.today()), + c.field(Opportunity.LastActivityDate).greaterThan(Date.today().addDays(-2)) + ), + c.field(Opportunity.NextStep).notEquals('Analysis') + ) + ) + .get(); ``` #### Filtering similar records Will filter records that have the same value set as given example ```apex List filtered = (List) Collection.of(opportunities) - .filterAlike(new Opportunity( - StageName = 'Prospect', - AccountId = myAccount.Id - )) - .get(); + .filterAlike(new Opportunity( + StageName = 'Prospect', + AccountId = myAccount.Id + )) + .get(); ``` @@ -227,9 +227,9 @@ Order methods can sort list by sobject field or custom comparator. #### Order by SObject field. ```apex List sortedOpportunities = (List) - Collection.of(opportunities) - .orderAsc(Opportunity.CreatedDate) - .get(); + Collection.of(opportunities) + .orderAsc(Opportunity.CreatedDate) + .get(); ``` #### Order using custom comparator class **Note!** As of Winter '24, Salesforce introduces standard Comparator interface. It only took 20 years, but we are getting there. @@ -237,24 +237,24 @@ Framework will be refactored to use standard comparator closer to the Salesforce ```apex List opportunities = (List) - Collection.of(opportunities) - .orderBy(new ReverseProbabilityComparator()) - .get(); + Collection.of(opportunities) + .orderBy(new ReverseProbabilityComparator()) + .get(); private class ReverseProbabilityComparator implements Comparator { - public Integer compare(Object thisItem, Object otherItem) { - Opportunity thisOpp = (Opportunity) thisItem; - Opportunity otherOpp = (Opportunity) otherItem; - - if (thisOpp.Probability < otherOpp.Probability) { - return 1; - } else if (thisOpp.Probability > otherOpp.Probability) { - return -1; - - } else { - return 0; - } - } + public Integer compare(Object thisItem, Object otherItem) { + Opportunity thisOpp = (Opportunity) thisItem; + Opportunity otherOpp = (Opportunity) otherItem; + + if (thisOpp.Probability < otherOpp.Probability) { + return 1; + } else if (thisOpp.Probability > otherOpp.Probability) { + return -1; + + } else { + return 0; + } + } } ``` @@ -303,68 +303,68 @@ Collection.of(contacts).removeLast(); //=> removes last element ```java interface Collection { - public static Collection of (List items); - - List get(); - Object getFirst(); - Object getLast(); - Object get(Integer i); - Object getRandom(); - Collection add(Integer index, Object element); - Collection add(Object element); - Collection addAll(List elements); - Collection addAll(Set elements); - Collection remove(Integer index); - Collection removeLast(); - Collection clear(); - Collection slice(Integer start, Integer stop); - Collection slice(List indexes); - - Boolean isNotEmpty(); - Boolean isEmpty(); - Integer size(); - FieldFilter filter(SObjectField field); - Collection filter(Condition filter); - Collection filterAlike(SObject prototype); - - - Object reduce(Reducer reducer, Object initialValue); - - Decimal getSum(SObjectField field); - Decimal getAverage(SObjectField field); - Decimal getMin(SObjectField field); - Decimal getMax(SObjectField field); - - - List getListId(SObjectField field); - List getListString(SObjectField field); - List getListInteger(SObjectField field); - List getList(SObjectField field); - List getList(Mapper valueMapper); - - Set getSetId(SObjectField field); - Set getSetString(SObjectField field); - Set getSetInteger(SObjectField field); - Object getSet(SObjectField field); - Object getSet(Mapper valueMapper); - - Object mapBy(SObjectField field); - Object mapBy(SObjectField keyField, SObjectField valueField); - Object mapBy(Mapper keyMapper); - Object mapBy(Mapper keyMapper, Mapper valueMapper); - Object mapByConcatenation(SObjectField field1, SObjectField field2); - - - Object groupBy(SObjectField field); - Object groupBy(SObjectField keyField, SObjectField valueField); - Object groupBy(Mapper keyMapper); - Object groupBy(Mapper keyMapper, Mapper valueMapper); - Object groupByConcatenation(SObjectField field1, SObjectField field2); - - - Collection orderAsc(SObjectField field); - Collection orderDesc(SObjectField field); - Collection orderBy(Comparator comparator); + public static Collection of (List items); + + List get(); + Object getFirst(); + Object getLast(); + Object get(Integer i); + Object getRandom(); + Collection add(Integer index, Object element); + Collection add(Object element); + Collection addAll(List elements); + Collection addAll(Set elements); + Collection remove(Integer index); + Collection removeLast(); + Collection clear(); + Collection slice(Integer start, Integer stop); + Collection slice(List indexes); + + Boolean isNotEmpty(); + Boolean isEmpty(); + Integer size(); + FieldFilter filter(SObjectField field); + Collection filter(Condition filter); + Collection filterAlike(SObject prototype); + + + Object reduce(Reducer reducer, Object initialValue); + + Decimal getSum(SObjectField field); + Decimal getAverage(SObjectField field); + Decimal getMin(SObjectField field); + Decimal getMax(SObjectField field); + + + List getListId(SObjectField field); + List getListString(SObjectField field); + List getListInteger(SObjectField field); + List getList(SObjectField field); + List getList(Mapper valueMapper); + + Set getSetId(SObjectField field); + Set getSetString(SObjectField field); + Set getSetInteger(SObjectField field); + Object getSet(SObjectField field); + Object getSet(Mapper valueMapper); + + Object mapBy(SObjectField field); + Object mapBy(SObjectField keyField, SObjectField valueField); + Object mapBy(Mapper keyMapper); + Object mapBy(Mapper keyMapper, Mapper valueMapper); + Object mapByConcatenation(SObjectField field1, SObjectField field2); + + + Object groupBy(SObjectField field); + Object groupBy(SObjectField keyField, SObjectField valueField); + Object groupBy(Mapper keyMapper); + Object groupBy(Mapper keyMapper, Mapper valueMapper); + Object groupByConcatenation(SObjectField field1, SObjectField field2); + + + Collection orderAsc(SObjectField field); + Collection orderDesc(SObjectField field); + Collection orderBy(Comparator comparator); } ``` @@ -373,18 +373,18 @@ interface Collection { These methods are available when filtering sobject list by field: ```apex public interface FieldFilter { - Collection equals(Object value); - Collection notEquals(Object value); - Collection greaterThan(Object value); - Collection greaterEqualsThan(Object value); - Collection lessThan(Object value); - Collection lessEqualsThan(Object value); - Collection isIn(Set values); - Collection isIn(List values); - Collection isIn(List parents); - Collection isNotIn(Set values); - Collection isNotIn(List values); - Collection contains(String value); + Collection equals(Object value); + Collection notEquals(Object value); + Collection greaterThan(Object value); + Collection greaterEqualsThan(Object value); + Collection lessThan(Object value); + Collection lessEqualsThan(Object value); + Collection isIn(Set values); + Collection isIn(List values); + Collection isIn(List parents); + Collection isNotIn(Set values); + Collection isNotIn(List values); + Collection contains(String value); } ``` @@ -392,8 +392,8 @@ public interface FieldFilter { Mapper is a class that takes single collection item and converts it to value. This value can be used as maps/groups/set/list key or value. ```apex interface Mapper { - Type valueType(); - Object value(Object item); + Type valueType(); + Object value(Object item); } ``` @@ -401,6 +401,6 @@ interface Mapper { Reducer takes entire collection and converts it to another value. This could be for example sum, min/max of the items fields, but also any different custom reduction. ```apex interface Reducer { - Object reduce(Object accumulator, Object item, Integer index); + Object reduce(Object accumulator, Object item, Integer index); } ``` diff --git a/docs/Constants.md b/docs/Constants.md index 88a7562..3eca84b 100644 --- a/docs/Constants.md +++ b/docs/Constants.md @@ -14,16 +14,16 @@ Each constant is just a static variable in Constants class. ```apex public with sharing class Constants { - public final static String - ACCOUNT_TYPE_CUSTOMER = 'Customer', - ACCOUNT_TYPE_SMB = 'SMB', - ACCOUNT_TYPE_ENTERPRISE = 'Enterprise', - ACCOUNT_TYPE_PERSON = 'Person', - - ORDER_STATUS_NEW = 'New', - ORDER_STATUS_INPROGRESS = 'In Progress', - ORDER_STATUS_ON_HOLD = 'On Hold', - ORDER_STATUS_COMPLETED = 'Completed'; + public final static String + ACCOUNT_TYPE_CUSTOMER = 'Customer', + ACCOUNT_TYPE_SMB = 'SMB', + ACCOUNT_TYPE_ENTERPRISE = 'Enterprise', + ACCOUNT_TYPE_PERSON = 'Person', + + ORDER_STATUS_NEW = 'New', + ORDER_STATUS_INPROGRESS = 'In Progress', + ORDER_STATUS_ON_HOLD = 'On Hold', + ORDER_STATUS_COMPLETED = 'Completed'; } ``` @@ -44,21 +44,21 @@ Within Constants class, each sobject and each field should have a separate inner ```apex public with sharing class Constants { - public final AccountConstants ACCOUNT = new AccountConstants(); - public final OrderConstants ORDER = new OrderConstants(); - - public class AccountConstants { - public AccountType TYPE = new AccountType(); - } - - // Values of Account.Type picklist - public class AccountType { - public final String - CUSTOMER = 'Customer', - SMB = 'SMB', - ENTERPRISE = 'ENTERPRISE', - PERSON = 'Person'; - } + public final AccountConstants ACCOUNT = new AccountConstants(); + public final OrderConstants ORDER = new OrderConstants(); + + public class AccountConstants { + public AccountType TYPE = new AccountType(); + } + + // Values of Account.Type picklist + public class AccountType { + public final String + CUSTOMER = 'Customer', + SMB = 'SMB', + ENTERPRISE = 'ENTERPRISE', + PERSON = 'Person'; + } } ``` @@ -74,17 +74,17 @@ String orderStatus = Constants.ORDER.STATUS.NEW; ```apex //Old for (Order o : orders) { - if (o.Status == Constants.ORDER_STATUS_NEW || o.Status == Constants.ORDER_STATUS_IN_PROGRESS) { - //... do something - } + if (o.Status == Constants.ORDER_STATUS_NEW || o.Status == Constants.ORDER_STATUS_IN_PROGRESS) { + //... do something + } } //Shorthand String STATUS = Constants.ORDER.STATUS; for (Order o : orders) { - if (o.Status == STATUS.NEW || o.Status == STATUS.IN_PROGRESS) { - //... do something - } + if (o.Status == STATUS.NEW || o.Status == STATUS.IN_PROGRESS) { + //... do something + } } ``` @@ -99,11 +99,11 @@ If there are hundreds of constants, this may account for an unnecessary slowdown ```apex public class Constants { - public String ACCOUNT_LABEL { get {return Schema.Account.SObjectType.getDescribe().getLabel();} } + public String ACCOUNT_LABEL { get {return Schema.Account.SObjectType.getDescribe().getLabel();} } - public String ORDER_LABEL() { - return Schema.Order.SObjectType.getDescribe().getLabel(); - } + public String ORDER_LABEL() { + return Schema.Order.SObjectType.getDescribe().getLabel(); + } } ``` diff --git a/docs/CustomMetadataService.md b/docs/CustomMetadataService.md index b2293d8..da2a597 100644 --- a/docs/CustomMetadataService.md +++ b/docs/CustomMetadataService.md @@ -14,14 +14,14 @@ It's so small, that it can be used straight from the Anonymous Apex without depl ```apex | Usage CustomMetadataService.deploy(new List{ - new Country__mdt(DeveloperName = 'USA', Active__c = true), - new Country__mdt(DeveloperName = 'France', Active__c = true), - new Country__mdt(DeveloperName = 'Poland', Active__c = true) + new Country__mdt(DeveloperName = 'USA', Active__c = true), + new Country__mdt(DeveloperName = 'France', Active__c = true), + new Country__mdt(DeveloperName = 'Poland', Active__c = true) }); ```
- Deployment from Anonymous Apex + Deployment from Anonymous Apex To use it without deployment in anonymous apex, just copy-paste the [deploy method of the class](https://github.com/pkozuchowski/Apex-Opensource-Library/blob/master/force-app/commons/schema/CustomMetadataService.cls) at the @@ -29,37 +29,37 @@ end of the script. ```apex deploy(new List{ - new Country__mdt(DeveloperName = 'USA', Active__c = true), - new Country__mdt(DeveloperName = 'France', Active__c = true), - new Country__mdt(DeveloperName = 'Poland', Active__c = true) + new Country__mdt(DeveloperName = 'USA', Active__c = true), + new Country__mdt(DeveloperName = 'France', Active__c = true), + new Country__mdt(DeveloperName = 'Poland', Active__c = true) }); public static Id deploy(List customMetadataRecords) { - Metadata.DeployContainer mdContainer = new Metadata.DeployContainer(); + Metadata.DeployContainer mdContainer = new Metadata.DeployContainer(); - for (SObject record : customMetadataRecords) { - Metadata.CustomMetadata customMetadata = new Metadata.CustomMetadata(); - customMetadata.fullName = ('' + record.getSObjectType()).remove('__mdt') + '.' + record.get('DeveloperName'); - customMetadata.label = (String) record.get('Label'); + for (SObject record : customMetadataRecords) { + Metadata.CustomMetadata customMetadata = new Metadata.CustomMetadata(); + customMetadata.fullName = ('' + record.getSObjectType()).remove('__mdt') + '.' + record.get('DeveloperName'); + customMetadata.label = (String) record.get('Label'); - Map populatedFields = record.getPopulatedFieldsAsMap().clone(); - populatedFields.remove('Id'); - populatedFields.remove('Label'); - populatedFields.remove('DeveloperName'); + Map populatedFields = record.getPopulatedFieldsAsMap().clone(); + populatedFields.remove('Id'); + populatedFields.remove('Label'); + populatedFields.remove('DeveloperName'); - for (String field : populatedFields.keySet()) { - Metadata.CustomMetadataValue customField = new Metadata.CustomMetadataValue(); - customField.field = field; - customField.value = populatedFields.get(field); + for (String field : populatedFields.keySet()) { + Metadata.CustomMetadataValue customField = new Metadata.CustomMetadataValue(); + customField.field = field; + customField.value = populatedFields.get(field); - customMetadata.values.add(customField); - } + customMetadata.values.add(customField); + } - mdContainer.addMetadata(customMetadata); - } + mdContainer.addMetadata(customMetadata); + } - return Metadata.Operations.enqueueDeployment(mdContainer, null); + return Metadata.Operations.enqueueDeployment(mdContainer, null); } ```
\ No newline at end of file diff --git a/docs/DatabaseService.md b/docs/DatabaseService.md index 671a139..7e89f35 100644 --- a/docs/DatabaseService.md +++ b/docs/DatabaseService.md @@ -20,12 +20,12 @@ DatabaseService encapsulates `System.Database` methods and allow for altering be ```apex | Usage | MyService performs update "without sharing" even though the class itself is "with sharing" public with sharing class MyService { - private DatabaseService databaseService = new DatabaseService() - .withoutSharing(); + private DatabaseService databaseService = new DatabaseService() + .withoutSharing(); - public void updateContacts(List contacts) { - databaseService.updateRecords(contacts); - } + public void updateContacts(List contacts) { + databaseService.updateRecords(contacts); + } } ``` @@ -36,12 +36,12 @@ method for allOrNone() parameter. DML Options can be constructed with builder cl ```apex DatabaseService databaseService = new DatabaseService() - .setDMLOptions(new DMLOptionsBuilder() - .allowDuplicates(false) - .allowFieldTruncation(false) - .allOrNone(false) - .build() - ); + .setDMLOptions(new DMLOptionsBuilder() + .allowDuplicates(false) + .allowFieldTruncation(false) + .allOrNone(false) + .build() + ); //Shortcut for All or None option DatabaseService databaseService = new DatabaseService().allOrNone(false); @@ -53,12 +53,12 @@ Database Service can switch between inherited, with sharing and without sharing ```apex DatabaseService databaseService = new DatabaseService() - .withSharing() - .insertRecords(accounts); + .withSharing() + .insertRecords(accounts); DatabaseService databaseService = new DatabaseService() - .withoutSharing() - .updateRecords(accounts); + .withoutSharing() + .updateRecords(accounts); ``` @@ -78,29 +78,29 @@ For example purposes, let's assume this class does some inserts and updates; ```apex public class AccountService { - @TestVisible private DatabaseService databaseService = new DatabaseService(); - - public void doBusinessLogic() { - // Create Account - Account account = new Account(Name = 'Test Account'); - account.BillingCountry = 'USA'; - databaseService.insertRecord(account); - - // Create Contact - Contact contact = new Contact(LastName = 'Doe', AccountId = account.Id); - databaseService.insertRecord(contact); - - // Create Opportunities - DatabaseService.insertRecords(new List{ - new Opportunity(AccountId = account.Id, Name = 'Opportunity 1'), - new Opportunity(AccountId = account.Id, Name = 'Opportunity 2'), - new Opportunity(AccountId = account.Id, Name = 'Opportunity 3') - }); - - // Update Contact field on Account - account.Contact__c = contact.Id; - databaseService.updateRecord(account); - } + @TestVisible private DatabaseService databaseService = new DatabaseService(); + + public void doBusinessLogic() { + // Create Account + Account account = new Account(Name = 'Test Account'); + account.BillingCountry = 'USA'; + databaseService.insertRecord(account); + + // Create Contact + Contact contact = new Contact(LastName = 'Doe', AccountId = account.Id); + databaseService.insertRecord(contact); + + // Create Opportunities + DatabaseService.insertRecords(new List{ + new Opportunity(AccountId = account.Id, Name = 'Opportunity 1'), + new Opportunity(AccountId = account.Id, Name = 'Opportunity 2'), + new Opportunity(AccountId = account.Id, Name = 'Opportunity 3') + }); + + // Update Contact field on Account + account.Contact__c = contact.Id; + databaseService.updateRecord(account); + } } ``` @@ -112,18 +112,18 @@ This is how it should look like in unit test: ```apex @IsTest static void testX() { - AccountService accountService = new AccountService(); - DatabaseMock dbMock = accountService.databaseService.useMock(); + AccountService accountService = new AccountService(); + DatabaseMock dbMock = accountService.databaseService.useMock(); - Test.startTest(); - accountService.doBusinessLogic(); - Test.stopTest(); + Test.startTest(); + accountService.doBusinessLogic(); + Test.stopTest(); - // Now we can check inserted and updated records: - Assert.areEqual(4, dbMock.insertedRecords.size()); - Assert.areEqual(1, dbMock.updatedRecords.size()); + // Now we can check inserted and updated records: + Assert.areEqual(4, dbMock.insertedRecords.size()); + Assert.areEqual(1, dbMock.updatedRecords.size()); } ``` @@ -194,46 +194,46 @@ I will cover them there. ```apex class DatabaseService { - static Id getFakeId(SObjectType sObjectType); + static Id getFakeId(SObjectType sObjectType); - DatabaseService withSharing(); - DatabaseService withoutSharing(); - DatabaseService setDMLOptions(Database.DMLOptions options); - DatabaseService allOrNone(Boolean allOrNone); + DatabaseService withSharing(); + DatabaseService withoutSharing(); + DatabaseService setDMLOptions(Database.DMLOptions options); + DatabaseService allOrNone(Boolean allOrNone); - DatabaseMock useMock(); + DatabaseMock useMock(); - List query(String query); - List query(String query, Map boundVars); + List query(String query); + List query(String query, Map boundVars); - Database.QueryLocator getQueryLocator(String query); - Database.QueryLocator getQueryLocator(String query, Map boundVars); + Database.QueryLocator getQueryLocator(String query); + Database.QueryLocator getQueryLocator(String query, Map boundVars); - Database.SaveResult insertRecord(SObject record); - Database.SaveResult updateRecord(SObject record); - Database.UpsertResult upsertRecord(SObject record, SObjectField field); - Database.DeleteResult deleteRecord(SObject record); - Database.UndeleteResult undeleteRecord(SObject record); + Database.SaveResult insertRecord(SObject record); + Database.SaveResult updateRecord(SObject record); + Database.UpsertResult upsertRecord(SObject record, SObjectField field); + Database.DeleteResult deleteRecord(SObject record); + Database.UndeleteResult undeleteRecord(SObject record); - List insertRecords(List records); - List updateRecords(List records); - List upsertRecords(List records, SObjectField field); - List deleteRecords(List records); - List undeleteRecords(List records); + List insertRecords(List records); + List updateRecords(List records); + List upsertRecords(List records, SObjectField field); + List deleteRecords(List records); + List undeleteRecords(List records); } ``` ```apex | DatabaseMock public with sharing class DatabaseMock { - List insertedRecords; - List updatedRecords; - List upsertedRecords; - List deletedRecords; - List undeletedRecords; - - DatabaseMock mockDmlError(SObject matcherRecord); - DatabaseMock mockDmlError(DmlType dmlType); - DatabaseMock mockDmlError(DmlType dmlType, SObject matcherRecord); - DatabaseMock mockDmlError(DmlType dmlType, SObject matcherRecord, String errorMsg); - List getDMLErrors(DmlType issuedDML, SObject record); + List insertedRecords; + List updatedRecords; + List upsertedRecords; + List deletedRecords; + List undeletedRecords; + + DatabaseMock mockDmlError(SObject matcherRecord); + DatabaseMock mockDmlError(DmlType dmlType); + DatabaseMock mockDmlError(DmlType dmlType, SObject matcherRecord); + DatabaseMock mockDmlError(DmlType dmlType, SObject matcherRecord, String errorMsg); + List getDMLErrors(DmlType issuedDML, SObject record); ``` \ No newline at end of file diff --git a/docs/HttpCalloutMockRouter.md b/docs/HttpCalloutMockRouter.md index 54f73af..b7f5ce3 100644 --- a/docs/HttpCalloutMockRouter.md +++ b/docs/HttpCalloutMockRouter.md @@ -34,13 +34,13 @@ Then for each callout request, router will check configured mocks until it match @IsTest private class MyTest { - @IsTest - static void myCalloutTest() { - Test.startTest(); - Test.setMock(HttpCalloutMock.class, HttpMocks.config()); - // Do callout - Test.stopTest(); - } + @IsTest + static void myCalloutTest() { + Test.startTest(); + Test.setMock(HttpCalloutMock.class, HttpMocks.config()); + // Do callout + Test.stopTest(); + } } ``` @@ -54,10 +54,10 @@ tests uses OrgMocks class. 1. Router respond method checks mocks from custom metadata and any other mocks defined in code. 1. Each mock is checked for handled HTTP Methods, and if the endpoint matches with the request. 1. The first mock that matches response will have its response returned according to the settings. - - If StaticResource__c field is provided, mock will respond with it's content - - If ApexClass__c is provided, mock creates instance of this class and returns it. The class should implement HttpCalloutMock interface. - - If Response__c field is provided, it's returned as callout response body. - - If Headers__c field is provided - it's split by new lines and colons and added to response body headers. + - If StaticResource__c field is provided, mock will respond with it's content + - If ApexClass__c is provided, mock creates instance of this class and returns it. The class should implement HttpCalloutMock interface. + - If Response__c field is provided, it's returned as callout response body. + - If Headers__c field is provided - it's split by new lines and colons and added to response body headers. 1. The response is handled. ### Negative Scenario @@ -81,7 +81,7 @@ Consider this example: ```apex Test.setMock(HttpCalloutMock.class, new OrgMocks() - .overrideMock('SF_REST_Query', HttpMocks.config('SF_REST_Query_Empty'))); + .overrideMock('SF_REST_Query', HttpMocks.config('SF_REST_Query_Empty'))); ``` `SF_REST_Query` mock was replaced, and now it will respond with mock registered under name `SF_REST_Query_Empty`. @@ -112,27 +112,27 @@ as seen on example bellow. ```apex public class AzureBlobStorageMocks extends HttpCalloutMockRouter { - public AzureBlobStorageMocks() { - variables(new Map{ - 'endpoint' => 'https://azure.com/api', // Pattern for Microhard Endpoint shared by all requests - 'mdId' => '[\\d]{10}', // ID pattern of Microhard database entities, - 'container' => '[a-zA-Z\\s]+', //Matches blob container name - 'blob' => '[a-zA-Z\\s]+' //Matches blob name - }); - - mock('Auth', 'POST', '{{endpoint}}/auth', HttpMocks.text(200, 'OK', '{"sessionToken":"000001"}')); - mock('Get Blob', 'GET', '{{endpoint}}/blobs/{container}/{blob}', HttpMocks.staticResource(200, 'OK', 'AzureMocks_GetBlob_200')); - mock('Put Blob', 'PUT', '{{endpoint}}/blobs/{container}/{blob}', HttpMocks.json(200, 'OK', new CreateBlobResult())); - mock('Delete Blob', 'Delete', '{{endpoint}}/blobs/{container}/{blob}', HttpMocks.json(200, 'OK', new DeleteBlobResult())); - //... so on - } + public AzureBlobStorageMocks() { + variables(new Map{ + 'endpoint' => 'https://azure.com/api', // Pattern for Microhard Endpoint shared by all requests + 'mdId' => '[\\d]{10}', // ID pattern of Microhard database entities, + 'container' => '[a-zA-Z\\s]+', //Matches blob container name + 'blob' => '[a-zA-Z\\s]+' //Matches blob name + }); + + mock('Auth', 'POST', '{{endpoint}}/auth', HttpMocks.text(200, 'OK', '{"sessionToken":"000001"}')); + mock('Get Blob', 'GET', '{{endpoint}}/blobs/{container}/{blob}', HttpMocks.staticResource(200, 'OK', 'AzureMocks_GetBlob_200')); + mock('Put Blob', 'PUT', '{{endpoint}}/blobs/{container}/{blob}', HttpMocks.json(200, 'OK', new CreateBlobResult())); + mock('Delete Blob', 'Delete', '{{endpoint}}/blobs/{container}/{blob}', HttpMocks.json(200, 'OK', new DeleteBlobResult())); + //... so on + } } ``` ## HttpCalloutChainMock Interface ```apex interface HttpCalloutChainMock extends HttpCalloutMock { - Boolean handles(HttpRequest request); + Boolean handles(HttpRequest request); } ``` @@ -147,37 +147,37 @@ This approach only makes sense if you are not using custom metadata. ```apex public class OrgMocks extends HttpCalloutMockRouter { - public OrgMocks() { - mock('AWS Mocks', new AWSMocks()); - mock('Salesforce Mocks', new SalesforceMocks()); - mock('Microdwarf Mocks', new MicrodwarfMocks()); - mock('Noodle Mocks', new NoodleMocks()); - } + public OrgMocks() { + mock('AWS Mocks', new AWSMocks()); + mock('Salesforce Mocks', new SalesforceMocks()); + mock('Microdwarf Mocks', new MicrodwarfMocks()); + mock('Noodle Mocks', new NoodleMocks()); + } } public with sharing class AWSMocks extends HttpCalloutMockRouter { - public AWSMocks() { - variable('endpoint', 'https://aws.com/amazing/services/v/\\d'); + public AWSMocks() { + variable('endpoint', 'https://aws.com/amazing/services/v/\\d'); - mock('Auth', 'POST', '{{endpoint}}/auth', HttpMocks.text(200, 'OK', '{"sessionToken":"000001"}')); - mock('Save Quote', 'POST', '{{endpoint}}/quotes', HttpMocks.json(200, 'OK', new SaveQuoteResult())); - //... - } + mock('Auth', 'POST', '{{endpoint}}/auth', HttpMocks.text(200, 'OK', '{"sessionToken":"000001"}')); + mock('Save Quote', 'POST', '{{endpoint}}/quotes', HttpMocks.json(200, 'OK', new SaveQuoteResult())); + //... + } } public class SalesforceRestAPIMocks extends HttpCalloutMockRouter { - public SalesforceRestAPIMocks() { - variables(new Map{ - 'endpoint' => Url.getOrgDomainUrl().toExternalForm() + '/services/data/v\\d.0', - 'id' => '([0-9a-zA-Z]{15,18})' - }); + public SalesforceRestAPIMocks() { + variables(new Map{ + 'endpoint' => Url.getOrgDomainUrl().toExternalForm() + '/services/data/v\\d.0', + 'id' => '([0-9a-zA-Z]{15,18})' + }); - mock('Query', 'GET', '{{endpoint}}/query/?q=.*', HttpMocks.text(200, 'OK', 'StaticResourceWithResponse')); - mock('Create Account', 'POST', '{{endpoint}}/sobjects/Account/', HttpMocks.json(201, 'Created', new CreateRecordResult(true))); - //... - } + mock('Query', 'GET', '{{endpoint}}/query/?q=.*', HttpMocks.text(200, 'OK', 'StaticResourceWithResponse')); + mock('Create Account', 'POST', '{{endpoint}}/sobjects/Account/', HttpMocks.json(201, 'Created', new CreateRecordResult(true))); + //... + } } ``` @@ -188,20 +188,20 @@ public class SalesforceRestAPIMocks extends HttpCalloutMockRouter { * Method__c field accepts comma separated values: 'POST,PUT' * Variables should be defined as the first thing * `HttpMocks` class contains methods for creating mocks from different sources - * `HttpMocks.staticResource(Integer statusCode, String status, String resource)` - Responds with static resource body - * `HttpMocks.json(Integer statusCode, String status, Object o)` - Responds with a serialized object - * `HttpMocks.text(Integer statusCode, String status, String response)` - Responds with plain text response body - * `HttpMocks.config(String developerName)` - Responds with mock from Custom Metadata + * `HttpMocks.staticResource(Integer statusCode, String status, String resource)` - Responds with static resource body + * `HttpMocks.json(Integer statusCode, String status, Object o)` - Responds with a serialized object + * `HttpMocks.text(Integer statusCode, String status, String response)` - Responds with plain text response body + * `HttpMocks.config(String developerName)` - Responds with mock from Custom Metadata * Mocks can be easily extended with custom properties if needed: ```apex public class MyCustomMock implements HttpCalloutMock { - public HttpResponse respond(HttpRequest request) { - HttpResponse response = HttpMocks.config('MyMetadata').respond(request); + public HttpResponse respond(HttpRequest request) { + HttpResponse response = HttpMocks.config('MyMetadata').respond(request); - //modify response - response.setStatus(400); + //modify response + response.setStatus(400); - return response; - } + return response; + } } ``` \ No newline at end of file diff --git a/docs/Localization.md b/docs/Localization.md index bc86e6b..e40250e 100644 --- a/docs/Localization.md +++ b/docs/Localization.md @@ -51,19 +51,19 @@ public static String getCustomLabel(String labelName, String locale); ##### Usage
- Get labels in bulk for given locale + Get labels in bulk for given locale ```apex Map labels = Localization.getCustomLabelsWithLocale(new List{ - 'COM_Toast_Success', - 'COM_Toast_Info' + 'COM_Toast_Success', + 'COM_Toast_Info' }, 'pl'); ``` ```json { - "COM_Toast_Info": "Info", - "COM_Toast_Success": "Sukces" + "COM_Toast_Info": "Info", + "COM_Toast_Success": "Sukces" } ```
@@ -90,19 +90,19 @@ public static String getFieldLabel(String field, String locale); ##### Usage
- Get labels in bulk for given locale + Get labels in bulk for given locale ```apex Map labels = Localization.getFieldLabelsWithLocale(new List{ - 'Account.Type', - 'Opportunity.StageName' + 'Account.Type', + 'Opportunity.StageName' }, 'pl'); ``` ```json { - "Account.Type": "Typ konta", - "Opportunity.StageName": "Etap" + "Account.Type": "Typ konta", + "Opportunity.StageName": "Etap" } ```
@@ -128,59 +128,59 @@ Retrieves translated labels of picklist options for given field and returns them ##### Usage
- Single Picklist + Single Picklist ```apex Map labels = Localization.getPicklistLabels('Opportunity.StageName', 'pl'); ``` ```json { - "Prospecting": "Prospecting", - "Qualification": "Qualification", - "Needs Analysis": "Needs Analysis", - "Value Proposition": "Value Proposition", - "Id. Decision Makers": "Id. Decision Makers", - "Perception Analysis": "Perception Analysis", - "Proposal/Price Quote": "Proposal/Price Quote", - "Negotiation/Review": "Negotiation/Review", - "Closed Won": "Closed Won", - "Closed Lost": "Closed Lost" + "Prospecting": "Prospecting", + "Qualification": "Qualification", + "Needs Analysis": "Needs Analysis", + "Value Proposition": "Value Proposition", + "Id. Decision Makers": "Id. Decision Makers", + "Perception Analysis": "Perception Analysis", + "Proposal/Price Quote": "Proposal/Price Quote", + "Negotiation/Review": "Negotiation/Review", + "Closed Won": "Closed Won", + "Closed Lost": "Closed Lost" } ```
- Bulk + Bulk ```apex Map> labels = Localization.getPicklistsLabels(new List{ - 'Account.Type', - 'Opportunity.StageName' + 'Account.Type', + 'Opportunity.StageName' }, 'pl'); ``` ```json { - "Account.Type": { - "Prospect": "Prospect", - "Customer - Direct": "Customer - Direct", - "Customer - Channel": "Customer - Channel", - "Channel Partner / Reseller": "Channel Partner / Reseller", - "Installation Partner": "Installation Partner", - "Technology Partner": "Technology Partner", - "Other": "Other" - }, - "Opportunity.StageName": { - "Prospecting": "Prospecting", - "Qualification": "Qualification", - "Needs Analysis": "Needs Analysis", - "Value Proposition": "Value Proposition", - "Id. Decision Makers": "Id. Decision Makers", - "Perception Analysis": "Perception Analysis", - "Proposal/Price Quote": "Proposal/Price Quote", - "Negotiation/Review": "Negotiation/Review", - "Closed Won": "Closed Won", - "Closed Lost": "Closed Lost" - } + "Account.Type": { + "Prospect": "Prospect", + "Customer - Direct": "Customer - Direct", + "Customer - Channel": "Customer - Channel", + "Channel Partner / Reseller": "Channel Partner / Reseller", + "Installation Partner": "Installation Partner", + "Technology Partner": "Technology Partner", + "Other": "Other" + }, + "Opportunity.StageName": { + "Prospecting": "Prospecting", + "Qualification": "Qualification", + "Needs Analysis": "Needs Analysis", + "Value Proposition": "Value Proposition", + "Id. Decision Makers": "Id. Decision Makers", + "Perception Analysis": "Perception Analysis", + "Proposal/Price Quote": "Proposal/Price Quote", + "Negotiation/Review": "Negotiation/Review", + "Closed Won": "Closed Won", + "Closed Lost": "Closed Lost" + } } ```
\ No newline at end of file diff --git a/docs/Logger.md b/docs/Logger.md index 3c24941..24658b5 100644 --- a/docs/Logger.md +++ b/docs/Logger.md @@ -30,16 +30,16 @@ or when a transaction fails. ```apex public with sharing class SomeCtrl { - @AuraEnabled - public static void doSomething() { - try { - SomeService.doBusinessLogic(); - - } catch (Exception ex) { - Logger.error(ex); - throw new AuraHandledException(ex.getMessage()); - } - } + @AuraEnabled + public static void doSomething() { + try { + SomeService.doBusinessLogic(); + + } catch (Exception ex) { + Logger.error(ex); + throw new AuraHandledException(ex.getMessage()); + } + } } ``` @@ -69,20 +69,20 @@ Logger.error(new Log(RestContext.request, RestContext.response)); ```apex public with sharing class SomeCtrl { - private static Datetime startTime = Datetime.now(); - - @AuraEnabled - public static void updateAccount(Id accountId) { - try { - SomeService.doBusinessLogic(accountId); - - } catch (Exception ex) { - Logger.error(new Log(ex) - .withReferenceId(accountId) - .withTimeMetric(startTime)); - throw new AuraHandledException(ex.getMessage()); - } - } + private static Datetime startTime = Datetime.now(); + + @AuraEnabled + public static void updateAccount(Id accountId) { + try { + SomeService.doBusinessLogic(accountId); + + } catch (Exception ex) { + Logger.error(new Log(ex) + .withReferenceId(accountId) + .withTimeMetric(startTime)); + throw new AuraHandledException(ex.getMessage()); + } + } } ``` @@ -92,23 +92,23 @@ public with sharing class SomeCtrl { ```apex public class Logger { - public static void info(Log log) {} - public static void warn(Log log) {} - public static void error(Log log) {} - public static void log(LoggingLevel loggingLevel, Log log) {} + public static void info(Log log) {} + public static void warn(Log log) {} + public static void error(Log log) {} + public static void log(LoggingLevel loggingLevel, Log log) {} } public class Log { - public Log(String message) {} - public Log(Exception ex) {} - public Log(HttpRequest request, HttpResponse response) {} - public Log(RestRequest request, RestResponse response) {} - public Log(LoggingEvent__e log) {} - - public Log withReferenceId(String referenceId) {} - public Log withParameter(String param, Object value) {} - public Log withParameters(Map parameters) {} - public Log withTimeMetric(Long timeMs) {} - public Log withTimeMetric(Datetime startTime) {} + public Log(String message) {} + public Log(Exception ex) {} + public Log(HttpRequest request, HttpResponse response) {} + public Log(RestRequest request, RestResponse response) {} + public Log(LoggingEvent__e log) {} + + public Log withReferenceId(String referenceId) {} + public Log withParameter(String param, Object value) {} + public Log withParameters(Map parameters) {} + public Log withTimeMetric(Long timeMs) {} + public Log withTimeMetric(Datetime startTime) {} } ``` \ No newline at end of file diff --git a/docs/Mock.md b/docs/Mock.md index 0204adc..fd5332f 100644 --- a/docs/Mock.md +++ b/docs/Mock.md @@ -24,17 +24,17 @@ In unit tests, we want to mock the response of the selector class. ```apex public class MyAccountCtrl { - @TestVisible AccountSelector accountSelector = new AccountSelector(); + @TestVisible AccountSelector accountSelector = new AccountSelector(); - public static Account getMyAccount() { - try { - Id myId = UserInfo.getUserId(); - return accountSelector.getByOwnerId(myId); + public static Account getMyAccount() { + try { + Id myId = UserInfo.getUserId(); + return accountSelector.getByOwnerId(myId); - } catch (Exception e) { - throw new AuraHandledException('Account unavailable'); - } - } + } catch (Exception e) { + throw new AuraHandledException('Account unavailable'); + } + } } ``` @@ -43,19 +43,19 @@ If the code only calls one method from mocked class, we can skip the method name ```apex public class MyAccountCtrlTest { - @IsTest - static void testGetMyAccount() { - MyAccountCtrl.accountSelector = (AccountSelector) Mock.response(AccountSelector.class, - new Account(Name = 'My Account') - ); - } + @IsTest + static void testGetMyAccount() { + MyAccountCtrl.accountSelector = (AccountSelector) Mock.response(AccountSelector.class, + new Account(Name = 'My Account') + ); + } } ``` If response value is an exception, that exception will be thrown when mocked method is called. ```apex MyAccountCtrl.accountSelector = (AccountSelector) Mock.response(AccountSelector.class, - new QueryException('List has no rows for assignment to SObject') + new QueryException('List has no rows for assignment to SObject') ); ``` @@ -65,12 +65,12 @@ Mock response just for given method. Other methods will return null. ```apex public class MyAccountCtrlTest { - @IsTest - static void testGetMyAccount() { - MyAccountCtrl.accountSelector = (AccountSelector) Mock.response(AccountSelector.class, - 'getByOwnerId', new Account(Name = 'My Account') - ); - } + @IsTest + static void testGetMyAccount() { + MyAccountCtrl.accountSelector = (AccountSelector) Mock.response(AccountSelector.class, + 'getByOwnerId', new Account(Name = 'My Account') + ); + } } ``` @@ -80,16 +80,16 @@ In example bellow, calling `accountSelector.getById()` will yield a different re ```apex public class MyAccountCtrlTest { - @IsTest - static void testGetMyAccount() { - MyAccountCtrl.accountSelector = (AccountSelector) Mock.response(AccountSelector.class, - new Map{ - 'getByOwnerId' => new Account(Name = 'My Account'), - 'getById#1' => new Account(Name = 'Test Account'), - 'getById#2' => new Account(Name = 'Another Account'), - 'getById#3' => new QueryException('List has no rows for assignment to SObject') - } - ); - } + @IsTest + static void testGetMyAccount() { + MyAccountCtrl.accountSelector = (AccountSelector) Mock.response(AccountSelector.class, + new Map{ + 'getByOwnerId' => new Account(Name = 'My Account'), + 'getById#1' => new Account(Name = 'Test Account'), + 'getById#2' => new Account(Name = 'Another Account'), + 'getById#3' => new QueryException('List has no rows for assignment to SObject') + } + ); + } } ``` \ No newline at end of file diff --git a/docs/Picklist.md b/docs/Picklist.md index 4f169d6..d0eecbd 100644 --- a/docs/Picklist.md +++ b/docs/Picklist.md @@ -36,10 +36,10 @@ Picklist.Entry[] entries = p.getEntries();//(Aura Enabled) ```apex | Picklist.Entry class public class Entry { - @AuraEnabled public String label { get; private set; } - @AuraEnabled public String value { get; private set; } - @AuraEnabled public Boolean defaultValue { get; private set; } - private String validFor; - private Boolean active; + @AuraEnabled public String label { get; private set; } + @AuraEnabled public String value { get; private set; } + @AuraEnabled public Boolean defaultValue { get; private set; } + private String validFor; + private Boolean active; } ``` \ No newline at end of file diff --git a/docs/Query.md b/docs/Query.md index f76b3ac..4218ace 100644 --- a/docs/Query.md +++ b/docs/Query.md @@ -44,10 +44,10 @@ Query Framework assumes that each query may need to be tailored to the specific Consider this example, where we can add additional fields to the selector's base field set. ```apex List contact = Query.Contacts - .byAccountId(/*...ids*/) - .withParentAccount() - .withFields('FirstName, LastName, CreatedBy.Name') - .getList(); + .byAccountId(/*...ids*/) + .withParentAccount() + .withFields('FirstName, LastName, CreatedBy.Name') + .getList(); ``` ### Define WHERE Clause @@ -55,80 +55,80 @@ List contact = Query.Contacts Where clause can be combined from methods implemented in ContactQuery and generic methods implemented in QueryObject class. ```apex List contact = Query.Contacts - .byAccountId(/*...ids*/) - .byRecordTypeId(/*Record Type Id*/) - .getList(); + .byAccountId(/*...ids*/) + .byRecordTypeId(/*Record Type Id*/) + .getList(); ``` For very specialized and complex queries, there are multiple ways to define the conditions: - Combining field filters and declaring filter logic. Each identifier in the logic string (`{0}`) corresponds to the `byCondition()` method above in the order they were declared. - ```apex - Query.Accounts - .byName('TestName') - .byRecordTypeDeveloperName('SMB') - .byOwnerId(UserInfo.getUserId()) - .withFilterLogic('{0} OR ({1} AND {2})') - .getList(); - ``` + ```apex + Query.Accounts + .byName('TestName') + .byRecordTypeDeveloperName('SMB') + .byOwnerId(UserInfo.getUserId()) + .withFilterLogic('{0} OR ({1} AND {2})') + .getList(); + ``` - Introduce case-specific filtering method in SObject's query class: - ```apex - class ContactQuery { - - public ContactQuery byMySuperSpecificCondition(String name, String recordTypeName, Id ownerId) { - appendMockName('byMySuperSpecificCondition'); - return (ContactQuery) wheres('Name =:name OR (RecordType.DeveloperName = :recordTypeName AND OwnerId = :ownerId)', - new Map{ - 'name' => name, - 'recordTypeName' => recordTypeName, - 'ownerId' => ownerId - }); - } - } - ``` - ```apex + ```apex + class ContactQuery { + + public ContactQuery byMySuperSpecificCondition(String name, String recordTypeName, Id ownerId) { + appendMockName('byMySuperSpecificCondition'); + return (ContactQuery) wheres('Name =:name OR (RecordType.DeveloperName = :recordTypeName AND OwnerId = :ownerId)', + new Map{ + 'name' => name, + 'recordTypeName' => recordTypeName, + 'ownerId' => ownerId + }); + } + } + ``` + ```apex Query.Accounts.byMySuperSpecificCondition( - 'TestName', 'SMB', UserInfo.getUserId() + 'TestName', 'SMB', UserInfo.getUserId() ).getList(); - ``` + ``` You can even mix that with other methods: - ```apex + ```apex Query.Accounts .byMySuperSpecificCondition( 'TestName', 'SMB', UserInfo.getUserId() ) .byIsActive(true) .getList(); - ``` + ``` - Using QueryConditions to build the query: - ```apex - QueryConditions c = new QueryConditions(); - Query.Accounts - .wheres( - c.ORs( - c.field(Account.Name).equals('TestName'), - c.ANDs( - c.field('RecordType.DeveloperName').equals('SMB'), - c.field(Account.OwnerId).equals(UserInfo.getUserId()) - ) - ) - ) - .getList(); - ``` + ```apex + QueryConditions c = new QueryConditions(); + Query.Accounts + .wheres( + c.ORs( + c.field(Account.Name).equals('TestName'), + c.ANDs( + c.field('RecordType.DeveloperName').equals('SMB'), + c.field(Account.OwnerId).equals(UserInfo.getUserId()) + ) + ) + ) + .getList(); + ``` - Writing WHERE clause directly in client code: - ```apex - List names = new List(); - List externalIds = new List(); - Id recordTypeId; - - Query.Accounts - .wheres('Name IN :names OR (RecordTypeId =:rtId AND ExternalID IN :externalIds)', new Map{ - 'names' => names, - 'rtId' => recordTypeId, - 'externalIds' => externalIds - }) - .getList(); - ``` + ```apex + List names = new List(); + List externalIds = new List(); + Id recordTypeId; + + Query.Accounts + .wheres('Name IN :names OR (RecordTypeId =:rtId AND ExternalID IN :externalIds)', new Map{ + 'names' => names, + 'rtId' => recordTypeId, + 'externalIds' => externalIds + }) + .getList(); + ``` ### Reduce to result Query result can be reduced to different things: @@ -215,15 +215,15 @@ It's possible to mock query response in the unit test as follows: ```apex @IsTest static void myTestMethod() { - Query.Accounts.mock('.byExternalId', new List{ - // my mocked query result - }); - - Test.startTest(); - List accounts = Query.Accounts - .byExternalId(/*...*/) - .getList(); //returns mocked result - Test.stopTest(); + Query.Accounts.mock('.byExternalId', new List{ + // my mocked query result + }); + + Test.startTest(); + List accounts = Query.Accounts + .byExternalId(/*...*/) + .getList(); //returns mocked result + Test.stopTest(); } ``` @@ -231,13 +231,13 @@ Mock name is generated from the used methods: ```apex // For Query in class: List accounts = Query.Accounts - .byExternalId(/*...*/) - .byRecordTypeId() - .getList(); //returns mock + .byExternalId(/*...*/) + .byRecordTypeId() + .getList(); //returns mock //Mock as follows: Query.Accounts.mock('.byExternalId.byRecordTypeId', new List{ - // my mocked query result + // my mocked query result }); //When in doubt add .debugLogMockName(); to the query chain. @@ -247,14 +247,14 @@ Alternatively, you can define your mock name to mock exactly that one query that ```apex // In your class: List accounts = Query.Accounts - .byExternalId(/*...*/) - .withMockName('myAccountQuery') - .getList(); //returns mocked result + .byExternalId(/*...*/) + .withMockName('myAccountQuery') + .getList(); //returns mocked result // In your test: Query.Accounts.mock('myAccountQuery', new List{ - // my mocked query result + // my mocked query result }); ``` @@ -274,12 +274,12 @@ extends `Query`. In the constructor, we should define default fields (optional) and sObject type (required): ```apex public with sharing class AccountQuery extends Query { - public AccountQuery() { - super(new List{ - 'Id', - 'Name' - }, Account.SObjectType); - } + public AccountQuery() { + super(new List{ + 'Id', + 'Name' + }, Account.SObjectType); + } } ``` @@ -287,15 +287,15 @@ Then we can add Account-specific methods — that could be methods that add more Condition methods should append mock name - this way, we can use this string for mocking in unit tests, without specifying mock name explicitly. ```apex public AccountQuery withContacts() { - withChildren(new List{ - 'Id' - }, 'Contacts'); - return this; + withChildren(new List{ + 'Id' + }, 'Contacts'); + return this; } public AccountQuery byName(Set names) { - appendMockName('byName'); - return (AccountQuery) byField(Account.Name, 'IN', names); + appendMockName('byName'); + return (AccountQuery) byField(Account.Name, 'IN', names); } ``` @@ -315,8 +315,8 @@ It's possible to construct a query without an inheritance, but it will be only p ```apex Query.fromSObject(Account.SObjectType) - .byField(Account.Name, '=', 'Test Account') - .getList(); + .byField(Account.Name, '=', 'Test Account') + .getList(); ``` ## QueryObject @@ -328,7 +328,7 @@ protected QueryObject(List fields, SObjectType sObjectType) ### Selecting Fields
- withFields(Query.Fields field) + withFields(Query.Fields field) ```apex public QueryObject withFields(Query.Fields field); @@ -344,14 +344,14 @@ Query is restricted to 200 records in case of ALL and CUSTOM fields. #### Usage ```apex Query.Accounts - .withFields(Query.Fields.ALL) - .getList(); + .withFields(Query.Fields.ALL) + .getList(); ```
- withFields(fields) + withFields(fields) ```apex public QueryObject withFields(String fields); @@ -366,16 +366,16 @@ Adds given fields to the query. #### Usage ```apex Query.Accounts - .withFields('Id, Name, Parent.Name') - //or - .withFields(new List{'Id', 'Name', 'Parent.Name'}) - .getList(); + .withFields('Id, Name, Parent.Name') + //or + .withFields(new List{'Id', 'Name', 'Parent.Name'}) + .getList(); ```
- withAllFields() + withAllFields() ```apex public QueryObject withAllFields(); @@ -387,14 +387,14 @@ to 200 records #### Usage ```apex Query.Accounts - .withAllFields() - .getList(); + .withAllFields() + .getList(); ```
- withChildren(fields, relationshipName) + withChildren(fields, relationshipName) ```apex public QueryObject withChildren(String fieldsCSV, String relationshipName); @@ -410,10 +410,10 @@ Adds subquery with given fields and relationship name. Disables caching. #### Usage ```apex Query.Accounts - .withChildren('FirstName, LastName', 'Contacts') - //or - .withChildren(new List{'FirstName', 'LastName', 'Contacts'}) - .getList(); + .withChildren('FirstName, LastName', 'Contacts') + //or + .withChildren(new List{'FirstName', 'LastName', 'Contacts'}) + .getList(); ```
@@ -433,15 +433,15 @@ Adds subquery using another Query instance. #### Usage ```apex Query.Accounts - .withChildren(Query.Contacts.byLastName('Doe'), 'Contacts') - .getList(); + .withChildren(Query.Contacts.byLastName('Doe'), 'Contacts') + .getList(); ``` ### Conditions
- withFilterLogic(filterLogic) + withFilterLogic(filterLogic) ```apex public QueryObject withFilterLogic(String filterLogic); @@ -455,11 +455,11 @@ Provide filter logic for previously specified conditions #### Usage ```apex Query.Accounts - .byName('TestName') - .byRecordTypeDeveloperName('SMB') - .byOwnerId(UserInfo.getUserId()) - .withFilterLogic('{0} OR ({1} AND {2})') - .toString(); + .byName('TestName') + .byRecordTypeDeveloperName('SMB') + .byOwnerId(UserInfo.getUserId()) + .withFilterLogic('{0} OR ({1} AND {2})') + .toString(); ``` Evaluates to: ```sql @@ -471,7 +471,7 @@ WHERE
- byId() + byId() ```apex public virtual QueryObject byId(Id recordId); @@ -500,7 +500,7 @@ Query.Account.byId(accounts).getList();
- byRecordTypeId() + byRecordTypeId() ```apex public virtual QueryObject byRecordTypeId(Id recordTypeId); @@ -519,7 +519,7 @@ Query.Accounts.byRecordTypeId(RecordTypes.Account.SMB.Id).getList();
- byRecordTypeDeveloperName() + byRecordTypeDeveloperName() ```apex public virtual QueryObject byRecordTypeDeveloperName(String recordTypeDeveloperName); @@ -537,7 +537,7 @@ Query.Accounts.byRecordTypeDeveloperName('SMB').getList();
- byOwnerId(ownerIds) + byOwnerId(ownerIds) ```apex public virtual QueryObject byOwnerId(Id ownerId); @@ -558,7 +558,7 @@ Query.Accounts.byOwnerId(thisUser).getList();
- relatedToChildren() + relatedToChildren() ```apex public virtual QueryObject relatedToChildren(SObject[] childRecords, SObjectField relationShipField); @@ -577,7 +577,7 @@ Query.Accounts.relatedToChildren(contacts, Contact.AccountId);
- relatedToParent() + relatedToParent() ```apex public virtual QueryObject relatedToParent(SObject[] parentRecords, SObjectField relationShipField); @@ -595,7 +595,7 @@ Query.Contacts.relatedToParent(accounts, Contact.AccountId);
- byField(field, operator, value) + byField(field, operator, value) ```apex public virtual QueryObject byField(SObjectField field, String operator, Object value); @@ -620,7 +620,7 @@ Query.Accounts.byField(Account.OwnerId, 'IN', owners);
- wheres(whereString, params) + wheres(whereString, params) ```apex public QueryObject wheres(String whereString, Map params); @@ -634,24 +634,24 @@ Adds explicitly typed WHERE condition. #### Usage ```apex | Example 1 - Usage in internal method public AccountQuery byParentIds(Set parentIds) { - wheres('ParentId IN :parentIds', new Map{'parentIds' => parentIds}); - return this; + wheres('ParentId IN :parentIds', new Map{'parentIds' => parentIds}); + return this; } ``` ```apex | Example 2 - Usage in client code Query.Accounts - .wheres('Parent.OwnerId IN :owners AND RecordType.DeveloperName = :rt', new Map{ - 'owners' => owners, - 'rt' => 'PersonAccount' - }); + .wheres('Parent.OwnerId IN :owners AND RecordType.DeveloperName = :rt', new Map{ + 'owners' => owners, + 'rt' => 'PersonAccount' + }); ```
- wheres(wheresCondition) + wheres(wheresCondition) ```apex public QueryObject wheres(Query.Condition wheres); @@ -662,18 +662,18 @@ Adds WHERE clause constructed from QueryConditions class. - `wheres` - Condition #### Usage ```apex - QueryConditions c = new QueryConditions(); + QueryConditions c = new QueryConditions(); Query.Accounts - .wheres( - c.ORs( - c.field(Account.Name).equals('TestName'), - c.ANDs( - c.field('RecordType.DeveloperName').equals('SMB'), - c.field(Account.OwnerId).equals(UserInfo.getUserId()) - ) - ) - ) - .getList(); + .wheres( + c.ORs( + c.field(Account.Name).equals('TestName'), + c.ANDs( + c.field('RecordType.DeveloperName').equals('SMB'), + c.field(Account.OwnerId).equals(UserInfo.getUserId()) + ) + ) + ) + .getList(); ```
@@ -681,7 +681,7 @@ Query.Accounts ### Aggregations
- groupBy(groupBy) + groupBy(groupBy) ```apex public QueryObject groupBy(String groupBy); @@ -694,16 +694,16 @@ Sets GROUP BY clause on the query. #### Usage ```apex Query.fromSObject(User.SObjectType) - .withFields('COUNT(Id), ProfileId') - .groupBy('ProfileId') - .havingCondition('COUNT(ID) > 1') - .toSOQL(); + .withFields('COUNT(Id), ProfileId') + .groupBy('ProfileId') + .havingCondition('COUNT(ID) > 1') + .toSOQL(); ```
- havingCondition(fields) + havingCondition(fields) ```apex public QueryObject havingCondition(String fields); @@ -718,16 +718,16 @@ Sets HAVING condition on the query #### Usage ```apex Query.fromSObject(User.SObjectType) - .withFields('COUNT(Id), ProfileId') - .groupBy('ProfileId') - .havingCondition('COUNT(ID) > :count', new Map{'count' => userCount}) - .toSOQL(); + .withFields('COUNT(Id), ProfileId') + .groupBy('ProfileId') + .havingCondition('COUNT(ID) > :count', new Map{'count' => userCount}) + .toSOQL(); ```
- havingCondition(condition) + havingCondition(condition) ```apex public QueryObject havingCondition(Query.Condition condition); @@ -741,13 +741,13 @@ Sets given condition as HAVING clause. ```apex QueryConditions c = new QueryConditions(); Query.Users - .selectFields('COUNT(ID), Email') - .havingConditions( - c.ANDs( - c.field('COUNT(ID)').greaterThan(5), - c.field('COUNT(ID)').lessThan(10) - ) - .getList(); + .selectFields('COUNT(ID), Email') + .havingConditions( + c.ANDs( + c.field('COUNT(ID)').greaterThan(5), + c.field('COUNT(ID)').lessThan(10) + ) + .getList(); ```
@@ -755,7 +755,7 @@ Query.Users ### Limit / Offset
- withLimit(limit) + withLimit(limit) ```apex public QueryObject withLimit(Integer l); @@ -768,15 +768,15 @@ Sets LIMIT clause on the query. #### Usage ```apex Query.Accounts - .byName('Test') - .withLimit(10) - .getList(); + .byName('Test') + .withLimit(10) + .getList(); ```
- withOffset(offset) + withOffset(offset) ```apex public QueryObject withOffset(Integer o); @@ -789,17 +789,17 @@ Sets OFFSET clause on the query. #### Usage ```apex Query.Accounts - .byName('Test') - .withLimit(10) - .withOffset(10) - .getList(); + .byName('Test') + .withLimit(10) + .withOffset(10) + .getList(); ```
### Mocking
- mock(mockName, result) + mock(mockName, result) ```apex public QueryObject mock(String mockName, Object result); @@ -822,7 +822,7 @@ Query.Accounts.mock('byName', mockAccounts);
- withMockName(mockName) + withMockName(mockName) ```apex public QueryObject withMockName(String mockName); @@ -844,7 +844,7 @@ Query.Accounts.mock('myquery', mockAccounts);
- debugLogMockName() + debugLogMockName() ```apex public QueryObject debugLogMockName(); @@ -855,9 +855,9 @@ Use this when in doubt what mocked name is expected. #### Usage ```apex Query.Account - .byName('Test') - .byRecordTypeDeveloperName('SMB') - .debugLogMockName(); + .byName('Test') + .byRecordTypeDeveloperName('SMB') + .debugLogMockName(); // > '.byName.byRecordTypeDeveloperName' ```
@@ -866,7 +866,7 @@ Query.Account ### Security
- withSharing() + withSharing() ```apex public QueryObject withSharing(); @@ -876,14 +876,14 @@ Query will be executed in "with sharing" context,returning only those records us #### Usage ```apex Query.Accounts - .withSharing() - .getList(); + .withSharing() + .getList(); ```
- withoutSharing() + withoutSharing() ```apex public QueryObject withoutSharing(); @@ -893,14 +893,14 @@ Query will be executed in "without sharing" context,returning only those records #### Usage ```apex Query.Accounts - .withoutSharing() - .getList(); + .withoutSharing() + .getList(); ```
- withFieldAccess(accessType) + withFieldAccess(accessType) ```apex public QueryObject withFieldAccess(AccessType accessType); @@ -917,14 +917,14 @@ Calls Security.stripInaccessible on the query result. #### Usage ```apex Query.Accounts - .withFieldAccess(AccessType.READABLE) - .getList(); + .withFieldAccess(AccessType.READABLE) + .getList(); ```
- usingCache(useCache) + usingCache(useCache) ```apex public QueryObject usingCache(Boolean useCache); @@ -937,15 +937,15 @@ Toggle usage of the cached records to limit SOQLs query limit. #### Usage ```apex | Query Profiles without resorting to cache. Query.Profile - .usingCache(false) - .getList(); + .usingCache(false) + .getList(); ```
### Reductors
- getList() + getList() ```apex public SObject[] getList(); @@ -955,14 +955,14 @@ Returns standard query result list. #### Usage ```apex List accounts = Query.Account - .byName('Test') - .getList(); + .byName('Test') + .getList(); ```
- getFirst() + getFirst() ```apex public SObject getFirst(); @@ -977,7 +977,7 @@ Account acc = (Account) Query.Account.byName('Test').getFirst();
- getFirstOrNull() + getFirstOrNull() ```apex public SObject getFirstOrNull(); @@ -993,7 +993,7 @@ Account acc = (Account) Query.Account.byName('Test').getFirstOrNull();
- getFirstIdOrNull() + getFirstIdOrNull() ```apex public Id getFirstIdOrNull(); @@ -1003,14 +1003,14 @@ Executes query and returns Id of the first record. If query result list is empty #### Usage ```apex Id systemAdmin = Query.Profile - .byName('System Administrator') - .getFirstIdOrNull(); + .byName('System Administrator') + .getFirstIdOrNull(); ```
- getFirstFieldOrNull(field) + getFirstFieldOrNull(field) ```apex public Object getFirstFieldOrNull(SObjectField field); @@ -1023,14 +1023,14 @@ Executes query and returns field of first record or null if list has no results #### Usage ```apex Id ownerId = (Id) Query.Account - .byName('Test Account') - .getFirstFieldOrNull(Account.OwnerId); + .byName('Test Account') + .getFirstFieldOrNull(Account.OwnerId); ```
- getFirstFieldOrFallback(field, fallbackValue) + getFirstFieldOrFallback(field, fallbackValue) ```apex public Object getFirstFieldOrFallback(SObjectField field, Object fallbackValue); @@ -1044,16 +1044,16 @@ Executes query and returns field of first record or fallback value if list has n #### Usage ```apex public static Id getDefaultOwner() { - Query.User - .byName('DEFAULT OWNER') - .getFirstFieldOrFallback(User.Id, UserInfo.getUserId()); + Query.User + .byName('DEFAULT OWNER') + .getFirstFieldOrFallback(User.Id, UserInfo.getUserId()); } ```
- getIds() + getIds() ```apex public Set getIds(); @@ -1063,14 +1063,14 @@ Returns Ids of SObjects. #### Usage ```apex Set accountIds = Query.Account - .byName('Test') - .getIds(); + .byName('Test') + .getIds(); ```
- getMapById() + getMapById() ```apex public Map getMapById(); @@ -1080,13 +1080,13 @@ Executes query and returns records mapped by Ids. #### Usage ```apex Map accountIds = (Map) Query.Account - .getMapById(); + .getMapById(); ```
- getMapByString(field) + getMapByString(field) ```apex public Map getMapByString(SObjectField field); @@ -1100,13 +1100,13 @@ Executes query and maps result records by given SObject Field. #### Usage ```apex Map accountIds = (Map) Query.Account - .getMapByString(Account.Name); + .getMapByString(Account.Name); ```
- QueryLocator getQueryLocator() + QueryLocator getQueryLocator() ```apex public Database.QueryLocator getQueryLocator(); @@ -1121,7 +1121,7 @@ Query.Accounts.getQueryLocator();
- getCount() + getCount() ```apex public Integer getCount(); @@ -1136,7 +1136,7 @@ Query.Accounts.getCount();
- toSOQL() + toSOQL() ```apex public String toSOQL(); @@ -1146,16 +1146,16 @@ Returns SOQL string for given query. Useful for unit testing query methods. #### Usage ```apex Query.Accounts - .byName('Test Account') - .byRecordTypDeveloperName('SMB') - .toSOQL(); + .byName('Test Account') + .byRecordTypDeveloperName('SMB') + .toSOQL(); // > SELECT Id, Name FROM Account WHERE ((Name IN :var0) AND (RecordType.DeveloperName IN :var1)) ```
- String toString() + String toString() ```apex public override String toString(); @@ -1165,9 +1165,9 @@ Returns SOQL string with bindings for given query. Useful for unit testing query #### Usage ```apex Query.Accounts - .byName('Test Account') - .byRecordTypDeveloperName('SMB') - .toSOQL(); + .byName('Test Account') + .byRecordTypDeveloperName('SMB') + .toSOQL(); // > SELECT Id, Name FROM Account WHERE ((Name IN :var0) AND (RecordType.DeveloperName IN :var1)), // {var0={Test Account}, var1={SMB}}) @@ -1188,16 +1188,16 @@ Consider the following selector: ```apex public with sharing class AccountQuery extends Query { - public AccountQuery() { - super(new List{ - 'Id', 'Name' - }, Account.SObjectType); - } + public AccountQuery() { + super(new List{ + 'Id', 'Name' + }, Account.SObjectType); + } - public AccountQuery byName(Set names) { - appendMockName('byName'); - return (AccountQuery) byField(Account.Name, 'IN', names); - } + public AccountQuery byName(Set names) { + appendMockName('byName'); + return (AccountQuery) byField(Account.Name, 'IN', names); + } } ``` @@ -1207,8 +1207,8 @@ As you can see, it returns `QueryObject`, not `AccountQuery`. ```apex new AccountQuery() - .withFields('Id, Name, BillingCity') // < this returns QueryObject, which doesn't have byName() method - .byName('Test Account'); + .withFields('Id, Name, BillingCity') // < this returns QueryObject, which doesn't have byName() method + .byName('Test Account'); ``` In modern languages, this is solved by generic types, but unfortunately Apex is 20 years behind the rest of the world. @@ -1219,18 +1219,18 @@ This problem can be solved in 3 ways: - Reordering methods, so the AccountQuery method is called first: ```apex new AccountQuery() - .byName('Test Account') - .withFields('Id, Name, BillingCity') - .getList(); + .byName('Test Account') + .withFields('Id, Name, BillingCity') + .getList(); ``` - Reintroduce method you need in AccountQuery and cast type. Unfortunately, we have to use different method name: ```apex public with sharing class AccountQuery extends Query { - public AccountQuery withFieldsx(String fields) { - return (AccountQuery) withFields(fields); - } + public AccountQuery withFieldsx(String fields) { + return (AccountQuery) withFields(fields); + } } ``` diff --git a/docs/Runtime.md b/docs/Runtime.md index 2fcb228..761735f 100644 --- a/docs/Runtime.md +++ b/docs/Runtime.md @@ -46,8 +46,8 @@ Runtime.getSObjectFieldType(Account.Name); // -> returns String.class Returns new list with the same item type like given SObject field. - `List newListOfItemType(Type itemType, List fallback)` Returns list of a given item type or fallback in case of exception - - `fallback` - Rallback type, in case a primary type is not constructible (ex. because the type is private). + - `fallback` + Rallback type, in case a primary type is not constructible (ex. because the type is private). ```apex List lst = new List(); @@ -70,45 +70,45 @@ Runtime.newListOfFieldType(Account.Name); // -> returns new List ```apex | Runtime Stack | Example when transaction is started from Aura method class MyController { - @AuraEnabled - public static void doSomething() { - try { - // ... - } catch (Exception e) { - Logger.error(e); - } - } + @AuraEnabled + public static void doSomething() { + try { + // ... + } catch (Exception e) { + Logger.error(e); + } + } } class Logger { - public static void error(Exception ex) { - Runtime.getRunningClass(); // returns 'Logger' - Runtime.getRunningMethod(); // returns 'Logger.error' - Runtime.getStackLocation(); // returns current stack place - // apexClassMethod - Logger.error - // apexClass - Logger - // method: error - - Runtime.getCaller(); // returns stack trace line with class and method that invoked MyController.doSomething(); - // - apexClassMethod: MyController.doSomething - // - apexClass: MyController - // - method: doSomething - - Runtime.getStackTrace(); // returns full stack trace array - // [ - // {"method":"error","line":38,"column":1,"apexClassMethod":"Logger.error","apexClass":"Logger"}, - // {"method":"doSomething","line":39,"column":1,"apexClassMethod":"MyController.doSomething","apexClass":"MyController"} - // ] - } + public static void error(Exception ex) { + Runtime.getRunningClass(); // returns 'Logger' + Runtime.getRunningMethod(); // returns 'Logger.error' + Runtime.getStackLocation(); // returns current stack place + // apexClassMethod - Logger.error + // apexClass - Logger + // method: error + + Runtime.getCaller(); // returns stack trace line with class and method that invoked MyController.doSomething(); + // - apexClassMethod: MyController.doSomething + // - apexClass: MyController + // - method: doSomething + + Runtime.getStackTrace(); // returns full stack trace array + // [ + // {"method":"error","line":38,"column":1,"apexClassMethod":"Logger.error","apexClass":"Logger"}, + // {"method":"doSomething","line":39,"column":1,"apexClassMethod":"MyController.doSomething","apexClass":"MyController"} + // ] + } } ``` ```apex | Stack Trace Line public class StackTraceLine { - public String apexClassMethod; - public String apexClass; - public String method; - public Integer line; - public Integer column; + public String apexClassMethod; + public String apexClass; + public String method; + public Integer line; + public Integer column; } ``` \ No newline at end of file diff --git a/docs/Scheduler.md b/docs/Scheduler.md index 46d7084..a049bc0 100644 --- a/docs/Scheduler.md +++ b/docs/Scheduler.md @@ -18,15 +18,15 @@ public static Id scheduleWeekly(String jobName, Integer hour, String dayOfWeek, public static Id scheduleMonthly(String jobName, Integer hour, String dayOfMonth, Schedulable job); public static Id schedule( - String jobName, - String seconds, - String minutes, - String hour, - String dayOfMonth, - String month, - String dayOfWeek, - String optionalYear, - Schedulable job); + String jobName, + String seconds, + String minutes, + String hour, + String dayOfMonth, + String month, + String dayOfWeek, + String optionalYear, + Schedulable job); ``` ```apex | Usage | The job will run everyday at 12:00 @@ -39,9 +39,9 @@ Scheduler.scheduleDaily('Data Cleaner', 12, 00, new DataCleaningSchedulable()); ## scheduleEveryXMinutes(jobName, everyMinutes, job) ```apex public static List scheduleEveryXMinutes( - String jobName, - Integer everyMinutes, - Schedulable job + String jobName, + Integer everyMinutes, + Schedulable job ); ``` Schedules multiple schedulable jobs to run every X minutes in an hour. @@ -60,9 +60,9 @@ Scheduler.scheduleEveryXMinutes('MySchedulableJob', 10, new MySchedulable()); ## scheduleHourly(jobName, minute, job) ```apex public static Id scheduleHourly( - String jobName, - Integer minute, - Schedulable job + String jobName, + Integer minute, + Schedulable job ); ``` Schedules a job to run every hour at specified minutes. @@ -135,15 +135,15 @@ Scheduler.scheduleMonthly('DataCleaner', 12, '1', new DataCleanerJob()); ## schedule(jobName, seconds, minutes, hour, dayOfMonth, month, dayOfWeek, optionalYear, job) ```apex public static Id schedule( - String jobName, - String seconds, - String minutes, - String hour, - String dayOfMonth, - String month, - String dayOfWeek, - String optionalYear, - Schedulable job); + String jobName, + String seconds, + String minutes, + String hour, + String dayOfMonth, + String month, + String dayOfWeek, + String optionalYear, + Schedulable job); ``` Helper method that breaks down cron expression into parameters. diff --git a/docs/SchemaConstants.md b/docs/SchemaConstants.md index d1ba738..110cd25 100644 --- a/docs/SchemaConstants.md +++ b/docs/SchemaConstants.md @@ -20,30 +20,30 @@ A better way is to expose schema constants through small, dedicated and lazily e ```apex public with sharing class Profiles { - public static Profile SystemAdministrator { get {return resolve('System Administrator');} } - public static Profile StandardUser { get {return resolve('Standard User');} } - public static Profile Dealer { get {return resolve('Dealer');} } - public static Profile SalesManager { get {return resolve('Sales Manager');} } - - private static Profile resolve(String name) { - // lazy evaluation - return cache.get(name); - } - - // Example of lazy evaluation and caching in static map, - // but Platform Cache would be much better and more performant. - private static Map cache = new Map(); - static { - for (Profile p : [SELECT Id, Name FROM Profile]) { - cache.put(p.Name, p); - } - } + public static Profile SystemAdministrator { get {return resolve('System Administrator');} } + public static Profile StandardUser { get {return resolve('Standard User');} } + public static Profile Dealer { get {return resolve('Dealer');} } + public static Profile SalesManager { get {return resolve('Sales Manager');} } + + private static Profile resolve(String name) { + // lazy evaluation + return cache.get(name); + } + + // Example of lazy evaluation and caching in static map, + // but Platform Cache would be much better and more performant. + private static Map cache = new Map(); + static { + for (Profile p : [SELECT Id, Name FROM Profile]) { + cache.put(p.Name, p); + } + } } ``` ```apex if (UserInfo.getProfileId() == Profiles.SystemAdministrator.Id) { - //... + //... } ``` @@ -54,35 +54,35 @@ All other classes will reference profile through this class. ```apex public with sharing class PermissionSets { - public static PermissionSet CommerceUser { get {return resolve('CommerceUser');} } - public static PermissionSet ManageOrders { get {return resolve('ManageOrders');} } - public static PermissionSet ManageAccounts { get {return resolve('ManageAccounts');} } - public static PermissionSet ManageContacts { get {return resolve('ManageContacts');} } + public static PermissionSet CommerceUser { get {return resolve('CommerceUser');} } + public static PermissionSet ManageOrders { get {return resolve('ManageOrders');} } + public static PermissionSet ManageAccounts { get {return resolve('ManageAccounts');} } + public static PermissionSet ManageContacts { get {return resolve('ManageContacts');} } - private static PermissionSet resolve(String name) { - //lazy evaluation - see Profiles example - } + private static PermissionSet resolve(String name) { + //lazy evaluation - see Profiles example + } } ``` ## Record Types ```apex public with sharing class RecordTypes { - public static RecordType ACCOUNT_SMB { get {return resolve(Account.SObjectType, 'SMB');} } - public static RecordType ACCOUNT_CUSTOMER { get {return resolve(Account.SObjectType, 'Customer');} } + public static RecordType ACCOUNT_SMB { get {return resolve(Account.SObjectType, 'SMB');} } + public static RecordType ACCOUNT_CUSTOMER { get {return resolve(Account.SObjectType, 'Customer');} } - private static Map> recordTypeCache = new Map>(); - private static RecordType resolve(SObjectType sObjectType, String developerName) { - // lazy evaluation code - } + private static Map> recordTypeCache = new Map>(); + private static RecordType resolve(SObjectType sObjectType, String developerName) { + // lazy evaluation code + } } ``` ```apex if (account.RecordTypeId == RecordTypes.Account.SMB.Id) { - //... + //... } ``` @@ -92,18 +92,18 @@ Expose Custom Permissions to your apex code, either directly through FeatureMana ```apex public with sharing class Permissions { - public static Boolean SeeInvoices { get {return resolve('SeeInvoices');} } - public static Boolean ModifyInvoices { get {return resolve('ModifyInvoices');} } + public static Boolean SeeInvoices { get {return resolve('SeeInvoices');} } + public static Boolean ModifyInvoices { get {return resolve('ModifyInvoices');} } - private static Boolean resolve(String customPermission) { - return FeatureManagement.checkPermission(customPermission); - } + private static Boolean resolve(String customPermission) { + return FeatureManagement.checkPermission(customPermission); + } } ``` ```apex if (Permissions.SeeInvoices) { - //.. + //.. } ``` @@ -114,12 +114,12 @@ Cache can be configured to store values in Platform Cache (Organization/Session) ```apex public with sharing class Profiles { - public static Profile SystemAdministrator { get {return resolve('System Administrator');} } - public static Profile StandardUser { get {return resolve('Standard User');} } + public static Profile SystemAdministrator { get {return resolve('System Administrator');} } + public static Profile StandardUser { get {return resolve('Standard User');} } - private static Profile resolve(String name) { - return (Profile) Query.Profiles.byName(name).getFirstOrNull(); - } + private static Profile resolve(String name) { + return (Profile) Query.Profiles.byName(name).getFirstOrNull(); + } } ``` \ No newline at end of file diff --git a/docs/TestDataBuilder.md b/docs/TestDataBuilder.md index 88bde21..521a52e 100644 --- a/docs/TestDataBuilder.md +++ b/docs/TestDataBuilder.md @@ -21,21 +21,21 @@ inserts. It can also create sets of similar records, where the following records ```apex @TestSetup static void testSetup() { - TestDataBuilder builder = new TestDataBuilder(); + TestDataBuilder builder = new TestDataBuilder(); - Account[] accounts = builder - // Creates Account with default fields + all fields defined here - .create(new Account(Name = 'Test', BillingCountry = 'US')) + Account[] accounts = builder + // Creates Account with default fields + all fields defined here + .create(new Account(Name = 'Test', BillingCountry = 'US')) - // Creates an Account with ALL fields of previous record, but with different BillingCity - .similarly(new Account(BillingCity = 'Austin')) + // Creates an Account with ALL fields of previous record, but with different BillingCity + .similarly(new Account(BillingCity = 'Austin')) - // Creates 5 more accounts with default values - .create(5, new Account(Name = 'Other Accounts')) + // Creates 5 more accounts with default values + .create(5, new Account(Name = 'Other Accounts')) - // Create account with different variant - different set of default fields - .create('SMB', new Account(Name = 'Business Account')) - .insertRecords(); + // Create account with different variant - different set of default fields + .create('SMB', new Account(Name = 'Business Account')) + .insertRecords(); } ``` @@ -68,10 +68,10 @@ Consider the following example: ```apex User[] users = new TestDataBuilder() - .create(new User(Username = 'johnson@test.com', ProfileId = salesManager.Id, UserRoleId = salesManager.Id)) - .similarly(new User(Username = 'xian@test.com')) - .similarly(new User(Username = 'dong@test.com', UserRoleId = ceo.Id)) - .insertRecords()(); + .create(new User(Username = 'johnson@test.com', ProfileId = salesManager.Id, UserRoleId = salesManager.Id)) + .similarly(new User(Username = 'xian@test.com')) + .similarly(new User(Username = 'dong@test.com', UserRoleId = ceo.Id)) + .insertRecords()(); ``` When `similarly` is called, it takes previous record as a base and applies new fields on top of it. So here we have: @@ -89,33 +89,33 @@ Each has a completely different layout and set of required fields. This is when we can use flavors in TestDataDefaults: ```apex - private final static Map> sObjectFactoriesMap = new Map>{ - - Account.SObjectType => new Map{ - defaults => new SimpleDefaultsFactory(new Account( - Name = 'Test Account' - )), - - 'PersonAccount' => new SimpleDefaultsFactory(new Account( - FirstName = 'John', - LastName = 'Doe', - RecordTypeId = RecordTypes.ACCOUNT_PERSON.id - /*other Person Account fields*/ - )), - - //Small Medium Business - 'SMB' => new SimpleDefaultsFactory(new Account( - Name = 'Test Account', - RecordTypeId = RecordTypes.ACCOUNT_SMB.id - /*other SMB fields*/ - )), - - 'Enterprise' => new SimpleDefaultsFactory(new Account( - Name = 'Test Account', - RecordTypeId = RecordTypes.ACCOUNT_ENTERPRISE.id - /*other enterprise fields*/ - )) - } + private final static Map> sObjectFactoriesMap = new Map>{ + + Account.SObjectType => new Map{ + defaults => new SimpleDefaultsFactory(new Account( + Name = 'Test Account' + )), + + 'PersonAccount' => new SimpleDefaultsFactory(new Account( + FirstName = 'John', + LastName = 'Doe', + RecordTypeId = RecordTypes.ACCOUNT_PERSON.id + /*other Person Account fields*/ + )), + + //Small Medium Business + 'SMB' => new SimpleDefaultsFactory(new Account( + Name = 'Test Account', + RecordTypeId = RecordTypes.ACCOUNT_SMB.id + /*other SMB fields*/ + )), + + 'Enterprise' => new SimpleDefaultsFactory(new Account( + Name = 'Test Account', + RecordTypeId = RecordTypes.ACCOUNT_ENTERPRISE.id + /*other enterprise fields*/ + )) + } }; @@ -125,10 +125,10 @@ We can create different sets of base fields and then specify that flavor in test ```apex Account[] accounts = new TestDataBuilder() - .create('Person Account', new Account(/*...*/)) - .create(2, 'SMB', new Account(/*...*/)) - .create(2, 'Enterprise', new Account(/*...*/)) - .insertRecords(); + .create('Person Account', new Account(/*...*/)) + .create(2, 'SMB', new Account(/*...*/)) + .create(2, 'Enterprise', new Account(/*...*/)) + .insertRecords(); ``` Each Account will come with default fields specific for its flavor. If flavor is not specified, `defaults` flavor is used. @@ -139,7 +139,7 @@ It's possible to use DML mocks from DatabaseService instead of actual record ins ```apex TestDataBuilder builder = new TestDataBuilder() - .mockDMLs(); + .mockDMLs(); Account[] accounts = builder.insertRecords(2, new Account()); ``` @@ -157,10 +157,10 @@ Account will have fake id `001000000000000`. ```apex SObject[] records = new TestDataBuilder() - .create(new Account(/*...*/)) - .create(new Contact(/*...*/)) - .create(new Case(/*...*/)) - .getRecords(); + .create(new Account(/*...*/)) + .create(new Contact(/*...*/)) + .create(new Case(/*...*/)) + .getRecords(); // => records has 3 different sobjects ``` @@ -194,61 +194,61 @@ For simple defaults, we can use inbuilt `SimpleDefaultsFactory` which only appli ```apex public with sharing class TestDataDefaults { - public final static String defaults = ''; - - private final static Map> sObjectFactoriesMap = new Map>{ - Account.SObjectType => new Map{ - defaults => new SimpleDefaultsFactory(new Account( - Name = 'Test Account' - )), - - 'PersonAccount' => new SimpleDefaultsFactory(new Account( - FirstName = 'John', - LastName = 'Doe' - )) - }, - - Contact.SObjectType => new Map{ - defaults => new SimpleDefaultsFactory(new Contact( - FirstName = 'Testy', - LastName = 'Jones' - )) - }, - - Opportunity.SObjectType => new Map{ - defaults => new SimpleDefaultsFactory(new Opportunity( - Name = 'Test Opportunity' - )) - }, - - User.SObjectType => new Map{ - defaults => new DefaultUserFactory() - } - }; - - private class DefaultUserFactory implements SObjectFactory { - private Integer counter = 0; - private Id orgId = UserInfo.getOrganizationId(); - - public SObject create() { - counter++; - String uid = '' + counter + Crypto.getRandomInteger(); - - return new User( - FirstName = 'Test', - LastName = 'User', - Email = 'test@example.com', - Username = uid + '@' + orgId + '.test.com', - ProfileId = UserInfo.getProfileId(), - Alias = uid.left(8), - CommunityNickname = uid.left(40), - TimeZoneSidKey = 'GMT', - LocaleSidKey = 'en_US', - EmailEncodingKey = 'UTF-8', - LanguageLocaleKey = 'en_US' - ); - } - } + public final static String defaults = ''; + + private final static Map> sObjectFactoriesMap = new Map>{ + Account.SObjectType => new Map{ + defaults => new SimpleDefaultsFactory(new Account( + Name = 'Test Account' + )), + + 'PersonAccount' => new SimpleDefaultsFactory(new Account( + FirstName = 'John', + LastName = 'Doe' + )) + }, + + Contact.SObjectType => new Map{ + defaults => new SimpleDefaultsFactory(new Contact( + FirstName = 'Testy', + LastName = 'Jones' + )) + }, + + Opportunity.SObjectType => new Map{ + defaults => new SimpleDefaultsFactory(new Opportunity( + Name = 'Test Opportunity' + )) + }, + + User.SObjectType => new Map{ + defaults => new DefaultUserFactory() + } + }; + + private class DefaultUserFactory implements SObjectFactory { + private Integer counter = 0; + private Id orgId = UserInfo.getOrganizationId(); + + public SObject create() { + counter++; + String uid = '' + counter + Crypto.getRandomInteger(); + + return new User( + FirstName = 'Test', + LastName = 'User', + Email = 'test@example.com', + Username = uid + '@' + orgId + '.test.com', + ProfileId = UserInfo.getProfileId(), + Alias = uid.left(8), + CommunityNickname = uid.left(40), + TimeZoneSidKey = 'GMT', + LocaleSidKey = 'en_US', + EmailEncodingKey = 'UTF-8', + LanguageLocaleKey = 'en_US' + ); + } + } } ``` @@ -262,11 +262,11 @@ Test Data Factory class is a place where you can do that: @IsTest private class SomeTest { - @TestSetup - static void testSetup() { - User testUser = TestDataFactory.createUser(Profiles.SYSTEM_ADMINISTRATOR.Id, 'test@company.com'); + @TestSetup + static void testSetup() { + User testUser = TestDataFactory.createUser(Profiles.SYSTEM_ADMINISTRATOR.Id, 'test@company.com'); - Account account = TestDataFactory.createAccount('Test'); - } + Account account = TestDataFactory.createAccount('Test'); + } } ``` \ No newline at end of file diff --git a/docs/TestDataSuite.md b/docs/TestDataSuite.md index 3274d40..a122770 100644 --- a/docs/TestDataSuite.md +++ b/docs/TestDataSuite.md @@ -25,76 +25,76 @@ Our test data will be grouped into two `TestDataSuites`: ```apex private class MyClassTest { - @TestSetup - static void testSetup() { - TestDataBuilder dataBuilder = new TestDataBuilder(); - - List accounts = dataBuilder - .create(new Account(Name = '1')) - .create(new Account(Name = '2')) - .insertRecords(); - - Case[] cases = (Case[]) dataBuilder - .create(new Case(Subject = 'Case 1', AccountId = accounts[0].Id)) - .similarly(new Case(Subject = 'Case 2')) - .similarly(new Case(Subject = 'Case 3')) - .insertRecords(); - - Contact[] contacts = (Contact[]) dataBuilder - .create(new Contact(FirstName = 'Joe', LastName = 'Doe1', AccountId = accounts[0].Id)) - .similarly(new Contact(LastName = 'Doe2')) - .similarly(new Contact(LastName = 'Doe3')) - .similarly(new Contact(LastName = 'Doe4')) - .insertRecords(); - - - TestDataSuite suite1 = TestDataSuiteManager.registerSuite('Account with related'); - suite1.registerRecord('Account', accounts[0]); - suite1.registerRecords(Case.Subject, cases); - suite1.registerRecordsUnderGroup('Contacts to process', new List{contacts[0], contacts[1]}); - suite1.registerRecordsUnderGroup('Contacts to remove', new List{contacts[2], contacts[3]}); - - TestDataSuite suite2 = TestDataSuiteManager.registerSuite('Account without related'); - suite2.registerRecord('Account', accounts[1]); - - TestDataSuiteManager.saveSuites(); - } + @TestSetup + static void testSetup() { + TestDataBuilder dataBuilder = new TestDataBuilder(); + + List accounts = dataBuilder + .create(new Account(Name = '1')) + .create(new Account(Name = '2')) + .insertRecords(); + + Case[] cases = (Case[]) dataBuilder + .create(new Case(Subject = 'Case 1', AccountId = accounts[0].Id)) + .similarly(new Case(Subject = 'Case 2')) + .similarly(new Case(Subject = 'Case 3')) + .insertRecords(); + + Contact[] contacts = (Contact[]) dataBuilder + .create(new Contact(FirstName = 'Joe', LastName = 'Doe1', AccountId = accounts[0].Id)) + .similarly(new Contact(LastName = 'Doe2')) + .similarly(new Contact(LastName = 'Doe3')) + .similarly(new Contact(LastName = 'Doe4')) + .insertRecords(); + + + TestDataSuite suite1 = TestDataSuiteManager.registerSuite('Account with related'); + suite1.registerRecord('Account', accounts[0]); + suite1.registerRecords(Case.Subject, cases); + suite1.registerRecordsUnderGroup('Contacts to process', new List{contacts[0], contacts[1]}); + suite1.registerRecordsUnderGroup('Contacts to remove', new List{contacts[2], contacts[3]}); + + TestDataSuite suite2 = TestDataSuiteManager.registerSuite('Account without related'); + suite2.registerRecord('Account', accounts[1]); + + TestDataSuiteManager.saveSuites(); + } } ``` This code internally produces a map of sObjects which looks like this: ```js { - /*Test Data Suite 1*/ - "Account with related": { - "sobjectByUniqueName": { - //from registerRecord('Account', accounts[0]) - "Account": account1, - //from registerRecords(Case.Subject, cases) - "Case 1": case1, - "Case 2": case2, - "Case 3": case3 - }, - "sobjectBySObjectType": { - "Account": [account1], - "Case": [case1, case2, case3], - "Contact": [contact1, contact2, contact3, contac4] - }, - "sobjectByGroup": { - //from registerRecordsUnderGroup('Contacts to process', ...); - "Contacts to process": [contact1, contact2], - "Contacts to remove": [contact3, contact4] - } - }, - /*Test Data Suite 2*/ - "Account without related": { - "sobjectByUniqueName": { - "Account": account2 - }, - "sobjectBySObjectType": { - "Account": [account2] - } - } + /*Test Data Suite 1*/ + "Account with related": { + "sobjectByUniqueName": { + //from registerRecord('Account', accounts[0]) + "Account": account1, + //from registerRecords(Case.Subject, cases) + "Case 1": case1, + "Case 2": case2, + "Case 3": case3 + }, + "sobjectBySObjectType": { + "Account": [account1], + "Case": [case1, case2, case3], + "Contact": [contact1, contact2, contact3, contac4] + }, + "sobjectByGroup": { + //from registerRecordsUnderGroup('Contacts to process', ...); + "Contacts to process": [contact1, contact2], + "Contacts to remove": [contact3, contact4] + } + }, + /*Test Data Suite 2*/ + "Account without related": { + "sobjectByUniqueName": { + "Account": account2 + }, + "sobjectBySObjectType": { + "Account": [account2] + } + } } ``` @@ -103,17 +103,17 @@ Framework uses one query to retrieve all the suites. ```apex @IsTest static void myTestMethod() { - TestDataSuite suite = TestDataSuiteManager.getSuite('Account with related'); + TestDataSuite suite = TestDataSuiteManager.getSuite('Account with related'); - //Get record by unique name - Account acc = (Account) suite.get('Account'); - Case case1 = (Case) suite.get('Case 1'); + //Get record by unique name + Account acc = (Account) suite.get('Account'); + Case case1 = (Case) suite.get('Case 1'); - //Get All Cases in Suite - List cases = suite.get(Case.SObjectType); + //Get All Cases in Suite + List cases = suite.get(Case.SObjectType); - //Get one group of records - List contacts = suite.getGroup('Contacts to remove'); + //Get one group of records + List contacts = suite.getGroup('Contacts to remove'); } ``` @@ -130,7 +130,7 @@ but let's us reference any number of records in unit tests without any additiona Creates, retrieves and persists TestDataSuites.
- registerSuite(String uniqueName) + registerSuite(String uniqueName) ```apex public static TestDataSuite registerSuite(String uniqueName); @@ -148,13 +148,13 @@ This method should be called in @TestSetup to create suite of data. ```apex @TestSetup static void testSetup() { - TestDataSuite suite1 = TestDataSuiteManager.registerSuite('Account with related'); + TestDataSuite suite1 = TestDataSuiteManager.registerSuite('Account with related'); } ```
- getSuite(String uniqueName) + getSuite(String uniqueName) ```apex public static TestDataSuite getSuite(String uniqueName); @@ -172,13 +172,13 @@ Retrieves previously created Data Suite by its unique name. This method should b ```apex @IsTest static void testMethodName() { - TestDataSuite suite = TestDataSuiteManager.getSuite('Account with related'); + TestDataSuite suite = TestDataSuiteManager.getSuite('Account with related'); } ```
- saveSuites() + saveSuites() ```apex public static void saveSuites(); @@ -190,9 +190,9 @@ It should be called at the end of @TestSetup when all test data is already inser ```apex @TestSetup static void testSetup() { - TestDataSuite suite = TestDataSuiteManager.registerSuite('Account with related'); - //... - TestDataSuiteManager.saveSuites(); + TestDataSuite suite = TestDataSuiteManager.registerSuite('Account with related'); + //... + TestDataSuiteManager.saveSuites(); } ```
@@ -201,7 +201,7 @@ static void testSetup() { ## TestDataSuite
- registerRecords(SObjectField field, SObject[] records) + registerRecords(SObjectField field, SObject[] records) ```apex public void registerRecords(SObjectField field, SObject[] records); @@ -218,14 +218,14 @@ Record can be later retrieved in unit test using get() method. ```apex @TestSetup static void testSetup() { - //... - suite.registerRecords(User.Username, users); + //... + suite.registerRecords(User.Username, users); } ```
- registerRecord(String uniqueName, SObject record) + registerRecord(String uniqueName, SObject record) ```apex public void registerRecord(String uniqueName, SObject record); @@ -242,15 +242,15 @@ Record can be later retrieved in unit test using get() method. ```apex @TestSetup static void testSetup() { - //... - suite.registerRecord('My User', user); + //... + suite.registerRecord('My User', user); } ```
- registerRecordsUnderGroup(String groupName, SObject[] records) + registerRecordsUnderGroup(String groupName, SObject[] records) ```apex public void registerRecordsUnderGroup(String groupName, SObject[] records); @@ -266,14 +266,14 @@ Record can be later retrieved in unit test using getRecords() method. ```apex @TestSetup static void testSetup() { - //... - suite.registerRecordsUnderGroup('Contacts to process', contacts); + //... + suite.registerRecordsUnderGroup('Contacts to process', contacts); } ```
- get(String uniqueName) + get(String uniqueName) ```apex public SObject get(String uniqueName); @@ -287,15 +287,15 @@ Returns Record registered in the test data suite under give unique name. ```apex @IsTest static void myTestMethod() { - TestDataSuite suite = TestDataSuiteManager.getSuite('Account with related'); - User user = (User) suite.get('My User'); + TestDataSuite suite = TestDataSuiteManager.getSuite('Account with related'); + User user = (User) suite.get('My User'); } ```
- get(SObjectType type) + get(SObjectType type) ```apex public List get(SObjectType type); @@ -309,15 +309,15 @@ Returns all records of given SObjectType in suite. ```apex @IsTest static void myTestMethod() { - TestDataSuite suite = TestDataSuiteManager.getSuite('Account with related'); - List cases = suite.get(Case.SObjectType); + TestDataSuite suite = TestDataSuiteManager.getSuite('Account with related'); + List cases = suite.get(Case.SObjectType); } ```
- getGroup(String groupName) + getGroup(String groupName) ```apex public List getGroup(String groupName); @@ -332,8 +332,8 @@ Respects order in which records were added to the group. ```apex @IsTest static void myTestMethod() { - TestDataSuite suite = TestDataSuiteManager.getSuite('Account with related'); - List contacts = suite.getGroup('Contacts to remove'); + TestDataSuite suite = TestDataSuiteManager.getSuite('Account with related'); + List contacts = suite.getGroup('Contacts to remove'); } ```
\ No newline at end of file diff --git a/docs/TriggerHandler.md b/docs/TriggerHandler.md index fe97728..1d2000d 100644 --- a/docs/TriggerHandler.md +++ b/docs/TriggerHandler.md @@ -32,7 +32,7 @@ and another that is visible only in unit tests and provides an ability to mock T Trigger should contain only one line of code which executes trigger handler: ```apex trigger AccountTrigger on Account (before insert, after insert, before update, after update, before delete, after delete, after undelete) { - TriggerDispatcher.run(new AccountTriggerHandler()); + TriggerDispatcher.run(new AccountTriggerHandler()); } ``` @@ -59,25 +59,25 @@ public virtual void afterUndelete(List triggerNew, TriggerContext tc); ```
- Example + Example ```apex | Example implementation public inherited sharing class AccountTriggerHandler extends TriggerHandler { - public override void afterInsert(List triggerNew, TriggerContext tc) { - Accounts accounts = new Accounts(triggerNew); - accounts.linkToStore(tc); - accounts.preventDuplicateAccounts(tc); - accounts.updatePersonContact(tc); - accounts.createAccountShares(tc); - } - - public override void afterUpdate(List triggerNew, TriggerContext tc) { - Accounts accounts = new Accounts(triggerNew); - accounts.linkToStore(tc); - accounts.syncChangesWithCustomerService(); - accounts.createCustomerCareNotes(); - } + public override void afterInsert(List triggerNew, TriggerContext tc) { + Accounts accounts = new Accounts(triggerNew); + accounts.linkToStore(tc); + accounts.preventDuplicateAccounts(tc); + accounts.updatePersonContact(tc); + accounts.createAccountShares(tc); + } + + public override void afterUpdate(List triggerNew, TriggerContext tc) { + Accounts accounts = new Accounts(triggerNew); + accounts.linkToStore(tc); + accounts.syncChangesWithCustomerService(); + accounts.createCustomerCareNotes(); + } } ```
@@ -91,7 +91,7 @@ This class serves the following purposes: 1. It contains methods that make record filtering easier and more verbose:
- Methods + Methods ```apex SObject[] getRecords(); // returns Trigger.old in DELETE triggers and Trigger.new in all other cases @@ -145,18 +145,18 @@ Integer getExecutionCount(String featureName, Id recordId); ##### Process records once To make sure that record is not needlessly processed number of times, a developer can use `isFirstRun()` method as follows: ```apex - public class AccountAddressPopulator { - public void populateDefaultAddress(List records, TriggerContext ctx) { - String thisFeature = AccountAddressPopulator.class.getName(); - - for (Account acc : (Account[]) records) { - if (ctx.isFirstRun(thisFeature, acc.Id)) { - // Increment to test trigger recursion - acc.NumberOfEmployees = acc.NumberOfEmployees == null ? 1 : acc.NumberOfEmployees + 1; - ctx.setExecuted(thisFeature, acc.Id); - } - } - } + public class AccountAddressPopulator { + public void populateDefaultAddress(List records, TriggerContext ctx) { + String thisFeature = AccountAddressPopulator.class.getName(); + + for (Account acc : (Account[]) records) { + if (ctx.isFirstRun(thisFeature, acc.Id)) { + // Increment to test trigger recursion + acc.NumberOfEmployees = acc.NumberOfEmployees == null ? 1 : acc.NumberOfEmployees + 1; + ctx.setExecuted(thisFeature, acc.Id); + } + } + } } ``` Using `isFirstRun()`/`getExecutionCount()` and setExecuted() lets us control how many times this logic will be executed. diff --git a/docs/TriggerHandlerMdt.md b/docs/TriggerHandlerMdt.md index 3c9fc1c..b006088 100644 --- a/docs/TriggerHandlerMdt.md +++ b/docs/TriggerHandlerMdt.md @@ -27,8 +27,8 @@ Framework initializes and parametrizes each of the defined classes and executes Apex Classes defined in the custom metadata must implement TriggerLogic interface: ```apex public interface TriggerLogic { - void setParameters(String parameters); - void execute(List records, TriggerContext ctx); + void setParameters(String parameters); + void execute(List records, TriggerContext ctx); } ``` @@ -36,22 +36,22 @@ Example of a class that copies BillingCountry to ShippingCountry when it's blank ```apex public class AccountShippingCountryFiller implements TriggerLogic { - public void execute(List records, TriggerContext ctx) { - for (Account acc : records) { - if (ctx.isChanged(acc, Account.BillingCountry) && String.isBlank(acc.ShippingCountry)) { - acc.ShippingCountry = acc.BillingCountry; - } - } - } + public void execute(List records, TriggerContext ctx) { + for (Account acc : records) { + if (ctx.isChanged(acc, Account.BillingCountry) && String.isBlank(acc.ShippingCountry)) { + acc.ShippingCountry = acc.BillingCountry; + } + } + } - public void setParameters(String parameters) {} + public void setParameters(String parameters) {} } ``` To run Metadata Trigger Handler, define your trigger as follows: ```apex trigger AccountTrigger on Account (before insert, before update, before delete, after insert, after update, after delete, after undelete) { - TriggerDispatcher.run(new MetadataTriggerHandler()); + TriggerDispatcher.run(new MetadataTriggerHandler()); } ``` diff --git a/docs/XmlParser.md b/docs/XmlParser.md index c8a3c81..5291989 100644 --- a/docs/XmlParser.md +++ b/docs/XmlParser.md @@ -24,83 +24,83 @@ Profile p = (Profile) xmlParser.getAs(Profile.class, false); Given Profile XML:
- Profile XML + Profile XML ```xml - AccountSelector - true + AccountSelector + true - AccountTriggerHandler - true + AccountTriggerHandler + true false - false - Log__c.ApexClass__c - false + false + Log__c.ApexClass__c + false - false - Log__c.LoggingLevel__c - false + false + Log__c.LoggingLevel__c + false - false - Log__c.Message__c - false + false + Log__c.Message__c + false - Account-Account Layout + Account-Account Layout - LogRetention__mdt-Logging Setting Layout + LogRetention__mdt-Logging Setting Layout - true - true - true - true - true - Log__c - true + true + true + true + true + true + Log__c + true - true - true - true - true - true - LoggingEvent__e - true + true + true + true + true + true + LoggingEvent__e + true - TestPage - true + TestPage + true - Log__c - DefaultOn + Log__c + DefaultOn - Test - DefaultOn + Test + DefaultOn Salesforce - true - ActivateContract + true + ActivateContract - true - ActivateOrder + true + ActivateOrder - true - ActivitiesAccess + true + ActivitiesAccess ``` @@ -111,94 +111,94 @@ Output for `Map result = new XmlParser(PROFILE_XML).getUntyped(); ```json { "Profile": { - "userPermissions": [ - { - "name": "ActivateContract", - "enabled": true - }, - { - "name": "ActivateOrder", - "enabled": true - }, - { - "name": "ActivitiesAccess", - "enabled": true - } - ], - "userLicense": "Salesforce", - "tabVisibilities": [ - { - "visibility": "DefaultOn", - "tab": "Log__c" - }, - { - "visibility": "DefaultOn", - "tab": "Test" - } - ], - "pageAccesses": [ - { - "enabled": true, - "apexPage": "TestPage" - } - ], - "objectPermissions": [ - { - "viewAllRecords": true, - "object": "Log__c", - "modifyAllRecords": true, - "allowRead": true, - "allowEdit": true, - "allowDelete": true, - "allowCreate": true - }, - { - "viewAllRecords": true, - "object": "LoggingEvent__e", - "modifyAllRecords": true, - "allowRead": true, - "allowEdit": true, - "allowDelete": true, - "allowCreate": true - } - ], - "layoutAssignments": [ - { - "layout": "Account-Account Layout" - }, - { - "layout": "LogRetention__mdt-Logging Setting Layout" - } - ], - "fieldPermissions": [ - { - "readable": false, - "field": "Log__c.ApexClass__c", - "editable": false - }, - { - "readable": false, - "field": "Log__c.LoggingLevel__c", - "editable": false - }, - { - "readable": false, - "field": "Log__c.Message__c", - "editable": false - } - ], - "custom": false, - "classAccesses": [ - { - "enabled": true, - "apexClass": "AccountSelector", - "@someAttribute": "Test" - }, - { - "enabled": true, - "apexClass": "AccountTriggerHandler" - } - ] + "userPermissions": [ + { + "name": "ActivateContract", + "enabled": true + }, + { + "name": "ActivateOrder", + "enabled": true + }, + { + "name": "ActivitiesAccess", + "enabled": true + } + ], + "userLicense": "Salesforce", + "tabVisibilities": [ + { + "visibility": "DefaultOn", + "tab": "Log__c" + }, + { + "visibility": "DefaultOn", + "tab": "Test" + } + ], + "pageAccesses": [ + { + "enabled": true, + "apexPage": "TestPage" + } + ], + "objectPermissions": [ + { + "viewAllRecords": true, + "object": "Log__c", + "modifyAllRecords": true, + "allowRead": true, + "allowEdit": true, + "allowDelete": true, + "allowCreate": true + }, + { + "viewAllRecords": true, + "object": "LoggingEvent__e", + "modifyAllRecords": true, + "allowRead": true, + "allowEdit": true, + "allowDelete": true, + "allowCreate": true + } + ], + "layoutAssignments": [ + { + "layout": "Account-Account Layout" + }, + { + "layout": "LogRetention__mdt-Logging Setting Layout" + } + ], + "fieldPermissions": [ + { + "readable": false, + "field": "Log__c.ApexClass__c", + "editable": false + }, + { + "readable": false, + "field": "Log__c.LoggingLevel__c", + "editable": false + }, + { + "readable": false, + "field": "Log__c.Message__c", + "editable": false + } + ], + "custom": false, + "classAccesses": [ + { + "enabled": true, + "apexClass": "AccountSelector", + "@someAttribute": "Test" + }, + { + "enabled": true, + "apexClass": "AccountTriggerHandler" + } + ] } } ``` @@ -210,7 +210,7 @@ Output for `Map result = new XmlParser(PROFILE_XML).getUntyped(); ### XmlParser
- setAttributePrefix(String prefix) + setAttributePrefix(String prefix) ```apex public void setAttributePrefix(String prefix); @@ -225,8 +225,8 @@ Respects order in which records were added to the group. ```xml - Anna - Smith + Anna + Smith ``` ```apex @@ -237,17 +237,17 @@ parser.getUntypedMap(); Result: ```json { - "person": { - "@gender": "female", - "firstname": "Anna", - "lastname": "Smith" - } + "person": { + "@gender": "female", + "firstname": "Anna", + "lastname": "Smith" + } } ```
- getUntyped() + getUntyped() ```apex public Map getUntyped(); @@ -262,17 +262,17 @@ Map result = parser.getUntypedMap(); Result: ```json { - "person": { - "@gender": "female", - "firstname": "Anna", - "lastname": "Smith" - } + "person": { + "@gender": "female", + "firstname": "Anna", + "lastname": "Smith" + } } ```
- getAs(Type apexType, Boolean withEnvelope) + getAs(Type apexType, Boolean withEnvelope) ```apex public Object getAs(Type apexType, Boolean withEnvelope); @@ -284,11 +284,11 @@ Parses XML into a concrete apex type. - `Boolean withEnvelope` If true, only xml's root element is deserialized into type instead of envelope ```text { // < with envelope > - "person": { // < without envelope > - "@gender": "female", - "firstname": "Anna", - "lastname": "Smith" - } + "person": { // < without envelope > + "@gender": "female", + "firstname": "Anna", + "lastname": "Smith" + } } ``` diff --git a/docs/asset-manifest.json b/docs/asset-manifest.json index 2615cae..3e41660 100644 --- a/docs/asset-manifest.json +++ b/docs/asset-manifest.json @@ -1,11 +1,11 @@ { "files": { - "main.css": "/static/css/main.2b1c1c72.css", - "main.js": "/static/js/main.f6e8715a.js", + "main.css": "/static/css/main.78f03de6.css", + "main.js": "/static/js/main.3658b9f6.js", "index.html": "/index.html" }, "entrypoints": [ - "static/css/main.2b1c1c72.css", - "static/js/main.f6e8715a.js" + "static/css/main.78f03de6.css", + "static/js/main.3658b9f6.js" ] } \ No newline at end of file diff --git a/docs/commons.md b/docs/commons.md index dfc5cd2..d618c19 100644 --- a/docs/commons.md +++ b/docs/commons.md @@ -16,7 +16,7 @@ Then each file in the same folder can export a class with static methods: ```javascript export class DateUtils { - static formatDate() {/*...*/} + static formatDate() {/*...*/} } ``` @@ -31,14 +31,14 @@ import {Toasts} from "c/commons"; export default class Mytab extends LightningElement { - handleClick(ev) { - try { - doSomething(); - Toasts.showSuccessToast(this, "Record Updated!"); + handleClick(ev) { + try { + doSomething(); + Toasts.showSuccessToast(this, "Record Updated!"); - } catch (e) { - Toasts.showUnexpectedErrorToast(this); - } - } + } catch (e) { + Toasts.showUnexpectedErrorToast(this); + } + } } ``` \ No newline at end of file diff --git a/docs/icons/custom-sprite/svg/91.svg b/docs/icons/custom-sprite/svg/91.svg new file mode 100644 index 0000000..6fcdaf0 --- /dev/null +++ b/docs/icons/custom-sprite/svg/91.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/docs/icons/custom-sprite/svg/symbols.svg b/docs/icons/custom-sprite/svg/symbols.svg index c2ed629..0374adf 100644 --- a/docs/icons/custom-sprite/svg/symbols.svg +++ b/docs/icons/custom-sprite/svg/symbols.svg @@ -1 +1,548 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/icons/doctype-sprite/svg/symbols.svg b/docs/icons/doctype-sprite/svg/symbols.svg index dfc1148..34a7302 100644 --- a/docs/icons/doctype-sprite/svg/symbols.svg +++ b/docs/icons/doctype-sprite/svg/symbols.svg @@ -1 +1,2 @@ - \ No newline at end of file + + diff --git a/docs/index.html b/docs/index.html index 1af04c7..44fe1bb 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1 +1 @@ -Apex Libra - Library of Salesforce Accelerators, Frameworks, Utilities

Apex Commons

Salesforce Frameworks

Salesforce Accelerators

Trigger Handler

Lambda

Collection

XML

Selectors

Selector Layer

LWC

\ No newline at end of file +Apex Libra - Library of Salesforce Accelerators, Frameworks, Utilities

Apex Commons

Salesforce Frameworks

Salesforce Accelerators

Trigger Handler

Lambda

Collection

XML

Selectors

Selector Layer

LWC

\ No newline at end of file diff --git a/docs/labels.md b/docs/labels.md index 203d716..9396b9d 100644 --- a/docs/labels.md +++ b/docs/labels.md @@ -7,9 +7,9 @@ Custom Labels in LWC have a tendency to significantly obscure main component's code if there are many imported. Elegant way to handle the custom labels is to separate them to separate file: * myComponent - * myComponent.html - * myComponent.js - * myComponentLabels.js + * myComponent.html + * myComponent.js + * myComponentLabels.js **Note**: Naming it `"labels.js"` on every component will make it harder to find in IDE file search. It's better to prefix the filename with component's name. @@ -22,7 +22,7 @@ import submit from '@salesforce/label/c.Submit'; /*... other labels*/ const labels = { - success, error, submit //...other labels + success, error, submit //...other labels } export {labels}; @@ -33,7 +33,7 @@ myComponent.js import {labels} from "./myTabLabels"; export default class MyComponent extends LightningElement { - label = labels; + label = labels; } ``` Custom labels can be referenced in HTML using syntax `{label.success}` diff --git a/docs/static/css/main.2b1c1c72.css b/docs/static/css/main.78f03de6.css similarity index 80% rename from docs/static/css/main.2b1c1c72.css rename to docs/static/css/main.78f03de6.css index ddad4cc..364d4f4 100644 --- a/docs/static/css/main.2b1c1c72.css +++ b/docs/static/css/main.78f03de6.css @@ -1 +1 @@ -.fullHeight,body,html{height:calc(100vh - 40px)}.fullWidth{width:100%}.text-preformatted{white-space:pre-wrap}.site-sidebar{background:#fafaf9;max-width:245px;min-width:245px;width:245px}.max-width{max-width:50rem}.slds-tabs_default__content{font-size:16px;& h1,& h2,& h3,& h4{margin:2rem 0 6px}& table{width:auto}}h1:first-child,h2:first-child,h3:first-child,h4:first-child{margin-top:0}.button-copy{position:absolute;right:10px;top:5px;z-index:10}.slds-accordion__summary h3.slds-accordion__summary-heading{margin:0}.slds-tabs_default__content h3.slds-m-top_large:not(:first-child){margin-top:2rem}p{margin-bottom:1rem}.slds-theme_light{background-color:#fafaf9}.slds-box_darker{border:1px solid #c1c1c1}.tile:hover{background-color:#f4f6f9}.code{box-shadow:2px 2px 6px #979797;font-size:14px}.code-border{border-left:10px solid #358ccb}.slds-text-body_regular,.slds-text-title{font-size:16px}.slds-text-heading_small,.slds-text-title{font-size:18px}.slds-tabs_default__nav{justify-content:center}.centered{margin-left:auto;margin-right:auto;max-width:960px}ul.slds-accordion+ul.slds-accordion{border-top:1px solid #e5e5e5;border-top:1px solid var(--slds-g-color-border-base-1,#e5e5e5)}::-webkit-scrollbar{height:5px;width:5px}::-webkit-scrollbar-track{background:#fafaf9}::-webkit-scrollbar-thumb{background:#888}::-webkit-scrollbar-thumb:hover{background:#555}.animated{overflow:hidden;transition:all .5s ease-in-out}.animated-open{max-height:100%}.animated-closed{max-height:0;padding:0}.slds-tree_container[data-filtered=true] [role=treeitem][data-matched=false]{display:none}.slds-tree_container[data-filtered=true] [role=treeitem][data-matched=true] [role=treeitem]{display:inline;display:initial}.slds-tree_container[data-filtered=true] [role=treeitem][data-matched=false]:has([role=treeitem][data-matched=true]){display:inline;display:initial} \ No newline at end of file +.fullHeight,body,html{height:calc(100vh - 40px)}.fullWidth{width:100%}.text-preformatted{white-space:pre-wrap}.site-sidebar{background:#fafaf9;max-width:245px;min-width:245px;width:245px}.max-width{max-width:50rem}.slds-tabs_default__content{font-size:16px;& h1,& h2,& h3,& h4{margin:2rem 0 6px}& table{width:auto}}h1:first-child,h2:first-child,h3:first-child,h4:first-child{margin-top:0}.button-copy{position:absolute;right:10px;top:5px;z-index:10}.slds-accordion__summary h3.slds-accordion__summary-heading{margin:0}.slds-tabs_default__content h3.slds-m-top_large:not(:first-child){margin-top:2rem}p{margin-bottom:1rem}.slds-theme_light{background-color:#fafaf9}.slds-box_darker{border:1px solid #c1c1c1}.tile:hover{background-color:#f4f6f9}.code{box-shadow:2px 2px 6px #979797;font-size:14px}.code-border{border-left:10px solid #358ccb}.slds-text-body_regular,.slds-text-title{font-size:16px}.slds-text-heading_small,.slds-text-title{font-size:18px}.slds-tabs_default__nav{justify-content:center}.centered{margin-left:auto;margin-right:auto;max-width:960px}ul.slds-accordion+ul.slds-accordion{border-top:1px solid #e5e5e5;border-top:1px solid var(--slds-g-color-border-base-1,#e5e5e5)}::-webkit-scrollbar{height:5px;width:5px}::-webkit-scrollbar-track{background:#fafaf9}::-webkit-scrollbar-thumb{background:#888}::-webkit-scrollbar-thumb:hover{background:#555}.animated{overflow:hidden;transition:all .5s ease-in-out}.animated-open{max-height:100%}.animated-closed{max-height:0;padding:0}.slds-tree_container[data-filtered=true] [role=treeitem][data-matched=false]{display:none}.slds-tree_container[data-filtered=true] [role=treeitem][data-matched=true] [role=treeitem]{display:inline;display:initial}.slds-tree_container[data-filtered=true] [role=treeitem][data-matched=false]:has([role=treeitem][data-matched=true]){display:inline;display:initial}.max-width-100{max-width:100%}.c-p-around_small{padding:.75rem}@media (max-width:600px){.c-p-around_small{padding:0}.slds-context-bar{padding-left:.5rem}.slds-context-bar__label-action.slds-context-bar__app-name{display:none}span.linenumber.react-syntax-highlighter-line-number{display:none!important}code.language-apex{tab-size:2!important}.slds-tabs_default__nav{flex-wrap:wrap}} \ No newline at end of file diff --git a/docs/static/js/main.f6e8715a.js b/docs/static/js/main.3658b9f6.js similarity index 82% rename from docs/static/js/main.f6e8715a.js rename to docs/static/js/main.3658b9f6.js index 6c70355..469048d 100644 --- a/docs/static/js/main.f6e8715a.js +++ b/docs/static/js/main.3658b9f6.js @@ -1,2 +1,2 @@ -/*! For license information please see main.f6e8715a.js.LICENSE.txt */ -!function(){var e={3512:function(e){e.exports={"global-setup":"#2A739E","service-cloud":"#7f2443","industry-cloud":"#4c2248","sales-cloud":"#00857d","commerce-cloud":"#41693d","community-cloud":"#ffc20e","marketing-cloud":"#ea7600",quip:"#cf451d"}},6185:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.bodyOpenClassName=t.portalClassName=void 0;var r=Object.assign||function(e){for(var t=1;t0&&0===(y-=1)&&c.show(t),n.props.shouldFocusAfterRender&&(n.props.shouldReturnFocusAfterClose?(l.returnFocus(n.props.preventScroll),l.teardownScopedFocus()):l.popWithoutFocus()),n.props.onAfterClose&&n.props.onAfterClose(),m.default.deregister(n)},n.open=function(){n.beforeOpen(),n.state.afterOpen&&n.state.beforeClose?(clearTimeout(n.closeTimer),n.setState({beforeClose:!1})):(n.props.shouldFocusAfterRender&&(l.setupScopedFocus(n.node),l.markForFocusLater()),n.setState({isOpen:!0},(function(){n.openAnimationFrame=requestAnimationFrame((function(){n.setState({afterOpen:!0}),n.props.isOpen&&n.props.onAfterOpen&&n.props.onAfterOpen({overlayEl:n.overlay,contentEl:n.content})}))})))},n.close=function(){n.props.closeTimeoutMS>0?n.closeWithTimeout():n.closeWithoutTimeout()},n.focusContent=function(){return n.content&&!n.contentHasFocus()&&n.content.focus({preventScroll:!0})},n.closeWithTimeout=function(){var e=Date.now()+n.props.closeTimeoutMS;n.setState({beforeClose:!0,closesAt:e},(function(){n.closeTimer=setTimeout(n.closeWithoutTimeout,n.state.closesAt-Date.now())}))},n.closeWithoutTimeout=function(){n.setState({beforeClose:!1,isOpen:!1,afterOpen:!1,closesAt:null},n.afterClose)},n.handleKeyDown=function(e){9===e.keyCode&&(0,u.default)(n.content,e),n.props.shouldCloseOnEsc&&27===e.keyCode&&(e.stopPropagation(),n.requestClose(e))},n.handleOverlayOnClick=function(e){null===n.shouldClose&&(n.shouldClose=!0),n.shouldClose&&n.props.shouldCloseOnOverlayClick&&(n.ownerHandlesClose()?n.requestClose(e):n.focusContent()),n.shouldClose=null},n.handleContentOnMouseUp=function(){n.shouldClose=!1},n.handleOverlayOnMouseDown=function(e){n.props.shouldCloseOnOverlayClick||e.target!=n.overlay||e.preventDefault()},n.handleContentOnClick=function(){n.shouldClose=!1},n.handleContentOnMouseDown=function(){n.shouldClose=!1},n.requestClose=function(e){return n.ownerHandlesClose()&&n.props.onRequestClose(e)},n.ownerHandlesClose=function(){return n.props.onRequestClose},n.shouldBeClosed=function(){return!n.state.isOpen&&!n.state.beforeClose},n.contentHasFocus=function(){return document.activeElement===n.content||n.content.contains(document.activeElement)},n.buildClassName=function(e,t){var r="object"===("undefined"===typeof t?"undefined":a(t))?t:{base:b[e],afterOpen:b[e]+"--after-open",beforeClose:b[e]+"--before-close"},o=r.base;return n.state.afterOpen&&(o=o+" "+r.afterOpen),n.state.beforeClose&&(o=o+" "+r.beforeClose),"string"===typeof t&&t?o+" "+t:o},n.attributesFromObject=function(e,t){return Object.keys(t).reduce((function(n,r){return n[e+"-"+r]=t[r],n}),{})},n.state={afterOpen:!1,beforeClose:!1},n.shouldClose=null,n.moveFromContentToOverlay=null,n}return function(e,t){if("function"!==typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),o(t,[{key:"componentDidMount",value:function(){this.props.isOpen&&this.open()}},{key:"componentDidUpdate",value:function(e,t){this.props.isOpen&&!e.isOpen?this.open():!this.props.isOpen&&e.isOpen&&this.close(),this.props.shouldFocusAfterRender&&this.state.isOpen&&!t.isOpen&&this.focusContent()}},{key:"componentWillUnmount",value:function(){this.state.isOpen&&this.afterClose(),clearTimeout(this.closeTimer),cancelAnimationFrame(this.openAnimationFrame)}},{key:"beforeOpen",value:function(){var e=this.props,t=e.appElement,n=e.ariaHideApp,r=e.htmlOpenClassName,a=e.bodyOpenClassName;a&&d.add(document.body,a),r&&d.add(document.getElementsByTagName("html")[0],r),n&&(y+=1,c.hide(t)),m.default.register(this)}},{key:"render",value:function(){var e=this.props,t=e.id,n=e.className,a=e.overlayClassName,o=e.defaultStyles,i=e.children,s=n?{}:o.content,l=a?{}:o.overlay;if(this.shouldBeClosed())return null;var u={ref:this.setOverlayRef,className:this.buildClassName("overlay",a),style:r({},l,this.props.style.overlay),onClick:this.handleOverlayOnClick,onMouseDown:this.handleOverlayOnMouseDown},c=r({id:t,ref:this.setContentRef,style:r({},s,this.props.style.content),className:this.buildClassName("content",n),tabIndex:"-1",onKeyDown:this.handleKeyDown,onMouseDown:this.handleContentOnMouseDown,onMouseUp:this.handleContentOnMouseUp,onClick:this.handleContentOnClick,role:this.props.role,"aria-label":this.props.contentLabel},this.attributesFromObject("aria",r({modal:!0},this.props.aria)),this.attributesFromObject("data",this.props.data||{}),{"data-testid":this.props.testId}),d=this.props.contentElement(c,i);return this.props.overlayElement(u,d)}}]),t}(i.Component);v.defaultProps={style:{overlay:{},content:{}},defaultStyles:{}},v.propTypes={isOpen:s.default.bool.isRequired,defaultStyles:s.default.shape({content:s.default.object,overlay:s.default.object}),style:s.default.shape({content:s.default.object,overlay:s.default.object}),className:s.default.oneOfType([s.default.string,s.default.object]),overlayClassName:s.default.oneOfType([s.default.string,s.default.object]),bodyOpenClassName:s.default.string,htmlOpenClassName:s.default.string,ariaHideApp:s.default.bool,appElement:s.default.oneOfType([s.default.instanceOf(f.default),s.default.instanceOf(p.SafeHTMLCollection),s.default.instanceOf(p.SafeNodeList),s.default.arrayOf(s.default.instanceOf(f.default))]),onAfterOpen:s.default.func,onAfterClose:s.default.func,onRequestClose:s.default.func,closeTimeoutMS:s.default.number,shouldFocusAfterRender:s.default.bool,shouldCloseOnOverlayClick:s.default.bool,shouldReturnFocusAfterClose:s.default.bool,preventScroll:s.default.bool,role:s.default.string,contentLabel:s.default.string,aria:s.default.object,data:s.default.object,children:s.default.node,shouldCloseOnEsc:s.default.bool,overlayRef:s.default.func,contentRef:s.default.func,id:s.default.string,overlayElement:s.default.func,contentElement:s.default.func,testId:s.default.string},t.default=v,e.exports=t.default},978:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.resetState=function(){s&&(s.removeAttribute?s.removeAttribute("aria-hidden"):null!=s.length?s.forEach((function(e){return e.removeAttribute("aria-hidden")})):document.querySelectorAll(s).forEach((function(e){return e.removeAttribute("aria-hidden")})));s=null},t.log=function(){return void 0;var e=s||{};console.log("ariaAppHider ----------"),console.log(e.nodeName,e.className,e.id),console.log("end ariaAppHider ----------")},t.assertNodeList=l,t.setElement=function(e){var t=e;if("string"===typeof t&&i.canUseDOM){var n=document.querySelectorAll(t);l(n,t),t=n}return s=t||s},t.validateElement=u,t.hide=function(e){var t=!0,n=!1,r=void 0;try{for(var a,o=u(e)[Symbol.iterator]();!(t=(a=o.next()).done);t=!0){a.value.setAttribute("aria-hidden","true")}}catch(i){n=!0,r=i}finally{try{!t&&o.return&&o.return()}finally{if(n)throw r}}},t.show=function(e){var t=!0,n=!1,r=void 0;try{for(var a,o=u(e)[Symbol.iterator]();!(t=(a=o.next()).done);t=!0){a.value.removeAttribute("aria-hidden")}}catch(i){n=!0,r=i}finally{try{!t&&o.return&&o.return()}finally{if(n)throw r}}},t.documentNotReadyOrSSRTesting=function(){s=null};var r,a=n(1024),o=(r=a)&&r.__esModule?r:{default:r},i=n(592);var s=null;function l(e,t){if(!e||!e.length)throw new Error("react-modal: No elements were found for selector "+t+".")}function u(e){var t=e||s;return t?Array.isArray(t)||t instanceof HTMLCollection||t instanceof NodeList?t:[t]:((0,o.default)(!1,["react-modal: App element is not defined.","Please use `Modal.setAppElement(el)` or set `appElement={el}`.","This is needed so screen readers don't see main content","when modal is opened. It is not recommended, but you can opt-out","by setting `ariaHideApp={false}`."].join(" ")),[])}},3318:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.resetState=function(){for(var e=[i,s],t=0;t0?(document.body.firstChild!==i&&document.body.insertBefore(i,document.body.firstChild),document.body.lastChild!==s&&document.body.appendChild(s)):(i.parentElement&&i.parentElement.removeChild(i),s.parentElement&&s.parentElement.removeChild(s))}))},6373:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.resetState=function(){var e=document.getElementsByTagName("html")[0];for(var t in n)a(e,n[t]);var o=document.body;for(var i in r)a(o,r[i]);n={},r={}},t.log=function(){return void 0;var e=document.getElementsByTagName("html")[0].className,t="Show tracked classes:\n\n";for(var a in t+=" ("+e+"):\n",n)t+=" "+a+" "+n[a]+"\n";for(var o in e=document.body.className,t+="\n\ndoc.body ("+e+"):\n",r)t+=" "+o+" "+r[o]+"\n";t+="\n",console.log(t)};var n={},r={};function a(e,t){e.classList.remove(t)}t.add=function(e,t){return a=e.classList,o="html"==e.nodeName.toLowerCase()?n:r,void t.split(" ").forEach((function(e){!function(e,t){e[t]||(e[t]=0),e[t]+=1}(o,e),a.add(e)}));var a,o},t.remove=function(e,t){return a=e.classList,o="html"==e.nodeName.toLowerCase()?n:r,void t.split(" ").forEach((function(e){!function(e,t){e[t]&&(e[t]-=1)}(o,e),0===o[e]&&a.remove(e)}));var a,o}},6983:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.resetState=function(){i=[]},t.log=function(){return void 0;console.log("focusManager ----------"),i.forEach((function(e){var t=e||{};console.log(t.nodeName,t.className,t.id)})),console.log("end focusManager ----------")},t.handleBlur=u,t.handleFocus=c,t.markForFocusLater=function(){i.push(document.activeElement)},t.returnFocus=function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=null;try{return void(0!==i.length&&(t=i.pop()).focus({preventScroll:e}))}catch(n){console.warn(["You tried to return focus to",t,"but it is not in the DOM anymore"].join(" "))}},t.popWithoutFocus=function(){i.length>0&&i.pop()},t.setupScopedFocus=function(e){s=e,window.addEventListener?(window.addEventListener("blur",u,!1),document.addEventListener("focus",c,!0)):(window.attachEvent("onBlur",u),document.attachEvent("onFocus",c))},t.teardownScopedFocus=function(){s=null,window.addEventListener?(window.removeEventListener("blur",u),document.removeEventListener("focus",c)):(window.detachEvent("onBlur",u),document.detachEvent("onFocus",c))};var r,a=n(1227),o=(r=a)&&r.__esModule?r:{default:r};var i=[],s=null,l=!1;function u(){l=!0}function c(){if(l){if(l=!1,!s)return;setTimeout((function(){s.contains(document.activeElement)||((0,o.default)(s)[0]||s).focus()}),0)}}},3754:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.log=function(){console.log("portalOpenInstances ----------"),console.log(r.openInstances.length),r.openInstances.forEach((function(e){return console.log(e)})),console.log("end portalOpenInstances ----------")},t.resetState=function(){r=new n};var n=function e(){var t=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.register=function(e){-1===t.openInstances.indexOf(e)&&(t.openInstances.push(e),t.emit("register"))},this.deregister=function(e){var n=t.openInstances.indexOf(e);-1!==n&&(t.openInstances.splice(n,1),t.emit("deregister"))},this.subscribe=function(e){t.subscribers.push(e)},this.emit=function(e){t.subscribers.forEach((function(n){return n(e,t.openInstances.slice())}))},this.openInstances=[],this.subscribers=[]},r=new n;t.default=r},592:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.canUseDOM=t.SafeNodeList=t.SafeHTMLCollection=void 0;var r,a=n(5538);var o=((r=a)&&r.__esModule?r:{default:r}).default,i=o.canUseDOM?window.HTMLElement:{};t.SafeHTMLCollection=o.canUseDOM?window.HTMLCollection:{},t.SafeNodeList=o.canUseDOM?window.NodeList:{},t.canUseDOM=o.canUseDOM;t.default=i},1009:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){var n=(0,o.default)(e);if(!n.length)return void t.preventDefault();var r=void 0,a=t.shiftKey,i=n[0],s=n[n.length-1];if(e===document.activeElement){if(!a)return;r=s}s!==document.activeElement||a||(r=i);i===document.activeElement&&a&&(r=s);if(r)return t.preventDefault(),void r.focus();var l=/(\bChrome\b|\bSafari\b)\//.exec(navigator.userAgent);if(null==l||"Chrome"==l[1]||null!=/\biPod\b|\biPad\b/g.exec(navigator.userAgent))return;var u=n.indexOf(document.activeElement);u>-1&&(u+=a?-1:1);if("undefined"===typeof(r=n[u]))return t.preventDefault(),void(r=a?s:i).focus();t.preventDefault(),r.focus()};var r,a=n(1227),o=(r=a)&&r.__esModule?r:{default:r};e.exports=t.default},1227:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return[].slice.call(e.querySelectorAll("*"),0).filter(o)};var n=/input|select|textarea|button|object/;function r(e){var t=e.offsetWidth<=0&&e.offsetHeight<=0;if(t&&!e.innerHTML)return!0;try{var n=window.getComputedStyle(e);return t?"visible"!==n.getPropertyValue("overflow")||e.scrollWidth<=0&&e.scrollHeight<=0:"none"==n.getPropertyValue("display")}catch(r){return console.warn("Failed to inspect element style"),!1}}function a(e,t){var a=e.nodeName.toLowerCase();return(n.test(a)&&!e.disabled||"a"===a&&e.href||t)&&function(e){for(var t=e;t&&t!==document.body;){if(r(t))return!1;t=t.parentNode}return!0}(e)}function o(e){var t=e.getAttribute("tabindex");null===t&&(t=void 0);var n=isNaN(t);return(n||t>=0)&&a(e,!n)}e.exports=t.default},5196:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,a=n(6185),o=(r=a)&&r.__esModule?r:{default:r};t.default=o.default,e.exports=t.default},6123:function(e,t){var n;!function(){"use strict";var r={}.hasOwnProperty;function a(){for(var e=[],t=0;t4&&n.slice(0,4)===i&&s.test(t)&&("-"===t.charAt(4)?p=function(e){var t=e.slice(5).replace(l,d);return i+t.charAt(0).toUpperCase()+t.slice(1)}(t):t=function(e){var t=e.slice(4);if(l.test(t))return e;t=t.replace(u,c),"-"!==t.charAt(0)&&(t="-"+t);return i+t}(t),f=a);return new f(p,t)};var s=/^data[-\w.:]+$/i,l=/-[a-z]/g,u=/[A-Z]/g;function c(e){return"-"+e.toLowerCase()}function d(e){return e.charAt(1).toUpperCase()}},3261:function(e,t,n){"use strict";var r=n(7883),a=n(748),o=n(6885),i=n(3009),s=n(8470),l=n(6134);e.exports=r([o,a,i,s,l])},8470:function(e,t,n){"use strict";var r=n(8118),a=n(6512),o=r.booleanish,i=r.number,s=r.spaceSeparated;e.exports=a({transform:function(e,t){return"role"===t?t:"aria-"+t.slice(4).toLowerCase()},properties:{ariaActiveDescendant:null,ariaAtomic:o,ariaAutoComplete:null,ariaBusy:o,ariaChecked:o,ariaColCount:i,ariaColIndex:i,ariaColSpan:i,ariaControls:s,ariaCurrent:null,ariaDescribedBy:s,ariaDetails:null,ariaDisabled:o,ariaDropEffect:s,ariaErrorMessage:null,ariaExpanded:o,ariaFlowTo:s,ariaGrabbed:o,ariaHasPopup:null,ariaHidden:o,ariaInvalid:null,ariaKeyShortcuts:null,ariaLabel:null,ariaLabelledBy:s,ariaLevel:i,ariaLive:null,ariaModal:o,ariaMultiLine:o,ariaMultiSelectable:o,ariaOrientation:null,ariaOwns:s,ariaPlaceholder:null,ariaPosInSet:i,ariaPressed:o,ariaReadOnly:o,ariaRelevant:null,ariaRequired:o,ariaRoleDescription:s,ariaRowCount:i,ariaRowIndex:i,ariaRowSpan:i,ariaSelected:o,ariaSetSize:i,ariaSort:null,ariaValueMax:i,ariaValueMin:i,ariaValueNow:i,ariaValueText:null,role:null}})},6134:function(e,t,n){"use strict";var r=n(8118),a=n(6512),o=n(7136),i=r.boolean,s=r.overloadedBoolean,l=r.booleanish,u=r.number,c=r.spaceSeparated,d=r.commaSeparated;e.exports=a({space:"html",attributes:{acceptcharset:"accept-charset",classname:"class",htmlfor:"for",httpequiv:"http-equiv"},transform:o,mustUseProperty:["checked","multiple","muted","selected"],properties:{abbr:null,accept:d,acceptCharset:c,accessKey:c,action:null,allow:null,allowFullScreen:i,allowPaymentRequest:i,allowUserMedia:i,alt:null,as:null,async:i,autoCapitalize:null,autoComplete:c,autoFocus:i,autoPlay:i,capture:i,charSet:null,checked:i,cite:null,className:c,cols:u,colSpan:null,content:null,contentEditable:l,controls:i,controlsList:c,coords:u|d,crossOrigin:null,data:null,dateTime:null,decoding:null,default:i,defer:i,dir:null,dirName:null,disabled:i,download:s,draggable:l,encType:null,enterKeyHint:null,form:null,formAction:null,formEncType:null,formMethod:null,formNoValidate:i,formTarget:null,headers:c,height:u,hidden:i,high:u,href:null,hrefLang:null,htmlFor:c,httpEquiv:c,id:null,imageSizes:null,imageSrcSet:d,inputMode:null,integrity:null,is:null,isMap:i,itemId:null,itemProp:c,itemRef:c,itemScope:i,itemType:c,kind:null,label:null,lang:null,language:null,list:null,loading:null,loop:i,low:u,manifest:null,max:null,maxLength:u,media:null,method:null,min:null,minLength:u,multiple:i,muted:i,name:null,nonce:null,noModule:i,noValidate:i,onAbort:null,onAfterPrint:null,onAuxClick:null,onBeforePrint:null,onBeforeUnload:null,onBlur:null,onCancel:null,onCanPlay:null,onCanPlayThrough:null,onChange:null,onClick:null,onClose:null,onContextMenu:null,onCopy:null,onCueChange:null,onCut:null,onDblClick:null,onDrag:null,onDragEnd:null,onDragEnter:null,onDragExit:null,onDragLeave:null,onDragOver:null,onDragStart:null,onDrop:null,onDurationChange:null,onEmptied:null,onEnded:null,onError:null,onFocus:null,onFormData:null,onHashChange:null,onInput:null,onInvalid:null,onKeyDown:null,onKeyPress:null,onKeyUp:null,onLanguageChange:null,onLoad:null,onLoadedData:null,onLoadedMetadata:null,onLoadEnd:null,onLoadStart:null,onMessage:null,onMessageError:null,onMouseDown:null,onMouseEnter:null,onMouseLeave:null,onMouseMove:null,onMouseOut:null,onMouseOver:null,onMouseUp:null,onOffline:null,onOnline:null,onPageHide:null,onPageShow:null,onPaste:null,onPause:null,onPlay:null,onPlaying:null,onPopState:null,onProgress:null,onRateChange:null,onRejectionHandled:null,onReset:null,onResize:null,onScroll:null,onSecurityPolicyViolation:null,onSeeked:null,onSeeking:null,onSelect:null,onSlotChange:null,onStalled:null,onStorage:null,onSubmit:null,onSuspend:null,onTimeUpdate:null,onToggle:null,onUnhandledRejection:null,onUnload:null,onVolumeChange:null,onWaiting:null,onWheel:null,open:i,optimum:u,pattern:null,ping:c,placeholder:null,playsInline:i,poster:null,preload:null,readOnly:i,referrerPolicy:null,rel:c,required:i,reversed:i,rows:u,rowSpan:u,sandbox:c,scope:null,scoped:i,seamless:i,selected:i,shape:null,size:u,sizes:null,slot:null,span:u,spellCheck:l,src:null,srcDoc:null,srcLang:null,srcSet:d,start:u,step:null,style:null,tabIndex:u,target:null,title:null,translate:null,type:null,typeMustMatch:i,useMap:null,value:l,width:u,wrap:null,align:null,aLink:null,archive:c,axis:null,background:null,bgColor:null,border:u,borderColor:null,bottomMargin:u,cellPadding:null,cellSpacing:null,char:null,charOff:null,classId:null,clear:null,code:null,codeBase:null,codeType:null,color:null,compact:i,declare:i,event:null,face:null,frame:null,frameBorder:null,hSpace:u,leftMargin:u,link:null,longDesc:null,lowSrc:null,marginHeight:u,marginWidth:u,noResize:i,noHref:i,noShade:i,noWrap:i,object:null,profile:null,prompt:null,rev:null,rightMargin:u,rules:null,scheme:null,scrolling:l,standby:null,summary:null,text:null,topMargin:u,valueType:null,version:null,vAlign:null,vLink:null,vSpace:u,allowTransparency:null,autoCorrect:null,autoSave:null,disablePictureInPicture:i,disableRemotePlayback:i,prefix:null,property:null,results:u,security:null,unselectable:null}})},7136:function(e,t,n){"use strict";var r=n(1989);e.exports=function(e,t){return r(e,t.toLowerCase())}},1989:function(e){"use strict";e.exports=function(e,t){return t in e?e[t]:t}},6512:function(e,t,n){"use strict";var r=n(8134),a=n(8815),o=n(4388);e.exports=function(e){var t,n,i=e.space,s=e.mustUseProperty||[],l=e.attributes||{},u=e.properties,c=e.transform,d={},p={};for(t in u)n=new o(t,c(l,t),u[t],i),-1!==s.indexOf(t)&&(n.mustUseProperty=!0),d[t]=n,p[r(t)]=t,p[r(n.attribute)]=t;return new a(d,p,i)}},4388:function(e,t,n){"use strict";var r=n(6721),a=n(8118);e.exports=s,s.prototype=new r,s.prototype.defined=!0;var o=["boolean","booleanish","overloadedBoolean","number","commaSeparated","spaceSeparated","commaOrSpaceSeparated"],i=o.length;function s(e,t,n,s){var u,c=-1;for(l(this,"space",s),r.call(this,e,t);++c=97&&t<=122||t>=65&&t<=90}},9319:function(e,t,n){"use strict";var r=n(7631),a=n(4597);e.exports=function(e){return r(e)||a(e)}},3110:function(e){e.exports=function(e){return null!=e&&null!=e.constructor&&"function"===typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}},4597:function(e){"use strict";e.exports=function(e){var t="string"===typeof e?e.charCodeAt(0):e;return t>=48&&t<=57}},6422:function(e){"use strict";e.exports=function(e){var t="string"===typeof e?e.charCodeAt(0):e;return t>=97&&t<=102||t>=65&&t<=70||t>=48&&t<=57}},3108:function(e,t,n){e=n.nmd(e);var r="__lodash_hash_undefined__",a=1,o=2,i=9007199254740991,s="[object Arguments]",l="[object Array]",u="[object AsyncFunction]",c="[object Boolean]",d="[object Date]",p="[object Error]",f="[object Function]",m="[object GeneratorFunction]",g="[object Map]",h="[object Number]",b="[object Null]",y="[object Object]",v="[object Promise]",E="[object Proxy]",S="[object RegExp]",w="[object Set]",k="[object String]",T="[object Symbol]",_="[object Undefined]",x="[object WeakMap]",A="[object ArrayBuffer]",O="[object DataView]",N=/^\[object .+?Constructor\]$/,C=/^(?:0|[1-9]\d*)$/,R={};R["[object Float32Array]"]=R["[object Float64Array]"]=R["[object Int8Array]"]=R["[object Int16Array]"]=R["[object Int32Array]"]=R["[object Uint8Array]"]=R["[object Uint8ClampedArray]"]=R["[object Uint16Array]"]=R["[object Uint32Array]"]=!0,R[s]=R[l]=R[A]=R[c]=R[O]=R[d]=R[p]=R[f]=R[g]=R[h]=R[y]=R[S]=R[w]=R[k]=R[x]=!1;var I="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,L="object"==typeof self&&self&&self.Object===Object&&self,D=I||L||Function("return this")(),P=t&&!t.nodeType&&t,M=P&&e&&!e.nodeType&&e,F=M&&M.exports===P,U=F&&I.process,B=function(){try{return U&&U.binding&&U.binding("util")}catch(e){}}(),j=B&&B.isTypedArray;function z(e,t){for(var n=-1,r=null==e?0:e.length;++nu))return!1;var d=s.get(e);if(d&&s.get(t))return d==t;var p=-1,f=!0,m=n&o?new xe:void 0;for(s.set(e,t),s.set(t,e);++p-1},Te.prototype.set=function(e,t){var n=this.__data__,r=Ne(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this},_e.prototype.clear=function(){this.size=0,this.__data__={hash:new ke,map:new(de||Te),string:new ke}},_e.prototype.delete=function(e){var t=Fe(this,e).delete(e);return this.size-=t?1:0,t},_e.prototype.get=function(e){return Fe(this,e).get(e)},_e.prototype.has=function(e){return Fe(this,e).has(e)},_e.prototype.set=function(e,t){var n=Fe(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this},xe.prototype.add=xe.prototype.push=function(e){return this.__data__.set(e,r),this},xe.prototype.has=function(e){return this.__data__.has(e)},Ae.prototype.clear=function(){this.__data__=new Te,this.size=0},Ae.prototype.delete=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n},Ae.prototype.get=function(e){return this.__data__.get(e)},Ae.prototype.has=function(e){return this.__data__.has(e)},Ae.prototype.set=function(e,t){var n=this.__data__;if(n instanceof Te){var r=n.__data__;if(!de||r.length<199)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new _e(r)}return n.set(e,t),this.size=n.size,this};var Be=se?function(e){return null==e?[]:(e=Object(e),function(e,t){for(var n=-1,r=null==e?0:e.length,a=0,o=[];++n-1&&e%1==0&&e-1&&e%1==0&&e<=i}function Ke(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}function Xe(e){return null!=e&&"object"==typeof e}var Ze=j?function(e){return function(t){return e(t)}}(j):function(e){return Xe(e)&&Ye(e.length)&&!!R[Ce(e)]};function Qe(e){return null!=(t=e)&&Ye(t.length)&&!qe(t)?Oe(e):De(e);var t}e.exports=function(e,t){return Ie(e,t)}},6174:function(e,t,n){var r="[object Null]",a="[object Undefined]",o="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,i="object"==typeof self&&self&&self.Object===Object&&self,s=o||i||Function("return this")(),l=Object.prototype,u=l.hasOwnProperty,c=l.toString,d=s.Symbol,p=d?d.toStringTag:void 0;function f(e){return null==e?void 0===e?a:r:p&&p in Object(e)?function(e){var t=u.call(e,p),n=e[p];try{e[p]=void 0;var r=!0}catch(o){}var a=c.call(e);r&&(t?e[p]=n:delete e[p]);return a}(e):function(e){return c.call(e)}(e)}e.exports=function(e){if(!function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}(e))return!1;var t=f(e);return"[object Function]"==t||"[object GeneratorFunction]"==t||"[object AsyncFunction]"==t||"[object Proxy]"==t}},1843:function(e){"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(a){return!1}}()?Object.assign:function(e,a){for(var o,i,s=function(e){if(null===e||void 0===e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(e),l=1;l65535&&(V+=c((z-=65536)>>>10|55296),z=56320|1023&z),z=V+c(z))):K!==_&&G(L,J)),z?(Ee(),Z=ye(),pe=ee-1,me+=ee-Y+1,be.push(z),(Q=ye()).offset++,ae&&ae.call(se,z,{start:Z,end:Q},e.slice(Y-1,ee)),Z=Q):(p=e.slice(Y-1,ee),he+=p,me+=p.length,pe=ee-1)}else 10===j&&(ge++,fe++,me=0),j===j?(he+=c(j),me++):Ee();return be.join("");function ye(){return{line:ge,column:me,offset:pe+(ue.offset||0)}}function ve(e,t){var n=ye();n.column+=t,n.offset+=t,oe.call(le,F[e],n,e)}function Ee(){he&&(be.push(he),re&&re.call(ie,he,{start:Z,end:ye()}),he="")}}(e,i)};var u={}.hasOwnProperty,c=String.fromCharCode,d=Function.prototype,p={warning:null,reference:null,text:null,warningContext:null,referenceContext:null,textContext:null,position:{},additional:null,attribute:!1,nonTerminated:!0},f=9,m=10,g=12,h=32,b=38,y=59,v=60,E=61,S=35,w=88,k=120,T=65533,_="named",x="hexadecimal",A="decimal",O={};O[x]=16,O[A]=10;var N={};N[_]=s,N[A]=o,N[x]=i;var C=1,R=2,I=3,L=4,D=5,P=6,M=7,F={};function U(e){return e>=55296&&e<=57343||e>1114111}function B(e){return e>=1&&e<=8||11===e||e>=13&&e<=31||e>=127&&e<=159||e>=64976&&e<=65007||65535===(65535&e)||65534===(65535&e)}F[C]="Named character references must be terminated by a semicolon",F[R]="Numeric character references must be terminated by a semicolon",F[I]="Named character references cannot be empty",F[L]="Numeric character references cannot be empty",F[D]="Named character references must be known",F[P]="Numeric character references cannot be disallowed",F[M]="Numeric character references cannot be outside the permissible Unicode range"},1729:function(e,t,n){"use strict";var r=n(9165);function a(){}function o(){}o.resetWarningCache=a,e.exports=function(){function e(e,t,n,a,o,i){if(i!==r){var s=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw s.name="Invariant Violation",s}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:o,resetWarningCache:a};return n.PropTypes=n,n}},5192:function(e,t,n){e.exports=n(1729)()},9165:function(e){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},534:function(e,t,n){"use strict";var r=n(7313),a=n(2224);function o(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n
"!==e.children[l].value;)u.children.push(e.children[l]),l++}else s="tab",a={type:"raw",children:[],data:{hProperties:{label:"",type:"tab",headers:[]}}},i.children.push(a)}Pg(r,"link",(function(e,t,n){n.children.splice(t,1),r.data.hProperties.buttons.push({url:e.url,label:e.children[0].value})})),e.children=[{type:"raw",children:[r,i],data:{hProperties:{type:"page"}}}]}}function zg(e){return zg="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},zg(e)}function $g(e,t){for(var n=0;n=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var Oh=function(e){var t=e.className,n=e.children,r=e.variant,o=e.selected,i=e.id,s=e.tabId;Ah(e,["className","children","variant","selected","id","tabId"]);return a.createElement("div",{"aria-labelledby":s,className:ln()(t,{"slds-show":o,"slds-hide":!o,"slds-tabs_default__content":"default"===r,"slds-tabs_scoped__content":"scoped"===r,"slds-vertical-tabs__content":"vertical"===r}),id:i,role:"tabpanel"},n.props.children)};Oh.displayName="SLDSTabPanel",Oh.propTypes={children:on().oneOfType([on().array,on().object,on().string]),className:on().string,id:on().string,selected:on().bool,variant:on().oneOf(["default","scoped","vertical"]),tabId:on().string},Oh.defaultProps={variant:"default",selected:!1};var Nh=Oh;function Ch(e){return Ch="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Ch(e)}function Rh(e,t){for(var n=0;ne;)if(!Fh(this.getTab(t)))return t;return e}},{key:"getSelectedIndex",value:function(){return Number.isInteger(this.props.selectedIndex)?this.props.selectedIndex:this.state.selectedIndex}},{key:"getTab",value:function(e){return this.tabs[e].tab}},{key:"getTabNode",value:function(e){return this.tabs[e].node}},{key:"getTabsCount",value:function(){return this.props.children?a.Children.count(this.props.children):0}},{key:"getVariant",value:function(){return this.props.variant||"default"}},{key:"setSelected",value:function(e,t){if(!(e<0||e>=this.getTabsCount())){var n,r=this.getSelectedIndex();Di()(this.props.onSelect)&&(n=this.props.onSelect(e,r)),!1!==n&&e!==this.state.selectedIndex&&this.setState({selectedIndex:e,focus:!0===t})}}},{key:"isTabFromContainer",value:function(e){if(!function(e){return"A"===e.nodeName&&"tab"===e.getAttribute("role")||"LI"===e.nodeName&&(e.classList.contains("slds-tabs_default__item")||e.classList.contains("slds-tabs_scoped__item")||e.classList.contains("slds-vertical-tabs__nav-item"))}(e))return!1;var t=e.parentElement;do{if(t===this.tabsNode)return!0;if(t.getAttribute("data-tabs"))break;t=t.parentElement}while(t);return!1}},{key:"renderTabPanels",value:function(e){var t,n=this,r=a.Children.toArray(this.props.children),o=this.getSelectedIndex();return t=r.map((function(t,i){var s="".concat(e,"-slds-tabs_tab-").concat(i),l="".concat(e,"-slds-tabs_panel-").concat(i),u=o===i,c=n.getVariant();return a.createElement(Nh,{key:t.key,selected:u,id:l,tabId:s,variant:c},r[i])})),t}},{key:"renderTabsList",value:function(e){var t=this,n=a.Children.toArray(this.props.children);return a.createElement(bh,{id:e,variant:this.getVariant()},n.map((function(n,r){var o="".concat(e,"-slds-tabs_tab-").concat(r),i="".concat(e,"-slds-tabs_panel-").concat(r),s=t.getSelectedIndex()===r,l=s&&t.state.focus,u=t.getVariant();return a.createElement(xh,{key:n.key,ref:function(e){t.tabs[r]={tab:n,node:e},t.state.focus&&t.setState({focus:!1})},focus:l,selected:s,id:o,panelId:i,disabled:n.props.disabled,variant:u,hasError:n.props.hasError,assistiveText:n.props.assistiveText},n.props.label)})))}},{key:"render",value:function(){var e=this,t=this.props,n=t.className,r=t.id,o=void 0===r?this.generatedId:r,i=t.variant,s=void 0===i?this.getVariant:i;return a.createElement("div",{id:o,className:ln()({"slds-tabs_default":"default"===s,"slds-tabs_scoped":"scoped"===s,"slds-vertical-tabs":"vertical"===s},n),onClick:this.handleClick,onKeyDown:this.handleKeyDown,"data-tabs":!0,ref:function(t){e.tabsNode=t}},this.renderTabsList(o),this.renderTabPanels(o))}}],n&&Rh(t.prototype,n),r&&Rh(t,r),i}(a.Component);Bh.displayName="SLDSTabs",Bh.propTypes=Uh,Bh.defaultProps={defaultSelectedIndex:0,variant:"default"};var jh=Bh,zh=function(e){var t=e.children;return a.createElement("div",null,a.Children.toArray(t))};zh.displayName="SLDSTabsPanel",zh.propTypes={label:on().oneOfType([on().string,on().element]).isRequired,children:on().oneOfType([on().arrayOf(on().node),on().node,on().element]).isRequired,hasError:on().bool,assistiveText:on().shape({withErrorIcon:on().string})};var $h=zh;function Gh(e){var t=e.href,n=e.label;return(0,Vl.jsx)("a",{className:"slds-button slds-button_neutral",target:"_blank",href:t,children:n})}var Hh=function(e){y(n,e);var t=w(n);function n(){return p(this,n),t.apply(this,arguments)}return h(n,[{key:"render",value:function(){var e=this.props,t=[];for(var n in e.items)e.items.hasOwnProperty(n)&&(t.push((0,Vl.jsx)("dt",{className:"slds-item_label slds-text-color_weak slds-truncate",children:n},n+"_key")),t.push((0,Vl.jsx)("dd",{className:"slds-item_detail slds-truncate",children:e.items[n]},n+"_val")));return(0,Vl.jsxs)("article",{className:"slds-tile",children:[(0,Vl.jsx)("h3",{className:"slds-tile__title slds-truncate",title:e.title,children:(0,Vl.jsx)("a",{children:e.title})}),(0,Vl.jsx)("div",{className:"slds-tile__detail",children:(0,Vl.jsx)("dl",{className:"slds-list_horizontal slds-wrap",children:t})})]})}}]),n}(a.Component);function Vh(e,t){if(e&&t){var n=[],r=e.properties.label;t.forEach((function(e){n.push(e),e.label===r&&n.push.apply(n,I(e.children))})),Kl("onTableOfContentsChange",{headers:n})}}function Wh(e){var t=e.config,n=e.header,r=e.tabs,o=e.buttons,i=d((0,a.useState)(0),2),s=i[0],l=i[1],u=n.node.properties.pageContent;(0,a.useEffect)((function(){var e;Vh(r.node.children[0],u),e=function(e){l(e),Vh(r.node.children[e],u)},Xl("onTabChangeRequest",0,(function(t){return e(t.detail.tabIndex)}))}),[r,n]);var c=t.iconName.split(":");return(0,Vl.jsxs)("div",{children:[(0,Vl.jsx)("div",{className:"slds-theme_default slds-box_border slds-m-bottom_medium",id:"top",children:(0,Vl.jsx)(gh,{heading:n.label,icon:(0,Vl.jsx)(go,{category:c[0],name:c[1]}),bodyClassName:"slds-p-horizontal_small",headerActions:o.map((function(e){return(0,Vl.jsx)(Gh,{href:e.url,label:e.label},e.label)})),children:(0,Vl.jsxs)("div",{className:"slds-grid",children:[(0,Vl.jsx)(ql,{isTrue:t.copyright,children:(0,Vl.jsx)("div",{className:"slds-col slds-size_1-of-4 slds-border_right",children:(0,Vl.jsx)(Hh,{title:"",items:{License:t.license||"MIT",Copyright:t.copyright}})})}),(0,Vl.jsx)("div",{className:"slds-col",children:(0,Vl.jsx)("div",{className:"slds-text-body_regular slds-p-horizontal_small",children:n.children})})]})})}),(0,Vl.jsx)("div",{className:"slds-theme_default slds-box_border slds-box_darker",children:(0,Vl.jsx)(jh,{onSelect:function(e){l(e),Vh(r.node.children[e],u)},selectedIndex:s,children:r.children.map((function(e,t){return(0,Vl.jsx)($h,{id:"".concat(t),label:e.props.label,children:(0,Vl.jsx)("div",{id:Ug(e.props.label),className:"centered",children:(0,Vl.jsx)("div",{className:"slds-p-horizontal_small",children:e.props.children})})},t)}))})})]})}var qh=["node","inline","className","children"];function Yh(e){return e.toLowerCase().replace(" ","-")}function Kh(e){var t=e.config,n=d((0,a.useState)(),2),r=n[0],o=n[1];return(0,a.useEffect)((function(){var e;(e=t.markdownUrl,Yf[e]?Promise.resolve(Yf[e]):(Yf[e]=fetch(e).then((function(e){return e.text()})).then((function(t){return Yf[e]=t,t})),Yf[e])).then((function(e){return o(e)}));var n=document.getElementById("top");null===n||void 0===n||n.scrollIntoView({behavior:"instant",block:"start"})}),[t.markdownUrl]),r?(0,Vl.jsx)(qf,{children:r,remarkPlugins:[_g,jg],rehypePlugins:[Bg],components:{div:function(e){var n=e.type,r=e.node,a=e.children;if("page"===n){var o=a[0].props,i=a[1].props,s=o.node.properties.buttons;return(0,Vl.jsx)(Wh,{config:t,header:o,tabs:i,buttons:s})}return"accordion"===n?(0,Vl.jsx)(Gu,{collapsed:!0,label:r.properties.title,children:a}):a},h1:function(e){var t=e.children;return(0,Vl.jsx)("h1",{id:Yh(t[0]),className:"slds-m-top_x-large slds-text-heading_large",children:t})},h2:function(e){var t=e.children;return(0,Vl.jsx)("h2",{id:Yh(t[0]),className:"slds-m-top_x-large slds-text-heading_medium",children:t})},h3:function(e){var t=e.children;return(0,Vl.jsx)("h3",{id:Yh(t[0]),className:"slds-m-top_large slds-text-heading_small",children:t})},h4:function(e){var t=e.children;return(0,Vl.jsx)("h4",{className:"slds-m-top_large slds-text-title_caps",children:t})},h5:function(e){var t=e.children;return(0,Vl.jsx)("h5",{className:"slds-m-top_large slds-text-title_bold",children:t})},h6:function(e){var t=e.children;return(0,Vl.jsx)("h6",{className:"slds-m-top_large slds-text-title",children:t})},ul:function(e){var t=e.children;return(0,Vl.jsx)("ul",{className:"slds-m-bottom_medium slds-list_dotted",children:t})},ol:function(e){var t=e.children;return(0,Vl.jsx)("ul",{className:"slds-m-bottom_medium slds-list_ordered",children:t})},table:function(e){var t=e.children;return(0,Vl.jsx)("div",{className:"slds-scrollable_x",children:(0,Vl.jsx)("table",{className:"slds-box_border slds-table slds-table_bordered slds-table_col-bordered",children:t})})},pre:function(e){var t=e.children;return(0,Vl.jsx)("pre",{className:"code",children:t})},code:function(e){var t,n=e.node,r=e.inline,a=e.className,o=e.children,i=ru(e,qh),s=/language-(\w+)/.exec(a||""),l=null===(t=n.data)||void 0===t?void 0:t.meta,u=l?l.split("|").flatMap((function(e){return e?e.trim():[]})):[];return!r&&s?(0,Vl.jsx)(Ru,{language:s[1],header:u[0],description:u[1],showLineNumbers:!0,code:String(o).replace(/\n$/,"")}):(0,Vl.jsx)("code",nu(nu({},i),{},{className:a,children:o}))}}}):(0,Vl.jsx)(xs,{})}function Xh(e){return Xh="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Xh(e)}function Zh(e,t){for(var n=0;n contact = Query.Contacts\n .byAccountId(accountIds)\n .getList();\n"}}),sb({path:"trigger-handler",label:"Trigger Handler",description:"Orchestrator for Apex Trigger Logic.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2019",markdownUrl:"/TriggerHandler.md",snippet:{language:"apex",code:"\nbeforeInsert(List triggerNew, TriggerContext tc);\nafterInsert(List triggerNew, TriggerContext tc);\n\nbeforeUpdate(List triggerNew, TriggerContext tc);\nafterUpdate(List triggerNew, TriggerContext tc);\n"}}),sb({path:"trigger-handler-mdt",label:"Metadata Trigger Handler",description:"Custom Metadata-driven orchestrator for Apex Trigger Logic",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2019",markdownUrl:"/TriggerHandlerMdt.md",image:"/img/th-mdt.jpg"})]),ib("Unit Testing",[sb({path:"http-mock-router",label:"Http Mock Router",description:"Configuration-driven, endpoint pattern-based router for Http Mocks.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2018",markdownUrl:"/HttpCalloutMockRouter.md",image:"/img/http-router.jpg",snippet:{language:"apex",code:"Test.setMock(HttpCalloutMock.class, HttpMocks.config());"}}),sb({path:"mock",label:"Mocking",description:"Mock response of the class.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2023",markdownUrl:"/Mock.md",snippet:{language:"apex",code:" \nMock.response(AccountSelector.class, new Account());\n\nMock.response(AccountSelector.class, new Map{\n 'getByOwnerId' => new Account(Name = 'My Account'),\n 'getById#1' => new Account(Name = 'Test Account'),\n 'getById#2' => new Account(Name = 'Another Account'),\n 'getById#3' => new QueryException('List has no rows...')\n});\n"}}),sb({path:"test-data-builder",label:"Test Data Builder",description:"Setup test records for unit tests.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2019",markdownUrl:"/TestDataBuilder.md",snippet:{language:"apex",code:"\nnew TestDataBuilder()\n .create(5, 'SMB', new Account(Name = 'Business Account'))\n .create(new Account(Name = 'Test', BillingCountry = 'US'))\n .similarly(new Account(BillingCity = 'Austin'))\n .insertRecords();\n"}}),sb({path:"test-data-suite",label:"Test Data Suite",description:"Access records created in @TestSetup",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2021",markdownUrl:"/TestDataSuite.md",snippet:{language:"apex",code:"\n@IsTest\nstatic void myTest(){\n TestDataSuite suite;\n List accounts = suite.get(Account.SObjectType);\n User testUser = (User) suite.get('testUser');\n}\n\n"}})]),ib("Utilities",[sb({path:"batch",label:"Batch Schedulable",description:"Schedule batches without implementing dedicated Schedulable.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2018",markdownUrl:"/BatchSchedulable.md",snippet:{language:"apex",code:"\nSystem.schedule('SObject Cleaner', cronExpr,\n new BatchSchedulable(SObjectCleanerBatch.class)\n);\n"}}),sb({path:"scheduler",label:"Scheduler",description:"Shorthand methods for easy scheduling.",iconName:"standard:date_time",copyright:"Piotr Ko\u017cuchowski @2018",markdownUrl:"/Scheduler.md",snippet:{language:"apex",code:"\nScheduler.scheduleDaily(\n 'Data Cleaner', 12, 00, \n new DataCleaningSchedulable()\n);\n"}}),sb({path:"custom-metadata-service",label:"Custom Metadata Service",description:"Deploy Custom Metadata from Apex.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2020",markdownUrl:"/CustomMetadataService.md",snippet:{language:"apex",code:"\nCustomMetadataService.deploy(new List{\n new Country__mdt(DeveloperName = 'USA'),\n new Country__mdt(DeveloperName = 'France'),\n new Country__mdt(DeveloperName = 'Poland')\n});\n"}}),sb({path:"database-service",label:"Database Service",description:"Issue and mock DMLs with configurable sharing and user mode.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2018",markdownUrl:"/DatabaseService.md",snippet:{language:"apex",code:"\nnew DatabaseService()\n .withoutSharing()\n .updateRecords(accounts); "}}),sb({path:"localization",label:"Localization",description:"Dynamically retrieve Custom Labels, Field and Picklist labels for given locale.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2020",markdownUrl:"/Localization.md",snippet:{language:"apex",code:"\nLocalization.getCustomLabelsWithLocale(new List{\n 'COM_Toast_Success',\n 'COM_Toast_Info'\n}, 'pl')"}}),sb({path:"picklist",label:"Picklist",description:"Access Picklist metadata.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2019",markdownUrl:"/Picklist.md",snippet:{language:"apex",code:"\nPicklist p = new Picklist(Account.Type);\np.getDefaultValue();\np.getValues();\np.getEntriesByControllingValue();\n"}}),sb({path:"runtime",label:"Runtime",description:"Reflective Apex Utility.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2019",markdownUrl:"/Runtime.md",snippet:{language:"apex",code:"\nObject o = new Account();\n\nRuntime.getType(o); // -> Account.class\nRuntime.getTypeName(o); // -> 'Account'\n"}}),sb({path:"xml-parser",label:"XML Parser",description:"Translate XML document into JSON or Apex class.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2023",markdownUrl:"/XmlParser.md",snippet:{language:"apex",code:"\nXmlParser parser = new XmlParser(xmlString);\n\nMap untypedMap = parser.getUntyped();\nProfile p = (Profile) parser.getAs(Profile.class, false);\n"}})])]}),ob({label:"LWC",path:"lwc",children:[ib("Patterns",[sb({path:"commons",label:"Commons Module",description:"Organize common utilities and methods.",iconName:"standard:code_playground",markdownUrl:"/commons.md",snippet:{language:"apex",code:'\n//commons.js \nexport * from "./toastUtils"\nexport * from "./dateUtils"\nexport * from "./sObjectUtils"\n'}}),sb({path:"labels",label:"Custom Labels",description:"Organize custom labels in components.",iconName:"standard:code_playground",markdownUrl:"/labels.md",snippet:{language:"apex",code:"\n"}})]),ib("Utilities",[sb({path:"toasts",label:"Toasts",description:"Toasts one-liners.",iconName:"standard:code_playground",markdownUrl:"/toasts.md",snippet:{language:"apex",code:"\nToasts.showErrorToast(this, 'Something went wrong');\n"}})])]})]}],cb="assets/",db={setAssetsPath:function(e){e&&(cb=e)},getAssetsPath:function(){return String(cb)},setAppElement:function(e){e&&(lb=e,Mi().setAppElement(e))},getAppElement:function(){return lb}};db.setAppElement("#root");var pb=function(e,t){return Ae({basename:null==t?void 0:t.basename,future:Yt({},null==t?void 0:t.future,{v7_prependBasename:!0}),history:(n={window:null==t?void 0:t.window},void 0===n&&(n={}),$((function(e,t){var n=e.location;return B("",{pathname:n.pathname,search:n.search,hash:n.hash},t.state&&t.state.usr||null,t.state&&t.state.key||"default")}),(function(e,t){return"string"===typeof t?t:j(t)}),null,n)),hydrationData:(null==t?void 0:t.hydrationData)||Zt(),routes:e,mapRouteProperties:qt}).initialize();var n}(ub);i.createRoot(document.getElementById("root")).render((0,Vl.jsx)(a.StrictMode,{children:(0,Vl.jsx)(nr,{iconPath:"/icons",children:(0,Vl.jsx)($t,{router:pb})})}))}()}(); \ No newline at end of file +/*! For license information please see main.3658b9f6.js.LICENSE.txt */ +!function(){var e={3512:function(e){e.exports={"global-setup":"#2A739E","service-cloud":"#7f2443","industry-cloud":"#4c2248","sales-cloud":"#00857d","commerce-cloud":"#41693d","community-cloud":"#ffc20e","marketing-cloud":"#ea7600",quip:"#cf451d"}},6185:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.bodyOpenClassName=t.portalClassName=void 0;var r=Object.assign||function(e){for(var t=1;t0&&0===(y-=1)&&c.show(t),n.props.shouldFocusAfterRender&&(n.props.shouldReturnFocusAfterClose?(l.returnFocus(n.props.preventScroll),l.teardownScopedFocus()):l.popWithoutFocus()),n.props.onAfterClose&&n.props.onAfterClose(),m.default.deregister(n)},n.open=function(){n.beforeOpen(),n.state.afterOpen&&n.state.beforeClose?(clearTimeout(n.closeTimer),n.setState({beforeClose:!1})):(n.props.shouldFocusAfterRender&&(l.setupScopedFocus(n.node),l.markForFocusLater()),n.setState({isOpen:!0},(function(){n.openAnimationFrame=requestAnimationFrame((function(){n.setState({afterOpen:!0}),n.props.isOpen&&n.props.onAfterOpen&&n.props.onAfterOpen({overlayEl:n.overlay,contentEl:n.content})}))})))},n.close=function(){n.props.closeTimeoutMS>0?n.closeWithTimeout():n.closeWithoutTimeout()},n.focusContent=function(){return n.content&&!n.contentHasFocus()&&n.content.focus({preventScroll:!0})},n.closeWithTimeout=function(){var e=Date.now()+n.props.closeTimeoutMS;n.setState({beforeClose:!0,closesAt:e},(function(){n.closeTimer=setTimeout(n.closeWithoutTimeout,n.state.closesAt-Date.now())}))},n.closeWithoutTimeout=function(){n.setState({beforeClose:!1,isOpen:!1,afterOpen:!1,closesAt:null},n.afterClose)},n.handleKeyDown=function(e){9===e.keyCode&&(0,u.default)(n.content,e),n.props.shouldCloseOnEsc&&27===e.keyCode&&(e.stopPropagation(),n.requestClose(e))},n.handleOverlayOnClick=function(e){null===n.shouldClose&&(n.shouldClose=!0),n.shouldClose&&n.props.shouldCloseOnOverlayClick&&(n.ownerHandlesClose()?n.requestClose(e):n.focusContent()),n.shouldClose=null},n.handleContentOnMouseUp=function(){n.shouldClose=!1},n.handleOverlayOnMouseDown=function(e){n.props.shouldCloseOnOverlayClick||e.target!=n.overlay||e.preventDefault()},n.handleContentOnClick=function(){n.shouldClose=!1},n.handleContentOnMouseDown=function(){n.shouldClose=!1},n.requestClose=function(e){return n.ownerHandlesClose()&&n.props.onRequestClose(e)},n.ownerHandlesClose=function(){return n.props.onRequestClose},n.shouldBeClosed=function(){return!n.state.isOpen&&!n.state.beforeClose},n.contentHasFocus=function(){return document.activeElement===n.content||n.content.contains(document.activeElement)},n.buildClassName=function(e,t){var r="object"===("undefined"===typeof t?"undefined":a(t))?t:{base:b[e],afterOpen:b[e]+"--after-open",beforeClose:b[e]+"--before-close"},o=r.base;return n.state.afterOpen&&(o=o+" "+r.afterOpen),n.state.beforeClose&&(o=o+" "+r.beforeClose),"string"===typeof t&&t?o+" "+t:o},n.attributesFromObject=function(e,t){return Object.keys(t).reduce((function(n,r){return n[e+"-"+r]=t[r],n}),{})},n.state={afterOpen:!1,beforeClose:!1},n.shouldClose=null,n.moveFromContentToOverlay=null,n}return function(e,t){if("function"!==typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),o(t,[{key:"componentDidMount",value:function(){this.props.isOpen&&this.open()}},{key:"componentDidUpdate",value:function(e,t){this.props.isOpen&&!e.isOpen?this.open():!this.props.isOpen&&e.isOpen&&this.close(),this.props.shouldFocusAfterRender&&this.state.isOpen&&!t.isOpen&&this.focusContent()}},{key:"componentWillUnmount",value:function(){this.state.isOpen&&this.afterClose(),clearTimeout(this.closeTimer),cancelAnimationFrame(this.openAnimationFrame)}},{key:"beforeOpen",value:function(){var e=this.props,t=e.appElement,n=e.ariaHideApp,r=e.htmlOpenClassName,a=e.bodyOpenClassName;a&&d.add(document.body,a),r&&d.add(document.getElementsByTagName("html")[0],r),n&&(y+=1,c.hide(t)),m.default.register(this)}},{key:"render",value:function(){var e=this.props,t=e.id,n=e.className,a=e.overlayClassName,o=e.defaultStyles,i=e.children,s=n?{}:o.content,l=a?{}:o.overlay;if(this.shouldBeClosed())return null;var u={ref:this.setOverlayRef,className:this.buildClassName("overlay",a),style:r({},l,this.props.style.overlay),onClick:this.handleOverlayOnClick,onMouseDown:this.handleOverlayOnMouseDown},c=r({id:t,ref:this.setContentRef,style:r({},s,this.props.style.content),className:this.buildClassName("content",n),tabIndex:"-1",onKeyDown:this.handleKeyDown,onMouseDown:this.handleContentOnMouseDown,onMouseUp:this.handleContentOnMouseUp,onClick:this.handleContentOnClick,role:this.props.role,"aria-label":this.props.contentLabel},this.attributesFromObject("aria",r({modal:!0},this.props.aria)),this.attributesFromObject("data",this.props.data||{}),{"data-testid":this.props.testId}),d=this.props.contentElement(c,i);return this.props.overlayElement(u,d)}}]),t}(i.Component);v.defaultProps={style:{overlay:{},content:{}},defaultStyles:{}},v.propTypes={isOpen:s.default.bool.isRequired,defaultStyles:s.default.shape({content:s.default.object,overlay:s.default.object}),style:s.default.shape({content:s.default.object,overlay:s.default.object}),className:s.default.oneOfType([s.default.string,s.default.object]),overlayClassName:s.default.oneOfType([s.default.string,s.default.object]),bodyOpenClassName:s.default.string,htmlOpenClassName:s.default.string,ariaHideApp:s.default.bool,appElement:s.default.oneOfType([s.default.instanceOf(f.default),s.default.instanceOf(p.SafeHTMLCollection),s.default.instanceOf(p.SafeNodeList),s.default.arrayOf(s.default.instanceOf(f.default))]),onAfterOpen:s.default.func,onAfterClose:s.default.func,onRequestClose:s.default.func,closeTimeoutMS:s.default.number,shouldFocusAfterRender:s.default.bool,shouldCloseOnOverlayClick:s.default.bool,shouldReturnFocusAfterClose:s.default.bool,preventScroll:s.default.bool,role:s.default.string,contentLabel:s.default.string,aria:s.default.object,data:s.default.object,children:s.default.node,shouldCloseOnEsc:s.default.bool,overlayRef:s.default.func,contentRef:s.default.func,id:s.default.string,overlayElement:s.default.func,contentElement:s.default.func,testId:s.default.string},t.default=v,e.exports=t.default},978:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.resetState=function(){s&&(s.removeAttribute?s.removeAttribute("aria-hidden"):null!=s.length?s.forEach((function(e){return e.removeAttribute("aria-hidden")})):document.querySelectorAll(s).forEach((function(e){return e.removeAttribute("aria-hidden")})));s=null},t.log=function(){return void 0;var e=s||{};console.log("ariaAppHider ----------"),console.log(e.nodeName,e.className,e.id),console.log("end ariaAppHider ----------")},t.assertNodeList=l,t.setElement=function(e){var t=e;if("string"===typeof t&&i.canUseDOM){var n=document.querySelectorAll(t);l(n,t),t=n}return s=t||s},t.validateElement=u,t.hide=function(e){var t=!0,n=!1,r=void 0;try{for(var a,o=u(e)[Symbol.iterator]();!(t=(a=o.next()).done);t=!0){a.value.setAttribute("aria-hidden","true")}}catch(i){n=!0,r=i}finally{try{!t&&o.return&&o.return()}finally{if(n)throw r}}},t.show=function(e){var t=!0,n=!1,r=void 0;try{for(var a,o=u(e)[Symbol.iterator]();!(t=(a=o.next()).done);t=!0){a.value.removeAttribute("aria-hidden")}}catch(i){n=!0,r=i}finally{try{!t&&o.return&&o.return()}finally{if(n)throw r}}},t.documentNotReadyOrSSRTesting=function(){s=null};var r,a=n(1024),o=(r=a)&&r.__esModule?r:{default:r},i=n(592);var s=null;function l(e,t){if(!e||!e.length)throw new Error("react-modal: No elements were found for selector "+t+".")}function u(e){var t=e||s;return t?Array.isArray(t)||t instanceof HTMLCollection||t instanceof NodeList?t:[t]:((0,o.default)(!1,["react-modal: App element is not defined.","Please use `Modal.setAppElement(el)` or set `appElement={el}`.","This is needed so screen readers don't see main content","when modal is opened. It is not recommended, but you can opt-out","by setting `ariaHideApp={false}`."].join(" ")),[])}},3318:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.resetState=function(){for(var e=[i,s],t=0;t0?(document.body.firstChild!==i&&document.body.insertBefore(i,document.body.firstChild),document.body.lastChild!==s&&document.body.appendChild(s)):(i.parentElement&&i.parentElement.removeChild(i),s.parentElement&&s.parentElement.removeChild(s))}))},6373:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.resetState=function(){var e=document.getElementsByTagName("html")[0];for(var t in n)a(e,n[t]);var o=document.body;for(var i in r)a(o,r[i]);n={},r={}},t.log=function(){return void 0;var e=document.getElementsByTagName("html")[0].className,t="Show tracked classes:\n\n";for(var a in t+=" ("+e+"):\n",n)t+=" "+a+" "+n[a]+"\n";for(var o in e=document.body.className,t+="\n\ndoc.body ("+e+"):\n",r)t+=" "+o+" "+r[o]+"\n";t+="\n",console.log(t)};var n={},r={};function a(e,t){e.classList.remove(t)}t.add=function(e,t){return a=e.classList,o="html"==e.nodeName.toLowerCase()?n:r,void t.split(" ").forEach((function(e){!function(e,t){e[t]||(e[t]=0),e[t]+=1}(o,e),a.add(e)}));var a,o},t.remove=function(e,t){return a=e.classList,o="html"==e.nodeName.toLowerCase()?n:r,void t.split(" ").forEach((function(e){!function(e,t){e[t]&&(e[t]-=1)}(o,e),0===o[e]&&a.remove(e)}));var a,o}},6983:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.resetState=function(){i=[]},t.log=function(){return void 0;console.log("focusManager ----------"),i.forEach((function(e){var t=e||{};console.log(t.nodeName,t.className,t.id)})),console.log("end focusManager ----------")},t.handleBlur=u,t.handleFocus=c,t.markForFocusLater=function(){i.push(document.activeElement)},t.returnFocus=function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=null;try{return void(0!==i.length&&(t=i.pop()).focus({preventScroll:e}))}catch(n){console.warn(["You tried to return focus to",t,"but it is not in the DOM anymore"].join(" "))}},t.popWithoutFocus=function(){i.length>0&&i.pop()},t.setupScopedFocus=function(e){s=e,window.addEventListener?(window.addEventListener("blur",u,!1),document.addEventListener("focus",c,!0)):(window.attachEvent("onBlur",u),document.attachEvent("onFocus",c))},t.teardownScopedFocus=function(){s=null,window.addEventListener?(window.removeEventListener("blur",u),document.removeEventListener("focus",c)):(window.detachEvent("onBlur",u),document.detachEvent("onFocus",c))};var r,a=n(1227),o=(r=a)&&r.__esModule?r:{default:r};var i=[],s=null,l=!1;function u(){l=!0}function c(){if(l){if(l=!1,!s)return;setTimeout((function(){s.contains(document.activeElement)||((0,o.default)(s)[0]||s).focus()}),0)}}},3754:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.log=function(){console.log("portalOpenInstances ----------"),console.log(r.openInstances.length),r.openInstances.forEach((function(e){return console.log(e)})),console.log("end portalOpenInstances ----------")},t.resetState=function(){r=new n};var n=function e(){var t=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.register=function(e){-1===t.openInstances.indexOf(e)&&(t.openInstances.push(e),t.emit("register"))},this.deregister=function(e){var n=t.openInstances.indexOf(e);-1!==n&&(t.openInstances.splice(n,1),t.emit("deregister"))},this.subscribe=function(e){t.subscribers.push(e)},this.emit=function(e){t.subscribers.forEach((function(n){return n(e,t.openInstances.slice())}))},this.openInstances=[],this.subscribers=[]},r=new n;t.default=r},592:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.canUseDOM=t.SafeNodeList=t.SafeHTMLCollection=void 0;var r,a=n(5538);var o=((r=a)&&r.__esModule?r:{default:r}).default,i=o.canUseDOM?window.HTMLElement:{};t.SafeHTMLCollection=o.canUseDOM?window.HTMLCollection:{},t.SafeNodeList=o.canUseDOM?window.NodeList:{},t.canUseDOM=o.canUseDOM;t.default=i},1009:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){var n=(0,o.default)(e);if(!n.length)return void t.preventDefault();var r=void 0,a=t.shiftKey,i=n[0],s=n[n.length-1];if(e===document.activeElement){if(!a)return;r=s}s!==document.activeElement||a||(r=i);i===document.activeElement&&a&&(r=s);if(r)return t.preventDefault(),void r.focus();var l=/(\bChrome\b|\bSafari\b)\//.exec(navigator.userAgent);if(null==l||"Chrome"==l[1]||null!=/\biPod\b|\biPad\b/g.exec(navigator.userAgent))return;var u=n.indexOf(document.activeElement);u>-1&&(u+=a?-1:1);if("undefined"===typeof(r=n[u]))return t.preventDefault(),void(r=a?s:i).focus();t.preventDefault(),r.focus()};var r,a=n(1227),o=(r=a)&&r.__esModule?r:{default:r};e.exports=t.default},1227:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return[].slice.call(e.querySelectorAll("*"),0).filter(o)};var n=/input|select|textarea|button|object/;function r(e){var t=e.offsetWidth<=0&&e.offsetHeight<=0;if(t&&!e.innerHTML)return!0;try{var n=window.getComputedStyle(e);return t?"visible"!==n.getPropertyValue("overflow")||e.scrollWidth<=0&&e.scrollHeight<=0:"none"==n.getPropertyValue("display")}catch(r){return console.warn("Failed to inspect element style"),!1}}function a(e,t){var a=e.nodeName.toLowerCase();return(n.test(a)&&!e.disabled||"a"===a&&e.href||t)&&function(e){for(var t=e;t&&t!==document.body;){if(r(t))return!1;t=t.parentNode}return!0}(e)}function o(e){var t=e.getAttribute("tabindex");null===t&&(t=void 0);var n=isNaN(t);return(n||t>=0)&&a(e,!n)}e.exports=t.default},5196:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,a=n(6185),o=(r=a)&&r.__esModule?r:{default:r};t.default=o.default,e.exports=t.default},6123:function(e,t){var n;!function(){"use strict";var r={}.hasOwnProperty;function a(){for(var e=[],t=0;t4&&n.slice(0,4)===i&&s.test(t)&&("-"===t.charAt(4)?p=function(e){var t=e.slice(5).replace(l,d);return i+t.charAt(0).toUpperCase()+t.slice(1)}(t):t=function(e){var t=e.slice(4);if(l.test(t))return e;t=t.replace(u,c),"-"!==t.charAt(0)&&(t="-"+t);return i+t}(t),f=a);return new f(p,t)};var s=/^data[-\w.:]+$/i,l=/-[a-z]/g,u=/[A-Z]/g;function c(e){return"-"+e.toLowerCase()}function d(e){return e.charAt(1).toUpperCase()}},3261:function(e,t,n){"use strict";var r=n(7883),a=n(748),o=n(6885),i=n(3009),s=n(8470),l=n(6134);e.exports=r([o,a,i,s,l])},8470:function(e,t,n){"use strict";var r=n(8118),a=n(6512),o=r.booleanish,i=r.number,s=r.spaceSeparated;e.exports=a({transform:function(e,t){return"role"===t?t:"aria-"+t.slice(4).toLowerCase()},properties:{ariaActiveDescendant:null,ariaAtomic:o,ariaAutoComplete:null,ariaBusy:o,ariaChecked:o,ariaColCount:i,ariaColIndex:i,ariaColSpan:i,ariaControls:s,ariaCurrent:null,ariaDescribedBy:s,ariaDetails:null,ariaDisabled:o,ariaDropEffect:s,ariaErrorMessage:null,ariaExpanded:o,ariaFlowTo:s,ariaGrabbed:o,ariaHasPopup:null,ariaHidden:o,ariaInvalid:null,ariaKeyShortcuts:null,ariaLabel:null,ariaLabelledBy:s,ariaLevel:i,ariaLive:null,ariaModal:o,ariaMultiLine:o,ariaMultiSelectable:o,ariaOrientation:null,ariaOwns:s,ariaPlaceholder:null,ariaPosInSet:i,ariaPressed:o,ariaReadOnly:o,ariaRelevant:null,ariaRequired:o,ariaRoleDescription:s,ariaRowCount:i,ariaRowIndex:i,ariaRowSpan:i,ariaSelected:o,ariaSetSize:i,ariaSort:null,ariaValueMax:i,ariaValueMin:i,ariaValueNow:i,ariaValueText:null,role:null}})},6134:function(e,t,n){"use strict";var r=n(8118),a=n(6512),o=n(7136),i=r.boolean,s=r.overloadedBoolean,l=r.booleanish,u=r.number,c=r.spaceSeparated,d=r.commaSeparated;e.exports=a({space:"html",attributes:{acceptcharset:"accept-charset",classname:"class",htmlfor:"for",httpequiv:"http-equiv"},transform:o,mustUseProperty:["checked","multiple","muted","selected"],properties:{abbr:null,accept:d,acceptCharset:c,accessKey:c,action:null,allow:null,allowFullScreen:i,allowPaymentRequest:i,allowUserMedia:i,alt:null,as:null,async:i,autoCapitalize:null,autoComplete:c,autoFocus:i,autoPlay:i,capture:i,charSet:null,checked:i,cite:null,className:c,cols:u,colSpan:null,content:null,contentEditable:l,controls:i,controlsList:c,coords:u|d,crossOrigin:null,data:null,dateTime:null,decoding:null,default:i,defer:i,dir:null,dirName:null,disabled:i,download:s,draggable:l,encType:null,enterKeyHint:null,form:null,formAction:null,formEncType:null,formMethod:null,formNoValidate:i,formTarget:null,headers:c,height:u,hidden:i,high:u,href:null,hrefLang:null,htmlFor:c,httpEquiv:c,id:null,imageSizes:null,imageSrcSet:d,inputMode:null,integrity:null,is:null,isMap:i,itemId:null,itemProp:c,itemRef:c,itemScope:i,itemType:c,kind:null,label:null,lang:null,language:null,list:null,loading:null,loop:i,low:u,manifest:null,max:null,maxLength:u,media:null,method:null,min:null,minLength:u,multiple:i,muted:i,name:null,nonce:null,noModule:i,noValidate:i,onAbort:null,onAfterPrint:null,onAuxClick:null,onBeforePrint:null,onBeforeUnload:null,onBlur:null,onCancel:null,onCanPlay:null,onCanPlayThrough:null,onChange:null,onClick:null,onClose:null,onContextMenu:null,onCopy:null,onCueChange:null,onCut:null,onDblClick:null,onDrag:null,onDragEnd:null,onDragEnter:null,onDragExit:null,onDragLeave:null,onDragOver:null,onDragStart:null,onDrop:null,onDurationChange:null,onEmptied:null,onEnded:null,onError:null,onFocus:null,onFormData:null,onHashChange:null,onInput:null,onInvalid:null,onKeyDown:null,onKeyPress:null,onKeyUp:null,onLanguageChange:null,onLoad:null,onLoadedData:null,onLoadedMetadata:null,onLoadEnd:null,onLoadStart:null,onMessage:null,onMessageError:null,onMouseDown:null,onMouseEnter:null,onMouseLeave:null,onMouseMove:null,onMouseOut:null,onMouseOver:null,onMouseUp:null,onOffline:null,onOnline:null,onPageHide:null,onPageShow:null,onPaste:null,onPause:null,onPlay:null,onPlaying:null,onPopState:null,onProgress:null,onRateChange:null,onRejectionHandled:null,onReset:null,onResize:null,onScroll:null,onSecurityPolicyViolation:null,onSeeked:null,onSeeking:null,onSelect:null,onSlotChange:null,onStalled:null,onStorage:null,onSubmit:null,onSuspend:null,onTimeUpdate:null,onToggle:null,onUnhandledRejection:null,onUnload:null,onVolumeChange:null,onWaiting:null,onWheel:null,open:i,optimum:u,pattern:null,ping:c,placeholder:null,playsInline:i,poster:null,preload:null,readOnly:i,referrerPolicy:null,rel:c,required:i,reversed:i,rows:u,rowSpan:u,sandbox:c,scope:null,scoped:i,seamless:i,selected:i,shape:null,size:u,sizes:null,slot:null,span:u,spellCheck:l,src:null,srcDoc:null,srcLang:null,srcSet:d,start:u,step:null,style:null,tabIndex:u,target:null,title:null,translate:null,type:null,typeMustMatch:i,useMap:null,value:l,width:u,wrap:null,align:null,aLink:null,archive:c,axis:null,background:null,bgColor:null,border:u,borderColor:null,bottomMargin:u,cellPadding:null,cellSpacing:null,char:null,charOff:null,classId:null,clear:null,code:null,codeBase:null,codeType:null,color:null,compact:i,declare:i,event:null,face:null,frame:null,frameBorder:null,hSpace:u,leftMargin:u,link:null,longDesc:null,lowSrc:null,marginHeight:u,marginWidth:u,noResize:i,noHref:i,noShade:i,noWrap:i,object:null,profile:null,prompt:null,rev:null,rightMargin:u,rules:null,scheme:null,scrolling:l,standby:null,summary:null,text:null,topMargin:u,valueType:null,version:null,vAlign:null,vLink:null,vSpace:u,allowTransparency:null,autoCorrect:null,autoSave:null,disablePictureInPicture:i,disableRemotePlayback:i,prefix:null,property:null,results:u,security:null,unselectable:null}})},7136:function(e,t,n){"use strict";var r=n(1989);e.exports=function(e,t){return r(e,t.toLowerCase())}},1989:function(e){"use strict";e.exports=function(e,t){return t in e?e[t]:t}},6512:function(e,t,n){"use strict";var r=n(8134),a=n(8815),o=n(4388);e.exports=function(e){var t,n,i=e.space,s=e.mustUseProperty||[],l=e.attributes||{},u=e.properties,c=e.transform,d={},p={};for(t in u)n=new o(t,c(l,t),u[t],i),-1!==s.indexOf(t)&&(n.mustUseProperty=!0),d[t]=n,p[r(t)]=t,p[r(n.attribute)]=t;return new a(d,p,i)}},4388:function(e,t,n){"use strict";var r=n(6721),a=n(8118);e.exports=s,s.prototype=new r,s.prototype.defined=!0;var o=["boolean","booleanish","overloadedBoolean","number","commaSeparated","spaceSeparated","commaOrSpaceSeparated"],i=o.length;function s(e,t,n,s){var u,c=-1;for(l(this,"space",s),r.call(this,e,t);++c=97&&t<=122||t>=65&&t<=90}},9319:function(e,t,n){"use strict";var r=n(7631),a=n(4597);e.exports=function(e){return r(e)||a(e)}},3110:function(e){e.exports=function(e){return null!=e&&null!=e.constructor&&"function"===typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}},4597:function(e){"use strict";e.exports=function(e){var t="string"===typeof e?e.charCodeAt(0):e;return t>=48&&t<=57}},6422:function(e){"use strict";e.exports=function(e){var t="string"===typeof e?e.charCodeAt(0):e;return t>=97&&t<=102||t>=65&&t<=70||t>=48&&t<=57}},3108:function(e,t,n){e=n.nmd(e);var r="__lodash_hash_undefined__",a=1,o=2,i=9007199254740991,s="[object Arguments]",l="[object Array]",u="[object AsyncFunction]",c="[object Boolean]",d="[object Date]",p="[object Error]",f="[object Function]",m="[object GeneratorFunction]",g="[object Map]",h="[object Number]",b="[object Null]",y="[object Object]",v="[object Promise]",E="[object Proxy]",S="[object RegExp]",w="[object Set]",k="[object String]",T="[object Symbol]",_="[object Undefined]",x="[object WeakMap]",A="[object ArrayBuffer]",O="[object DataView]",N=/^\[object .+?Constructor\]$/,C=/^(?:0|[1-9]\d*)$/,R={};R["[object Float32Array]"]=R["[object Float64Array]"]=R["[object Int8Array]"]=R["[object Int16Array]"]=R["[object Int32Array]"]=R["[object Uint8Array]"]=R["[object Uint8ClampedArray]"]=R["[object Uint16Array]"]=R["[object Uint32Array]"]=!0,R[s]=R[l]=R[A]=R[c]=R[O]=R[d]=R[p]=R[f]=R[g]=R[h]=R[y]=R[S]=R[w]=R[k]=R[x]=!1;var I="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,L="object"==typeof self&&self&&self.Object===Object&&self,D=I||L||Function("return this")(),P=t&&!t.nodeType&&t,M=P&&e&&!e.nodeType&&e,F=M&&M.exports===P,U=F&&I.process,B=function(){try{return U&&U.binding&&U.binding("util")}catch(e){}}(),j=B&&B.isTypedArray;function z(e,t){for(var n=-1,r=null==e?0:e.length;++nu))return!1;var d=s.get(e);if(d&&s.get(t))return d==t;var p=-1,f=!0,m=n&o?new xe:void 0;for(s.set(e,t),s.set(t,e);++p-1},Te.prototype.set=function(e,t){var n=this.__data__,r=Ne(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this},_e.prototype.clear=function(){this.size=0,this.__data__={hash:new ke,map:new(de||Te),string:new ke}},_e.prototype.delete=function(e){var t=Fe(this,e).delete(e);return this.size-=t?1:0,t},_e.prototype.get=function(e){return Fe(this,e).get(e)},_e.prototype.has=function(e){return Fe(this,e).has(e)},_e.prototype.set=function(e,t){var n=Fe(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this},xe.prototype.add=xe.prototype.push=function(e){return this.__data__.set(e,r),this},xe.prototype.has=function(e){return this.__data__.has(e)},Ae.prototype.clear=function(){this.__data__=new Te,this.size=0},Ae.prototype.delete=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n},Ae.prototype.get=function(e){return this.__data__.get(e)},Ae.prototype.has=function(e){return this.__data__.has(e)},Ae.prototype.set=function(e,t){var n=this.__data__;if(n instanceof Te){var r=n.__data__;if(!de||r.length<199)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new _e(r)}return n.set(e,t),this.size=n.size,this};var Be=se?function(e){return null==e?[]:(e=Object(e),function(e,t){for(var n=-1,r=null==e?0:e.length,a=0,o=[];++n-1&&e%1==0&&e-1&&e%1==0&&e<=i}function Ke(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}function Xe(e){return null!=e&&"object"==typeof e}var Ze=j?function(e){return function(t){return e(t)}}(j):function(e){return Xe(e)&&Ye(e.length)&&!!R[Ce(e)]};function Qe(e){return null!=(t=e)&&Ye(t.length)&&!qe(t)?Oe(e):De(e);var t}e.exports=function(e,t){return Ie(e,t)}},6174:function(e,t,n){var r="[object Null]",a="[object Undefined]",o="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,i="object"==typeof self&&self&&self.Object===Object&&self,s=o||i||Function("return this")(),l=Object.prototype,u=l.hasOwnProperty,c=l.toString,d=s.Symbol,p=d?d.toStringTag:void 0;function f(e){return null==e?void 0===e?a:r:p&&p in Object(e)?function(e){var t=u.call(e,p),n=e[p];try{e[p]=void 0;var r=!0}catch(o){}var a=c.call(e);r&&(t?e[p]=n:delete e[p]);return a}(e):function(e){return c.call(e)}(e)}e.exports=function(e){if(!function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}(e))return!1;var t=f(e);return"[object Function]"==t||"[object GeneratorFunction]"==t||"[object AsyncFunction]"==t||"[object Proxy]"==t}},1843:function(e){"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(a){return!1}}()?Object.assign:function(e,a){for(var o,i,s=function(e){if(null===e||void 0===e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(e),l=1;l65535&&(V+=c((z-=65536)>>>10|55296),z=56320|1023&z),z=V+c(z))):K!==_&&G(L,J)),z?(Ee(),Z=ye(),pe=ee-1,me+=ee-Y+1,be.push(z),(Q=ye()).offset++,ae&&ae.call(se,z,{start:Z,end:Q},e.slice(Y-1,ee)),Z=Q):(p=e.slice(Y-1,ee),he+=p,me+=p.length,pe=ee-1)}else 10===j&&(ge++,fe++,me=0),j===j?(he+=c(j),me++):Ee();return be.join("");function ye(){return{line:ge,column:me,offset:pe+(ue.offset||0)}}function ve(e,t){var n=ye();n.column+=t,n.offset+=t,oe.call(le,F[e],n,e)}function Ee(){he&&(be.push(he),re&&re.call(ie,he,{start:Z,end:ye()}),he="")}}(e,i)};var u={}.hasOwnProperty,c=String.fromCharCode,d=Function.prototype,p={warning:null,reference:null,text:null,warningContext:null,referenceContext:null,textContext:null,position:{},additional:null,attribute:!1,nonTerminated:!0},f=9,m=10,g=12,h=32,b=38,y=59,v=60,E=61,S=35,w=88,k=120,T=65533,_="named",x="hexadecimal",A="decimal",O={};O[x]=16,O[A]=10;var N={};N[_]=s,N[A]=o,N[x]=i;var C=1,R=2,I=3,L=4,D=5,P=6,M=7,F={};function U(e){return e>=55296&&e<=57343||e>1114111}function B(e){return e>=1&&e<=8||11===e||e>=13&&e<=31||e>=127&&e<=159||e>=64976&&e<=65007||65535===(65535&e)||65534===(65535&e)}F[C]="Named character references must be terminated by a semicolon",F[R]="Numeric character references must be terminated by a semicolon",F[I]="Named character references cannot be empty",F[L]="Numeric character references cannot be empty",F[D]="Named character references must be known",F[P]="Numeric character references cannot be disallowed",F[M]="Numeric character references cannot be outside the permissible Unicode range"},1729:function(e,t,n){"use strict";var r=n(9165);function a(){}function o(){}o.resetWarningCache=a,e.exports=function(){function e(e,t,n,a,o,i){if(i!==r){var s=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw s.name="Invariant Violation",s}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:o,resetWarningCache:a};return n.PropTypes=n,n}},5192:function(e,t,n){e.exports=n(1729)()},9165:function(e){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},534:function(e,t,n){"use strict";var r=n(7313),a=n(2224);function o(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n
"!==e.children[l].value;)u.children.push(e.children[l]),l++}else s="tab",a={type:"raw",children:[],data:{hProperties:{label:"",type:"tab",headers:[]}}},i.children.push(a)}Fg(r,"link",(function(e,t,n){n.children.splice(t,1),r.data.hProperties.buttons.push({url:e.url,label:e.children[0].value})})),e.children=[{type:"raw",children:[r,i],data:{hProperties:{type:"page"}}}]}}function Gg(e){return Gg="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Gg(e)}function Hg(e,t){for(var n=0;n=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var Ch=function(e){var t=e.className,n=e.children,r=e.variant,o=e.selected,i=e.id,s=e.tabId;Nh(e,["className","children","variant","selected","id","tabId"]);return a.createElement("div",{"aria-labelledby":s,className:ln()(t,{"slds-show":o,"slds-hide":!o,"slds-tabs_default__content":"default"===r,"slds-tabs_scoped__content":"scoped"===r,"slds-vertical-tabs__content":"vertical"===r}),id:i,role:"tabpanel"},n.props.children)};Ch.displayName="SLDSTabPanel",Ch.propTypes={children:on().oneOfType([on().array,on().object,on().string]),className:on().string,id:on().string,selected:on().bool,variant:on().oneOf(["default","scoped","vertical"]),tabId:on().string},Ch.defaultProps={variant:"default",selected:!1};var Rh=Ch;function Ih(e){return Ih="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Ih(e)}function Lh(e,t){for(var n=0;ne;)if(!Bh(this.getTab(t)))return t;return e}},{key:"getSelectedIndex",value:function(){return Number.isInteger(this.props.selectedIndex)?this.props.selectedIndex:this.state.selectedIndex}},{key:"getTab",value:function(e){return this.tabs[e].tab}},{key:"getTabNode",value:function(e){return this.tabs[e].node}},{key:"getTabsCount",value:function(){return this.props.children?a.Children.count(this.props.children):0}},{key:"getVariant",value:function(){return this.props.variant||"default"}},{key:"setSelected",value:function(e,t){if(!(e<0||e>=this.getTabsCount())){var n,r=this.getSelectedIndex();Di()(this.props.onSelect)&&(n=this.props.onSelect(e,r)),!1!==n&&e!==this.state.selectedIndex&&this.setState({selectedIndex:e,focus:!0===t})}}},{key:"isTabFromContainer",value:function(e){if(!function(e){return"A"===e.nodeName&&"tab"===e.getAttribute("role")||"LI"===e.nodeName&&(e.classList.contains("slds-tabs_default__item")||e.classList.contains("slds-tabs_scoped__item")||e.classList.contains("slds-vertical-tabs__nav-item"))}(e))return!1;var t=e.parentElement;do{if(t===this.tabsNode)return!0;if(t.getAttribute("data-tabs"))break;t=t.parentElement}while(t);return!1}},{key:"renderTabPanels",value:function(e){var t,n=this,r=a.Children.toArray(this.props.children),o=this.getSelectedIndex();return t=r.map((function(t,i){var s="".concat(e,"-slds-tabs_tab-").concat(i),l="".concat(e,"-slds-tabs_panel-").concat(i),u=o===i,c=n.getVariant();return a.createElement(Rh,{key:t.key,selected:u,id:l,tabId:s,variant:c},r[i])})),t}},{key:"renderTabsList",value:function(e){var t=this,n=a.Children.toArray(this.props.children);return a.createElement(vh,{id:e,variant:this.getVariant()},n.map((function(n,r){var o="".concat(e,"-slds-tabs_tab-").concat(r),i="".concat(e,"-slds-tabs_panel-").concat(r),s=t.getSelectedIndex()===r,l=s&&t.state.focus,u=t.getVariant();return a.createElement(Oh,{key:n.key,ref:function(e){t.tabs[r]={tab:n,node:e},t.state.focus&&t.setState({focus:!1})},focus:l,selected:s,id:o,panelId:i,disabled:n.props.disabled,variant:u,hasError:n.props.hasError,assistiveText:n.props.assistiveText},n.props.label)})))}},{key:"render",value:function(){var e=this,t=this.props,n=t.className,r=t.id,o=void 0===r?this.generatedId:r,i=t.variant,s=void 0===i?this.getVariant:i;return a.createElement("div",{id:o,className:ln()({"slds-tabs_default":"default"===s,"slds-tabs_scoped":"scoped"===s,"slds-vertical-tabs":"vertical"===s},n),onClick:this.handleClick,onKeyDown:this.handleKeyDown,"data-tabs":!0,ref:function(t){e.tabsNode=t}},this.renderTabsList(o),this.renderTabPanels(o))}}],n&&Lh(t.prototype,n),r&&Lh(t,r),i}(a.Component);zh.displayName="SLDSTabs",zh.propTypes=jh,zh.defaultProps={defaultSelectedIndex:0,variant:"default"};var $h=zh,Gh=function(e){var t=e.children;return a.createElement("div",null,a.Children.toArray(t))};Gh.displayName="SLDSTabsPanel",Gh.propTypes={label:on().oneOfType([on().string,on().element]).isRequired,children:on().oneOfType([on().arrayOf(on().node),on().node,on().element]).isRequired,hasError:on().bool,assistiveText:on().shape({withErrorIcon:on().string})};var Hh=Gh;function Vh(e){var t=e.href,n=e.label;return(0,ql.jsx)("a",{className:"slds-button slds-button_neutral",target:"_blank",href:t,children:n})}var Wh=function(e){y(n,e);var t=w(n);function n(){return p(this,n),t.apply(this,arguments)}return h(n,[{key:"render",value:function(){var e=this.props,t=[];for(var n in e.items)e.items.hasOwnProperty(n)&&(t.push((0,ql.jsx)("dt",{className:"slds-item_label slds-text-color_weak slds-truncate",children:n},n+"_key")),t.push((0,ql.jsx)("dd",{className:"slds-item_detail slds-truncate",children:e.items[n]},n+"_val")));return(0,ql.jsxs)("article",{className:"slds-tile",children:[(0,ql.jsx)("h3",{className:"slds-tile__title slds-truncate",title:e.title,children:(0,ql.jsx)("a",{children:e.title})}),(0,ql.jsx)("div",{className:"slds-tile__detail",children:(0,ql.jsx)("dl",{className:"slds-list_horizontal slds-wrap",children:t})})]})}}]),n}(a.Component);function qh(e,t){if(e&&t){var n=[],r=e.properties.label;t.forEach((function(e){n.push(e),e.label===r&&n.push.apply(n,I(e.children))})),Zl("onTableOfContentsChange",{headers:n})}}function Yh(e){var t=e.buttons,n=Vl().width,r=d((0,a.useState)(!1),2),o=r[0],i=r[1];if(0===t.length)return null;var s=[],l=[];if(t.length>0){s.push((0,ql.jsx)(Vh,{href:t[0].url,label:t[0].label},t[0].label));for(var u=n,c=1;c600?(s.push((0,ql.jsx)(Vh,{href:p.url,label:p.label},p.label)),u-=150):l.push((0,ql.jsx)("li",{className:"slds-dropdown__item",role:"presentation",children:(0,ql.jsx)("a",{href:p.url,role:"menuitem",tabIndex:"0",children:(0,ql.jsx)("span",{className:"slds-truncate",title:p.label,children:p.label})})},p.url))}}return(0,ql.jsxs)("div",{className:"slds-button-group",role:"group",children:[s,l.length>0&&(0,ql.jsxs)("div",{className:"slds-dropdown-trigger slds-dropdown-trigger_click slds-button_last ".concat(o&&"slds-is-open"),children:[(0,ql.jsx)(Wo,{variant:"icon",iconCategory:"utility",iconName:"down",iconVariant:"border-filled",onClick:function(e){return i(!o)}}),(0,ql.jsx)("div",{className:"slds-dropdown slds-dropdown_right slds-dropdown_actions",children:(0,ql.jsx)("ul",{className:"slds-dropdown__list",role:"menu",children:l})})]})]})}function Kh(e){var t=e.config,n=e.header,r=e.tabs,o=e.buttons,i=d((0,a.useState)(0),2),s=i[0],l=i[1],u=n.node.properties.pageContent;(0,a.useEffect)((function(){var e;qh(r.node.children[0],u),e=function(e){l(e),qh(r.node.children[e],u)},Ql("onTabChangeRequest",0,(function(t){return e(t.detail.tabIndex)}))}),[r,n]);var c=t.iconName.split(":");return(0,ql.jsxs)("div",{children:[(0,ql.jsx)("div",{className:"slds-theme_default slds-box_border slds-m-bottom_medium",children:(0,ql.jsx)(bh,{heading:n.label,icon:(0,ql.jsx)(go,{category:c[0],name:c[1]}),bodyClassName:"slds-p-horizontal_small",headerActions:(0,ql.jsx)(Yh,{buttons:o}),children:(0,ql.jsxs)("div",{className:"slds-grid slds-wrap slds-gutters_small max-width-100",children:[(0,ql.jsx)(Kl,{isTrue:t.copyright,children:(0,ql.jsx)("div",{className:"slds-col",style:{width:"280px",maxWidth:"280px"},children:(0,ql.jsx)(Wh,{title:"",items:{License:t.license||"MIT",Copyright:t.copyright}})})}),(0,ql.jsx)("div",{className:"slds-col max-width-100",children:(0,ql.jsx)("div",{className:"slds-text-body_regular max-width-100",children:n.children})})]})})}),(0,ql.jsx)("div",{className:"slds-theme_default slds-box_border slds-box_darker",children:(0,ql.jsx)($h,{onSelect:function(e){l(e),qh(r.node.children[e],u)},selectedIndex:s,children:r.children.map((function(e,t){return(0,ql.jsx)(Hh,{id:"".concat(t),label:e.props.label,children:(0,ql.jsx)("div",{id:jg(e.props.label),className:"centered",children:(0,ql.jsx)("div",{className:"slds-p-horizontal_small",children:e.props.children})})},t)}))})})]})}var Xh=["node","inline","className","children"];function Zh(e){return e.toLowerCase().replace(" ","-")}function Qh(e){var t=e.config,n=d((0,a.useState)(),2),r=n[0],o=n[1];return(0,a.useEffect)((function(){var e;(e=t.markdownUrl,Xf[e]?Promise.resolve(Xf[e]):(Xf[e]=fetch(e).then((function(e){return e.text()})).then((function(t){return Xf[e]=t,t})),Xf[e])).then((function(e){return o(e)}));var n=document.getElementById("top");null===n||void 0===n||n.scrollIntoView({behavior:"instant",block:"start"})}),[t.markdownUrl]),r?(0,ql.jsx)(Kf,{children:r,remarkPlugins:[Ag,$g],rehypePlugins:[zg],components:{div:function(e){var n=e.type,r=e.node,a=e.children;if("page"===n){var o=a[0].props,i=a[1].props,s=o.node.properties.buttons;return(0,ql.jsx)(Kh,{config:t,header:o,tabs:i,buttons:s})}return"accordion"===n?(0,ql.jsx)(Vu,{collapsed:!0,label:r.properties.title,children:a}):a},h1:function(e){var t=e.children;return(0,ql.jsx)("h1",{id:Zh(t[0]),className:"slds-m-top_x-large slds-text-heading_large",children:t})},h2:function(e){var t=e.children;return(0,ql.jsx)("h2",{id:Zh(t[0]),className:"slds-m-top_x-large slds-text-heading_medium",children:t})},h3:function(e){var t=e.children;return(0,ql.jsx)("h3",{id:Zh(t[0]),className:"slds-m-top_large slds-text-heading_small",children:t})},h4:function(e){var t=e.children;return(0,ql.jsx)("h4",{className:"slds-m-top_large slds-text-title_caps",children:t})},h5:function(e){var t=e.children;return(0,ql.jsx)("h5",{className:"slds-m-top_large slds-text-title_bold",children:t})},h6:function(e){var t=e.children;return(0,ql.jsx)("h6",{className:"slds-m-top_large slds-text-title",children:t})},ul:function(e){var t=e.children;return(0,ql.jsx)("ul",{className:"slds-m-bottom_medium slds-list_dotted",children:t})},ol:function(e){var t=e.children;return(0,ql.jsx)("ul",{className:"slds-m-bottom_medium slds-list_ordered",children:t})},table:function(e){var t=e.children;return(0,ql.jsx)("div",{className:"slds-scrollable_x",children:(0,ql.jsx)("table",{className:"slds-box_border slds-table slds-table_bordered slds-table_col-bordered",children:t})})},pre:function(e){var t=e.children;return(0,ql.jsx)("pre",{className:"code",children:t})},code:function(e){var t,n=e.node,r=e.inline,a=e.className,o=e.children,i=ou(e,Xh),s=/language-(\w+)/.exec(a||""),l=null===(t=n.data)||void 0===t?void 0:t.meta,u=l?l.split("|").flatMap((function(e){return e?e.trim():[]})):[];return!r&&s?(0,ql.jsx)(Lu,{language:s[1],header:u[0],description:u[1],showLineNumbers:!0,code:String(o).replace(/\n$/,"")}):(0,ql.jsx)("code",au(au({},i),{},{className:a,children:o}))}}}):(0,ql.jsx)(xs,{})}function Jh(e){return Jh="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Jh(e)}function eb(e,t){for(var n=0;n contact = Query.Contacts\n\t.byAccountId(accountIds)\n\t.getList();\n"}}),cb({path:"trigger-handler",label:"Trigger Handler",description:"Orchestrator for Apex Trigger Logic.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2019",markdownUrl:"/TriggerHandler.md",snippet:{language:"apex",code:"\nbeforeInsert(List triggerNew, TriggerContext tc);\nafterInsert(List triggerNew, TriggerContext tc);\n\nbeforeUpdate(List triggerNew, TriggerContext tc);\nafterUpdate(List triggerNew, TriggerContext tc);\n"}}),cb({path:"trigger-handler-mdt",label:"Metadata Trigger Handler",description:"Custom Metadata-driven orchestrator for Apex Trigger Logic",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2019",markdownUrl:"/TriggerHandlerMdt.md",image:"/img/th-mdt.jpg"})]),ub("Unit Testing",[cb({path:"http-mock-router",label:"Http Mock Router",description:"Configuration-driven, endpoint pattern-based router for Http Mocks.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2018",markdownUrl:"/HttpCalloutMockRouter.md",image:"/img/http-router.jpg",snippet:{language:"apex",code:"Test.setMock(HttpCalloutMock.class, HttpMocks.config());"}}),cb({path:"mock",label:"Mocking",description:"Mock response of the class.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2023",markdownUrl:"/Mock.md",snippet:{language:"apex",code:" \nMock.response(AccountSelector.class, new Account());\n\nMock.response(AccountSelector.class, new Map{\n\t'getByOwnerId' => new Account(Name = 'My Account'),\n\t'getById#1' => new Account(Name = 'Test Account'),\n\t'getById#2' => new Account(Name = 'Another Account'),\n\t'getById#3' => new QueryException('List has no rows...')\n});\n"}}),cb({path:"test-data-builder",label:"Test Data Builder",description:"Setup test records for unit tests.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2019",markdownUrl:"/TestDataBuilder.md",snippet:{language:"apex",code:"\nnew TestDataBuilder()\n\t.create(5, 'SMB', new Account(Name = 'Business Account'))\n\t.create(new Account(Name = 'Test', BillingCountry = 'US'))\n\t.similarly(new Account(BillingCity = 'Austin'))\n\t.insertRecords();\n"}}),cb({path:"test-data-suite",label:"Test Data Suite",description:"Access records created in @TestSetup",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2021",markdownUrl:"/TestDataSuite.md",snippet:{language:"apex",code:"\n@IsTest\nstatic void myTest(){\n\tTestDataSuite suite;\n\tList accounts = suite.get(Account.SObjectType);\n\tUser testUser = (User) suite.get('testUser');\n}\n\n"}})]),ub("Utilities",[cb({path:"batch",label:"Batch Schedulable",description:"Schedule batches without implementing dedicated Schedulable.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2018",markdownUrl:"/BatchSchedulable.md",snippet:{language:"apex",code:"\nSystem.schedule('SObject Cleaner', cronExpr,\n\tnew BatchSchedulable(SObjectCleanerBatch.class)\n);\n"}}),cb({path:"scheduler",label:"Scheduler",description:"Shorthand methods for easy scheduling.",iconName:"standard:date_time",copyright:"Piotr Ko\u017cuchowski @2018",markdownUrl:"/Scheduler.md",snippet:{language:"apex",code:"\nScheduler.scheduleDaily(\n\t'Data Cleaner', 12, 00, \n\tnew DataCleaningSchedulable()\n);\n"}}),cb({path:"custom-metadata-service",label:"Custom Metadata Service",description:"Deploy Custom Metadata from Apex.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2020",markdownUrl:"/CustomMetadataService.md",snippet:{language:"apex",code:"\nCustomMetadataService.deploy(new List{\n\tnew Country__mdt(DeveloperName = 'USA'),\n\tnew Country__mdt(DeveloperName = 'France'),\n\tnew Country__mdt(DeveloperName = 'Poland')\n});\n"}}),cb({path:"database-service",label:"Database Service",description:"Issue and mock DMLs with configurable sharing and user mode.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2018",markdownUrl:"/DatabaseService.md",snippet:{language:"apex",code:"\nnew DatabaseService()\n\t.withoutSharing()\n\t.updateRecords(accounts); "}}),cb({path:"localization",label:"Localization",description:"Dynamically retrieve Custom Labels, Field and Picklist labels for given locale.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2020",markdownUrl:"/Localization.md",snippet:{language:"apex",code:"\nLocalization.getCustomLabelsWithLocale(new List{\n\t'COM_Toast_Success',\n\t'COM_Toast_Info'\n}, 'pl')"}}),cb({path:"picklist",label:"Picklist",description:"Access Picklist metadata.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2019",markdownUrl:"/Picklist.md",snippet:{language:"apex",code:"\nPicklist p = new Picklist(Account.Type);\np.getDefaultValue();\np.getValues();\np.getEntriesByControllingValue();\n"}}),cb({path:"runtime",label:"Runtime",description:"Reflective Apex Utility.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2019",markdownUrl:"/Runtime.md",snippet:{language:"apex",code:"\nObject o = new Account();\n\nRuntime.getType(o); // -> Account.class\nRuntime.getTypeName(o); // -> 'Account'\n"}}),cb({path:"xml-parser",label:"XML Parser",description:"Translate XML document into JSON or Apex class.",iconName:"standard:apex",copyright:"Piotr Ko\u017cuchowski @2023",markdownUrl:"/XmlParser.md",snippet:{language:"apex",code:"\nXmlParser parser = new XmlParser(xmlString);\n\nMap untypedMap = parser.getUntyped();\nProfile p = (Profile) parser.getAs(Profile.class, false);\n"}})])]}),lb({label:"LWC",path:"lwc",children:[ub("Patterns",[cb({path:"commons",label:"Commons Module",description:"Organize common utilities and methods.",iconName:"standard:code_playground",markdownUrl:"/commons.md",snippet:{language:"apex",code:'\n//commons.js \nexport * from "./toastUtils"\nexport * from "./dateUtils"\nexport * from "./sObjectUtils"\n'}}),cb({path:"labels",label:"Custom Labels",description:"Organize custom labels in components.",iconName:"standard:code_playground",markdownUrl:"/labels.md",snippet:{language:"apex",code:"\n"}})]),ub("Utilities",[cb({path:"toasts",label:"Toasts",description:"Toasts one-liners.",iconName:"standard:code_playground",markdownUrl:"/toasts.md",snippet:{language:"apex",code:"\nToasts.showErrorToast(this, 'Something went wrong');\n"}})])]})]}],fb="assets/",mb={setAssetsPath:function(e){e&&(fb=e)},getAssetsPath:function(){return String(fb)},setAppElement:function(e){e&&(db=e,Mi().setAppElement(e))},getAppElement:function(){return db}};mb.setAppElement("#root");var gb=function(e,t){return Ae({basename:null==t?void 0:t.basename,future:Yt({},null==t?void 0:t.future,{v7_prependBasename:!0}),history:(n={window:null==t?void 0:t.window},void 0===n&&(n={}),$((function(e,t){var n=e.location;return B("",{pathname:n.pathname,search:n.search,hash:n.hash},t.state&&t.state.usr||null,t.state&&t.state.key||"default")}),(function(e,t){return"string"===typeof t?t:j(t)}),null,n)),hydrationData:(null==t?void 0:t.hydrationData)||Zt(),routes:e,mapRouteProperties:qt}).initialize();var n}(pb);i.createRoot(document.getElementById("root")).render((0,ql.jsx)(a.StrictMode,{children:(0,ql.jsx)(nr,{iconPath:"/icons",children:(0,ql.jsx)($t,{router:gb})})}))}()}(); \ No newline at end of file diff --git a/docs/static/js/main.f6e8715a.js.LICENSE.txt b/docs/static/js/main.3658b9f6.js.LICENSE.txt similarity index 100% rename from docs/static/js/main.f6e8715a.js.LICENSE.txt rename to docs/static/js/main.3658b9f6.js.LICENSE.txt diff --git a/docs/toasts.md b/docs/toasts.md index 1371083..687d906 100644 --- a/docs/toasts.md +++ b/docs/toasts.md @@ -15,15 +15,15 @@ import {Toasts} from "c/commons"; export default class Mytab extends LightningElement { - handleClick(ev) { - try { - doSomething(); - Toasts.showSuccessToast(this, "Record Updated!"); + handleClick(ev) { + try { + doSomething(); + Toasts.showSuccessToast(this, "Record Updated!"); - } catch (e) { - Toasts.showUnexpectedErrorToast(this); - } - } + } catch (e) { + Toasts.showUnexpectedErrorToast(this); + } + } } ```