Releases: jongpie/NebulaFramework
SFDX Metadata Conversion
- Converted metadata to SFDX format
- Arranged metadata into subfolders
Query Builder Enhancements
- Add query caching in all query builder classes (#41)
- New:
ISObjectQueryBuilder.cacheResults();
- New:
IAggregateResultQueryBuilder.cacheResults();
- New:
ISearchQueryBuilderQueryBuilder.cacheResults();
- New:
- Add support for Summer 17's new SOSL 'WITH HIGHLIGHT' and 'WITH SPELL_CORRECTION' features (#55)
- New:
ISearchQueryBuilderQueryBuilder.withHighlight(Boolean);
- New:
ISearchQueryBuilderQueryBuilder.withSpellCorrection(Boolean);
- New:
- Add support for SOQL 'OFFSET', 'FOR REFERENCE' & 'FOR VIEW' features (#8)
- Renamed:
ISObjectQueryBuilder forUpdate(); - formally called setAsUpdate();
- New:
ISObjectQueryBuilder.offset(numberOfRowsToSkip);
- New:
ISObjectQueryBuilder forReference();
- New:
ISObjectQueryBuilder forView();
- Renamed:
- Upgraded all classes to API v40.0 (Summer '17 release)
New Query Builders
Query Builders
QueryBuilder.cls has been split into SObjectQueryBuilder.cls, AggregateResultQueryBuilder.cls and SearchQueryBuilder.cls
QueryBuilder.cls
- QueryBuilder.cls is now an abstract class. It only contains protected methods for shared logic that is used by SObjectQueryBuilder.cls, AggregateQueryBuilder.cls and SearchQueryBuilder.cls. QueryBuilder.cls should not be used directly anymore.
SObjectQueryBuilder.cls
A new builder class that generates dynamic SOQL queries & returns a list of SObject
-
Several ways to add fields to your query:
1. addFields(Schema.FieldSet fieldSet)
2. addFields(List fields)
3. addAllFields()
4. addAllStandardFields()
5. addAllCustomFields()
6. addAllReadableFields()
7. addAllEditableFields() -
Name fields for lookups are automatically added to your query. For example, adding the contact object's field AccountId (a lookup to account):
sobjectQueryBuilder.addField(Schema.Contact.AccountId)
will result in your query having 2 fields added
SELECT AccountId, Account.Name FROM Contact
-
New method for including children records
-
New method, getQueryLocator()
AggregateResultQueryBuilder.cls
A new builder class that generates dynamic SOQL queries & returns a list of AggregateResult
-
Several methods to add fields to group by
1. groupBy(QueryDate queryDate);
2. groupBy(Schema.FieldSet fieldSet);
3. groupBy(Schema.SObjectField groupByField);
4. groupBy(List<Schema.SObjectField> groupByFields); -
Several aggregate methods - each method is overloaded to allow you to provide the string to use for a field alias
1. avg(Schema.SObjectField sbojectField)
2. count(Schema.SObjectField sbojectField)
3. countDistinct(Schema.SObjectField sbojectField)
4. max(Schema.SObjectField sbojectField)
5. min(Schema.SObjectField sbojectField)
6. sum(Schema.SObjectField sbojectField)
Since this is the first round of implementing the class, not all aggregate result features (like 'GROUP BY ROLLUP') have been implemented, but more will be added in the future.
SearchQueryBuilder.cls - generates SOSL queries
A new builder class that generates dynamic SOSL search queries & returns a list of a list of SObjects (that's not a typo, it's a list of lists)
- Instances of ISObjectQueryBuilder can be used to determine which SObjects are returned in the results
// Scenario: Search leads and accounts for a specified search term
// Step 1: define your search term
String searchTerm = 'Acme';
// Step 2: create 1 SObjectQueryBuilder per SObject Type that you want to search. In this case, there is 1 for account, and 1 for lead
ISObjectQueryBuilder accountQuery = SObjectQueryBuilder(Schema.Account.SObjectType).filterBy(queryFilters));
ISObjectQueryBuilder leadQuery = SObjectQueryBuilder(Schema.Lead.SObjectType).filterBy(queryFilters));
List<ISObjectQueryBuilder> queryBuilders = new List<ISObjectQueryBuilder>{accountQuery, leadQuery};
// Step 3: Pass search term & the query builders for account & lead to SearchQueryBuilder
List<List<SObject>> searchResults = new SearchQueryBuilder(searchTerm, queryBuilders).getSearchResults();
QueryField.cls
- Used to dynamically generate field string for SObject fields, including parent fields.
// Convert an SObjectField to a QueryField
QueryField qf = new QueryField(Schema.Account.Id);
// Convert a parent object's SObjectField to a QueryField
// This is the equivalent of 'contact.Account.CreatedBy.User.Profile.Name'
List<Schema.SObjectField> fieldChain = new List<Schema.SObjectField>{
Schema.Contact.AccountId, Schema.Account.CreatedById, Schema.User.ProfileId, Schema.Profile.Name
};
QueryField parentField= new QueryField(fieldChain);
QueryFilter.cls
- Completely changed the methods available to handle 3 filtering scenarios
1. filterByField(QueryField queryField, QueryOperator operator, Object providedValue)
2. filterByQueryDate(QueryDate queryDateToFilter, QueryOperator operator, Integer providedValue)
3. filterBySubquery(QueryOperator inOrNotIn, Schema.SObjectField lookupFieldOnRelatedSObject)
4. filterBySubquery(Schema.SObjectField lookupField, QueryOperator inOrNotIn, Schema.SObjectField lookupFieldOnRelatedSObject) - Added support for specifying 'AND' and 'OR' statements in a query. To use, provide a list of query filters that you want to be wrapped as a set of 'AND' or 'OR' statements to the corresponding method
1. andFilterBy(List queryFilters)
2. orFilterBy(List queryFilters)
QueryDate.cls
- A new class that can be used in two ways
1. Filter on a date/datetime field, using date functions
QueryDate qd = QueryDate.CALENDAR_MONTH(Schema.Lead.CreatedDate);
QueryFilter filter = new QueryFilter().filterByQueryDate(qd, QueryOperator.EQUALS, 3);
sobjectQueryBuilder.filterBy(filter);
System.assertEquals(filter.getValue(), 'CALENDAR_MONTH(CreatedDate) = 3');
// In SOQL, it would be written as
List<Lead> leads = [SELECT Id
FROM Lead
WHERE CALENDAR_MONTH(CreatedDate) = 3')];
- Group by a date function in an aggregate result
QueryDate qd = QueryDate.CALENDAR_MONTH(Schema.Lead.CreatedDate);
aggregateResultQueryBuilder.groupBy(qd);
// In SOQL, it would be written as
List<AggregateResult> results = [SELECT CALENDAR_MONTH(CreatedDate)
FROM Lead
GROUP BY CALENDAR_MONTH(CreatedDate)];
QueryFilterScope.cls
- Added 3 new filter scope enums
1. DELEGATED
2. MY_TERRITORY - only works in orgs with territory management enabled
3. MY_TEAM_TERRITORY - only works in orgs with territory management enabled
SObject Repository
DML.cls
- All DML methods now return the corresponding list of database results - see IDML.cls. Previously, these were void methods. This change is inherited by SObjectRepository.cls as well (it extends DML.cls)
SObjectRepository.cls
- The member variable sobjectRepository.Query has been removed. Each method now instead initializes an instance of SObjectQueryBuilder.cls
- New member variable sobjectRepository.AggregateQuery, an instance of the new class AggregateResultQueryBuilder.cls - see 'Query Builders' section for more info
Record Types
SObjectRecordTypes.cls
- Now requires the abstract method
Schema.SObjectType getSObjectType()
to be implemented
Settings
- The custom setting NebulaRepositorySettings__c.object has been renamed to NebulaSObjectQueryBuilderSettings__c.object
Utilities
CollectionUtils.cls
- Added 3 new methods for working with collections
1. getLastItem
2. pop
3. splice
Environment.cls
- New property, NamespacePrefix
Documentation
- Started commenting classes & methods with ApexDoc. This will be an ongoing improvement.
First major release
New Features & Enhancements
-
New Scheduler.cls class to help simplify scheduling jobs
-
Created the interface ISObjectRecordTypes.cls
-
SObjectRepository.cls has a new constructor that will automatically add all fields to the query for the specified SObject type - essentially, it's 'select * from sobject'
Sample code for the simplest implementation of an account repository:
public class AccountRepository extends SObjectRepository { public AccountRepository() { // When only an SObject type is provided, all fields for the SObject are included in your queries super(Schema.Account.SObjectType); } }
Bugfixes
- Fixed a bug related to using maps & sets as query parameters
- Refactored some code based on Code Climate's initial analysis (mostly in QueryDateLiteral.cls & QueryOperator.cls)
Other
- Updated README.md
- Setup Code Climate
Added support for parent field filtering and including children queries
New Features & Enhancements
-
SObjectRepository.cls now uses QueryFilter.cls to handle filtering on fields from the base object, parent object and grandparent objects - the constructor used for QueryFilter.cls determines the type of filter
Sample code for a filter to get accounts created by active userspublic class AccountRepository extends SObjectRepository { public AccountRepository() { // When only an SObject type is provided, all fields for the SObject are included in your queries super(Schema.Account.SObjectType); } public List<Account> getAccountsCreatedToday() { return (List<Account>)this.Query // Create an instance of QueryFilter to apply any filters to the query .filterBy(new QueryFilter(Schema.Account.Status, QueryOperator.EQUALS, QueryDateLiteral.TODAY)) .getQueryResults(); } }
-
Children queries can now be included in your queries generated by SObjectRepository.cls.
Sample code showing a method that could be added to AccountRepository (not included in the repository):
public class AccountRepository extends SObjectRepository { public AccountRepository() { // When only an SObject type is provided, all fields for the SObject are included in your queries super(Schema.Account.SObjectType); } public List<Account> getAccountsCreatedToday() { return (List<Account>)this.Query // Create an instance of QueryFilter to apply any filters to the query .filterBy(new QueryFilter(Schema.Account.Status, QueryOperator.EQUALS, QueryDateLiteral.TODAY)) .getQueryResults(); } public Account getAccountAndContactsWithEmails(Id accountId) { return (Account)this.Query .filterBy(new QueryFilter(Schema.Account.Id, QueryOperator.EQUALS, accountId)) .includeChildrenRecords( // Provide the child object's field that relates it to the parent object Schema.Contact.AccountId, // Filters for the children records can also be included new ContactRepository().filterBy(new QueryFilter(Schema.Contact.Email, QueryOperator.NOT_EQUAL_TO, null)) ) .getFirstQueryResult(); } }
Declarative/Configuration Changes
- Logging is now enabled by default (via the custom setting NebulaLoggerSettings__c)
- A custom app (cleverly called 'Nebula') has been added - it includes 1 tab for the NebulaLog__c object
- Updated some field descriptions & help text on the various custom settings
Refactoring (Could Impact Existing Code)
- SObjectRecordTypes.cls now uses QueryBuilder.cls for its query generation
- QueryBuilder.whereField() method has been replaced by QueryBuilder.filterBy() - it expects either a single instance or a list of QueryFilter.cls
- SObjectRepository.queryFactory is now SObjectRepository.Query
- Miscellaneous cleanup - variable name standardisation, whitespace, etc
- Removed the method ISObjectRepository.getCreatedSinceTimeValue
- Delete CollectionUtils.cls
- TestingUtils.setReadOnlyField() has been refactored to allow any object type as a value & to prevent duplicating fields on the returned SObject
Continuous Integration Setup & Code Cleanup
Tech debt release to get CI working, catch up on some unit tests, documentation, etc.
Refactoring (Could Impact Existing Code)
- Changed NebulaSettings.cls to use static variables and methods
- Changed Logger.cls to use static methods - everything now calls Logger directly
- Renamed QueryUtils.cls to QueryArgumentFormatter.cls (and refactored it)
- QueryBuilder.cls now uses QueryArgumentFormatter.cls
- Fixed the ordering of the filter scopes to be in descending order (for readability)
Bugfixes
- Fixed a problem with SObjectRecordTypes.cls when the object has no record types
- Logger now checks the custom setting to see if it is enabled
Continuous Integration
- Added .travis.yml for continuous integration. Passing unit tests with 75% code coverage is now required to approve a PR
- Added Travis CI build badges to README.md
Unit Tests
- Standardised the naming convention on a few test methods
- Added more unit tests & test classes
Added support for 'USING SCOPE' in SOQL
- New enum, QueryFilterScope, with values EVERYTHING, MINE, TEAM
- IQueryBuilder now has the method IQueryBuilder usingScope(QueryFilterScope filterScope);
- Fixed all methods in QueryBuilder & IQueryBuilder to return IQueryBuilder instead of QueryBuilder
- Fixed NebulaSettings so it actually loads the custom settings
Initial Alpha Release
Initial alpha release of the Apex framework for Salesforce development.