Skip to content

Commit

Permalink
Added support for using System.AccessLevel (jongpie#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
superkensington committed May 7, 2024
1 parent 00d196d commit 82c6d68
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 5 deletions.
7 changes: 6 additions & 1 deletion nebula-query-and-search/main/classes/AggregateQuery.cls
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ global class AggregateQuery extends SOQL {
return this.setHasChanged();
}

global AggregateQuery withAccessLevel(System.AccessLevel mode) {
super.doWithAccessLevel(mode);
return this.setHasChanged();
}

global AggregateQuery orderByField(Schema.SObjectField field) {
return this.orderByField(field, null);
}
Expand Down Expand Up @@ -220,7 +225,7 @@ global class AggregateQuery extends SOQL {
super.doGetOrderByString() +
super.doGetLimitCountString() +
super.doGetOffetString();
return Database.countQuery(countQuery);
return Database.countQuery(countQuery, this.doGetAccessLevel());
}

global AggregateResult getFirstResult() {
Expand Down
5 changes: 5 additions & 0 deletions nebula-query-and-search/main/classes/Query.cls
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,11 @@ global class Query extends SOQL {
//return this.setHasChanged();
//}

global Query withAccessLevel(System.AccessLevel mode) {
super.doWithAccessLevel(mode);
return this.setHasChanged();
}

global Query orderByField(Schema.SObjectField field) {
return this.orderByField(new SOQL.QueryField(field));
}
Expand Down
5 changes: 5 additions & 0 deletions nebula-query-and-search/main/classes/RecordSearch.cls
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ global class RecordSearch extends SOSL {
return this.setHasChanged();
}

global RecordSearch withAccessLevel(System.AccessLevel accessLevel) {
this.accessLevel = accessLevel;
return this.setHasChanged();
}

global RecordSearch updateArticleReporting(SOSL.ArticleReporting articleReporting) {
this.articleReporting = articleReporting;
return this.setHasChanged();
Expand Down
17 changes: 15 additions & 2 deletions nebula-query-and-search/main/classes/SOQL.cls
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ global abstract class SOQL implements Comparable {
protected Set<SOQL.QueryField> excludedQueryFields;
protected Scope scope;
protected List<String> whereFilters;
protected System.AccessLevel accessLevel;
protected List<String> orderByFieldApiNames;
protected Integer limitCount;
protected Integer offset;
Expand All @@ -198,6 +199,7 @@ global abstract class SOQL implements Comparable {
this.excludedQueryFields = new Set<SOQL.QueryField>();
this.whereFilters = new List<String>();
this.orderByFieldApiNames = new List<String>();
this.accessLevel = System.AccessLevel.SYSTEM_MODE;
this.cacheResults = false;
this.hasChanged = false;
}
Expand Down Expand Up @@ -271,6 +273,10 @@ global abstract class SOQL implements Comparable {
this.doSetHasChanged();
}

protected void doWithAccessLevel(System.AccessLevel accessLevel) {
this.accessLevel = accessLevel;
}

protected void doOrderBy(SOQL.QueryField queryField, SOQL.SortOrder sortOrder, Boolean sortNullsFirst) {
this.doOrderBy(queryField.toString(), sortOrder, sortNullsFirst);
}
Expand Down Expand Up @@ -305,7 +311,7 @@ global abstract class SOQL implements Comparable {
if (this.cacheResults) {
return this.getCachedResults();
} else {
return Database.query(this.getQuery());
return Database.query(this.getQuery(), this.doGetAccessLevel());
}
}

Expand Down Expand Up @@ -361,6 +367,10 @@ global abstract class SOQL implements Comparable {
return this.whereFilters.isEmpty() ? '' : ' WHERE ' + String.join(this.whereFilters, ' AND ');
}

protected System.AccessLevel doGetAccessLevel() {
return this.accessLevel ?? System.AccessLevel.SYSTEM_MODE;
}

protected String doGetOrderByString() {
return this.orderByFieldApiNames.isEmpty() ? '' : ' ORDER BY ' + String.join(this.orderByFieldApiNames, ', ');
}
Expand All @@ -383,7 +393,10 @@ global abstract class SOQL implements Comparable {

Boolean isCached = cachedResultsByHashCode.containsKey(hashCode);
if (!isCached) {
cachedResultsByHashCode.put(hashCode, Database.query(query));
cachedResultsByHashCode.put(
hashCode,
Database.query(query, this.doGetAccessLevel())
);
}

// Always return a deep clone so the original cached version is never modified
Expand Down
13 changes: 11 additions & 2 deletions nebula-query-and-search/main/classes/SOSL.cls
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ global abstract class SOSL {
protected SOSL.ArticleReporting articleReporting;
protected List<String> withClauses;
protected List<String> withDataCategoryClauses;
protected System.AccessLevel accessLevel;
protected SOSL.SearchGroup searchGroup;

protected SOSL(String searchTerm, Query sobjectQuery) {
Expand All @@ -57,6 +58,7 @@ global abstract class SOSL {
this.searchGroup = SOSL.SearchGroup.ALL_FIELDS;
this.withClauses = new List<String>();
this.withDataCategoryClauses = new List<String>();
this.accessLevel = System.AccessLevel.SYSTEM_MODE;
}

public Set<Schema.SObjectType> getSObjectTypes() {
Expand Down Expand Up @@ -88,7 +90,7 @@ global abstract class SOSL {
if (this.cacheResults) {
return this.getCachedResults();
} else {
return System.Search.query(this.getSearch());
return System.Search.query(this.getSearch(), this.doGetAccessLevel());
}
}

Expand Down Expand Up @@ -119,6 +121,10 @@ global abstract class SOSL {
return this.withClauses.isEmpty() ? '' : ' WITH ' + String.join(this.withClauses, ' WITH ');
}

protected System.AccessLevel doGetAccessLevel() {
return this.accessLevel ?? System.AccessLevel.SYSTEM_MODE;
}

protected String doGetUpdateArticleReportingString() {
return this.articleReporting == null ? '' : ' UPDATE ' + this.articleReporting.name();
}
Expand All @@ -129,7 +135,10 @@ global abstract class SOSL {

Boolean isCached = cachedSearchResultsByHashCode.containsKey(hashCode);
if (!isCached) {
cachedSearchResultsByHashCode.put(hashCode, Search.query(searchQuery));
cachedSearchResultsByHashCode.put(
hashCode,
System.Search.query(searchQuery, this.doGetAccessLevel())
);
}

// Always return a deep clone so the original cached version is never modified
Expand Down
60 changes: 60 additions & 0 deletions nebula-query-and-search/tests/classes/AggregateQuery_Tests.cls
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,52 @@ private class AggregateQuery_Tests {
System.Assert.areEqual(expectedResults, returnedResults);
}

@IsTest
static void it_should_run_with_system_mode() {
String expectedQueryString = 'SELECT COUNT(Id) COUNT__Id FROM Opportunity';

AggregateQuery aggregateQuery = new AggregateQuery(Schema.Opportunity.SObjectType)
.addAggregate(SOQL.Aggregate.COUNT, Opportunity.Id)
.withAccessLevel(System.AccessLevel.SYSTEM_MODE);

System.Assert.areEqual(expectedQueryString, aggregateQuery.getQuery());
List<Schema.AggregateResult> expectedResults = Database.query(expectedQueryString);
List<Schema.AggregateResult> returnedResults;
Exception caughtException;
System.runAs(minimumAccessUser()) {
try {
returnedResults = aggregateQuery.getResults();
} catch (Exception e) {
caughtException = e;
}
}
System.Assert.isNull(caughtException, 'Query should not throw exception when run in System Mode');
System.Assert.areEqual(expectedResults, returnedResults);
}

@IsTest
static void it_should_run_with_user_mode() {
String expectedQueryString = 'SELECT COUNT(Id) COUNT__Id FROM Opportunity';

AggregateQuery aggregateQuery = new AggregateQuery(Schema.Opportunity.SObjectType)
.addAggregate(SOQL.Aggregate.COUNT, Opportunity.Id)
.withAccessLevel(System.AccessLevel.USER_MODE);

System.Assert.areEqual(expectedQueryString, aggregateQuery.getQuery());
List<Schema.AggregateResult> expectedResults = Database.query(expectedQueryString);
List<Schema.AggregateResult> returnedResults;
Exception caughtException;
System.runAs(minimumAccessUser()) {
try {
returnedResults = aggregateQuery.getResults();
} catch (Exception e) {
caughtException = e;
}
}
System.Assert.isInstanceOfType(caughtException, System.QueryException.class, 'Query should throw exception when run in User Mode');
System.Assert.isTrue(caughtException.getMessage().contains('sObject type \'Opportunity\' is not supported'), 'Query should throw exception when run in User Mode');
}

@IsTest
static void it_should_build_a_ridiculous_query_string() {
String expectedQueryString =
Expand Down Expand Up @@ -168,4 +214,18 @@ private class AggregateQuery_Tests {
List<Schema.AggregateResult> returnedResults = aggregateQuery.getResults();
System.Assert.areEqual(expectedResults, returnedResults);
}

static User minimumAccessUser() {
return new User(
Alias = 'newUser',
Email = '[email protected]',
EmailEncodingKey = 'UTF-8',
LastName = 'Testing',
LanguageLocaleKey = 'en_US',
LocaleSidKey = 'en_US',
ProfileId = [SELECT Id FROM Profile WHERE Name = 'Minimum Access - Salesforce'].Id,
TimeZoneSidKey = 'GMT',
UserName = '[email protected]'
);
}
}
60 changes: 60 additions & 0 deletions nebula-query-and-search/tests/classes/Query_Tests.cls
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,52 @@ private class Query_Tests {
List<Account> accounts = accountQuery.getResults();
}

@IsTest
static void it_should_run_with_system_mode() {
String expectedQueryString = 'SELECT Id, Name FROM Account LIMIT 1';

Query accountQuery = new Query(Schema.Account.SObjectType)
.limitTo(1)
.withAccessLevel(System.AccessLevel.SYSTEM_MODE);

System.Assert.areEqual(expectedQueryString, accountQuery.getQuery());
List<Account> expectedResults = Database.query(expectedQueryString);
List<Account> returnedResults;
Exception caughtException;
System.runAs(minimumAccessUser()) {
try {
returnedResults = accountQuery.getResults();
} catch (Exception e) {
caughtException = e;
}
}
System.Assert.isNull(caughtException, 'Query should not throw exception when run in System Mode');
System.Assert.areEqual(expectedResults, returnedResults);
}

@IsTest
static void it_should_run_with_user_mode() {
String expectedQueryString = 'SELECT Id, Name FROM Account LIMIT 1';

Query accountQuery = new Query(Schema.Account.SObjectType)
.limitTo(1)
.withAccessLevel(System.AccessLevel.USER_MODE);

System.Assert.areEqual(expectedQueryString, accountQuery.getQuery());
List<Account> expectedResults = Database.query(expectedQueryString);
List<Account> returnedResults;
Exception caughtException;
System.runAs(minimumAccessUser()) {
try {
returnedResults = accountQuery.getResults();
} catch (Exception e) {
caughtException = e;
}
}
System.Assert.isInstanceOfType(caughtException, System.QueryException.class, 'Query should throw exception when run in User Mode');
System.Assert.isTrue(caughtException.getMessage().contains('sObject type \'Account\' is not supported'), 'Query should throw exception when run in User Mode');
}

@IsTest
static void it_includes_order_by_statement_for_single_field() {
String expectedQueryString = 'SELECT Id, Name FROM Lead ORDER BY CreatedDate ASC NULLS FIRST';
Expand Down Expand Up @@ -277,4 +323,18 @@ private class Query_Tests {

System.Test.stopTest();
}

static User minimumAccessUser() {
return new User(
Alias = 'newUser',
Email = '[email protected]',
EmailEncodingKey = 'UTF-8',
LastName = 'Testing',
LanguageLocaleKey = 'en_US',
LocaleSidKey = 'en_US',
ProfileId = [SELECT Id FROM Profile WHERE Name = 'Minimum Access - Salesforce'].Id,
TimeZoneSidKey = 'GMT',
UserName = '[email protected]'
);
}
}
64 changes: 64 additions & 0 deletions nebula-query-and-search/tests/classes/RecordSearch_Tests.cls
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,56 @@ private class RecordSearch_Tests {
List<User> userSearchResults = userSearch.getFirstResults();
}

@IsTest
static void it_should_run_with_system_mode() {
String expectedSearchQueryString = 'FIND \'' + System.UserInfo.getUserEmail() + '\' IN ALL FIELDS RETURNING Contact(Id, Name)';

Query contactQuery = new Query(Schema.Contact.SObjectType);
RecordSearch contactSearch = new RecordSearch(System.UserInfo.getUserEmail(), contactQuery)
.withAccessLevel(System.AccessLevel.SYSTEM_MODE);

System.assertEquals(expectedSearchQueryString, contactSearch.getSearch());
List<Contact> contactSearchResults = contactSearch.getFirstResults();

List<List<SObject>> expectedResults = Search.query(expectedSearchQueryString);
List<List<SObject>> returnedResults;
Exception caughtException;
System.runAs(minimumAccessUser()) {
try {
returnedResults = contactSearch.getResults();
} catch (Exception e) {
caughtException = e;
}
}
System.Assert.isNull(caughtException, 'Search should not throw exception when run in System Mode');
System.Assert.areEqual(expectedResults, returnedResults);
}

@IsTest
static void it_should_run_with_user_mode() {
String expectedSearchQueryString = 'FIND \'' + System.UserInfo.getUserEmail() + '\' IN ALL FIELDS RETURNING Contact(Id, Name)';

Query contactQuery = new Query(Schema.Contact.SObjectType);
RecordSearch contactSearch = new RecordSearch(System.UserInfo.getUserEmail(), contactQuery)
.withAccessLevel(System.AccessLevel.USER_MODE);

System.assertEquals(expectedSearchQueryString, contactSearch.getSearch());
List<Contact> contactSearchResults = contactSearch.getFirstResults();

List<List<SObject>> expectedResults = Search.query(expectedSearchQueryString);
List<List<SObject>> returnedResults;
Exception caughtException;
System.runAs(minimumAccessUser()) {
try {
returnedResults = contactSearch.getResults();
} catch (Exception e) {
caughtException = e;
}
}
System.Assert.isInstanceOfType(caughtException, System.QueryException.class, 'Search should throw exception when run in User Mode');
System.Assert.isTrue(caughtException.getMessage().contains('sObject type \'contact\' is not supported'), 'Search should throw exception when run in User Mode');
}

@IsTest
static void it_should_cache_search_results_when_enabled() {
Integer loops = 4;
Expand All @@ -150,4 +200,18 @@ private class RecordSearch_Tests {

System.Test.stopTest();
}

static User minimumAccessUser() {
return new User(
Alias = 'newUser',
Email = '[email protected]',
EmailEncodingKey = 'UTF-8',
LastName = 'Testing',
LanguageLocaleKey = 'en_US',
LocaleSidKey = 'en_US',
ProfileId = [SELECT Id FROM Profile WHERE Name = 'Minimum Access - Salesforce'].Id,
TimeZoneSidKey = 'GMT',
UserName = '[email protected]'
);
}
}

0 comments on commit 82c6d68

Please sign in to comment.