Skip to content

Commit

Permalink
Merge pull request #1103 from SFDO-Community/feature/cci_flow_custome…
Browse files Browse the repository at this point in the history
…rOrg

Feature/cci flow customer org
  • Loading branch information
davidmreed authored Nov 10, 2021
2 parents 9bc2f62 + 9f10b26 commit 5e06e42
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 39 deletions.
28 changes: 23 additions & 5 deletions README.markdown
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Declarative Rollups for Lookup Field Relationships

[![Build Status](https://travis-ci.org/afawcett/declarative-lookup-rollup-summaries.svg)](https://travis-ci.org/afawcett/declarative-lookup-rollup-summaries)

## Features Summary

- Rollup information between Lookup relationships not previously possible without writing Apex Triggers
Expand All @@ -14,9 +12,9 @@

Please refer to the blog posts below for more detailed information.

## Community Support
## Community Support and Contributing

This is a community driven tool, please help support it, share your experiences in this [Chatter group](https://success.salesforce.com/_ui/core/chatter/groups/GroupProfilePage?g=0F9300000009O5p).
This is a community driven tool, please help support it, share your experiences in this [Chatter group](https://success.salesforce.com/_ui/core/chatter/groups/GroupProfilePage?g=0F9300000009O5p). If you want to contribute as a developer, see [below](#contributing-to-declarative-rollup-summary-tools)

## Documentation

Expand Down Expand Up @@ -560,4 +558,24 @@ Package [Production URL](https://login.salesforce.com/packaging/installPackage.a

# Installing the Source Code (Developers)

This project now uses Salesforce DX for development and packaging. Clone this org and deploy the code to a scratch org to develop and contribute back changes via Pull Requests. If you want to deploy the unmanaged version of this to your sandbox or production org you can use the Salesforce DX convert and deploy commands to do so. However the recommended deployment for these orgs is via the managed package links above.
If you want to deploy the unmanaged version of this to your sandbox or production org, clone this repo and use the Salesforce DX toolchain for deployment. However the recommended deployment for these orgs is via the managed package links above.

# Contributing to Declarative Rollup Summary Tools

This project now uses [Salesforce DX](https://trailhead.salesforce.com/content/learn/modules/sfdx_app_dev) and [CumulusCI (CCI)](https://trailhead.salesforce.com/en/content/learn/trails/build-applications-with-cumulusci) for development and packaging. You can use either tool chain to contribute.

It's easy:

0. Have VS Code with [Salesforce DX Extended](https://marketplace.visualstudio.com/items?itemName=salesforce.salesforcedx-vscode-expanded) setup and running

1. Clone this repo
2. run `npm install` - this will ensure our code formatting rules apply (via Prettier)
3. create a _new branch_ from `main`, all branches must start with `feature/`, e.g. `feature/newSetupUX`(use a descriptive name)
4. Deploy code to a Scratch Org via CCI or DX
5. Work on it

When done:

- Open up a PR and fill out the template. Once done, one of two things will happen
1. If you are a DLRS team member, successful builds and reviews are required for merge
2. If you are not a member (yet) then a team member will pick up your PR, close it and open a new PR with your changes. That way your contribution will be preserved. From there it is back to 1)
40 changes: 30 additions & 10 deletions cumulusci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,22 @@ tasks:
retry_always: True

flows:
ci_beta:
steps:
1.5:
task: deploy_test_config

config_apextest:
steps:
3:
task: deploy_test_config

ci_beta:
customer_org:
steps:
1.5:
task: deploy_test_config
1:
task: install_managed
options:
security_type: NONE

deploy_unmanaged:
steps:
Expand All @@ -66,10 +73,23 @@ flows:
extra: "--sourcepath ./dlrs/libs/fflib-apexmocks,./dlrs/libs/fflib-common,./dlrs/libs/lrengine,./dlrs/libs/metadataservice,./dlrs/main"

orgs:
scratch:
dev_prerelease:
config_file: orgs/dev_prerelease.json
days: 7
beta_prerelease:
config_file: orgs/beta_prerelease.json
days: 7
scratch:
dev_prerelease:
config_file: orgs/dev_prerelease.json
days: 7
beta_prerelease:
config_file: orgs/beta_prerelease.json
days: 7

plans:
install:
slug: install
title: Install Declarative Lookup Rollup Summaries (DLRS)
tier: primary
is_listed: True
preflight_message: "This will install Declarative Lookup Rollup Summaries (DLRS) in your org."
post_install_message: "Thanks for installing Declarative Lookup Rollup Summaries (DLRS). Please visit the [DLRS Trailblazer Community group](https://trailhead.salesforce.com/trailblazer-community/groups/0F9300000009O5pCAE?tab=discussion) for any questions about DLRS."
error_message: "To get help with this error, visit the [DLRS Trailblazer Community group](https://trailhead.salesforce.com/trailblazer-community/groups/0F9300000009O5pCAE?tab=discussion)."
steps:
1:
flow: customer_org
14 changes: 7 additions & 7 deletions dlrs/libs/fflib-common/classes/fflib_SecurityUtils.cls
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
* user does not have the proper security granted.
**/
public class fflib_SecurityUtils {
@testVisible
@TestVisible
private enum OperationType {
CREATE,
READ,
Expand All @@ -39,7 +39,7 @@ public class fflib_SecurityUtils {

/**
* SecurityException is never be thrown directly by fflib_SecurityUtils, instead all
* forms of CRUD and FLD violations throw subclasses of it. It is provided as a conveneience
* forms of CRUD and FLD violations throw subclasses of it. It is provided as a convenience
* in the event you wish to handle CRUD and FLS violations the same way (e.g. die and display an error)
**/
public virtual class SecurityException extends Exception {
Expand Down Expand Up @@ -119,7 +119,7 @@ public class fflib_SecurityUtils {
* FLS and CRUD checks exists and is enabled.
* Per security best practices setting BYPASS should be an a opt-in, and not the default behavior.
**/
public static boolean BYPASS_INTERNAL_FLS_AND_CRUD = false;
public static Boolean BYPASS_INTERNAL_FLS_AND_CRUD = false;

/**
* Check{Insert,Read,Update} methods check both FLS and CRUD
Expand Down Expand Up @@ -345,7 +345,7 @@ public class fflib_SecurityUtils {

/**
* Checks insert CRUD for the specified object type.
* @exception CrudException if the running uder does not have insert rights to the {@code objType} SObject.
* @exception CrudException if the running user does not have insert rights to the {@code objType} SObject.
**/
public static void checkObjectIsInsertable(SObjectType objType) {
if (BYPASS_INTERNAL_FLS_AND_CRUD)
Expand All @@ -357,7 +357,7 @@ public class fflib_SecurityUtils {

/**
* Checks read CRUD for the specified object type.
* @exception CrudException if the running uder does not have read rights to the {@code objType} SObject.
* @exception CrudException if the running user does not have read rights to the {@code objType} SObject.
**/
public static void checkObjectIsReadable(SObjectType objType) {
if (BYPASS_INTERNAL_FLS_AND_CRUD)
Expand All @@ -368,7 +368,7 @@ public class fflib_SecurityUtils {

/**
* Checks update CRUD for the specified object type.
* @exception CrudException if the running uder does not have update rights to the {@code objType} SObject.
* @exception CrudException if the running user does not have update rights to the {@code objType} SObject.
**/
public static void checkObjectIsUpdateable(SObjectType objType) {
if (BYPASS_INTERNAL_FLS_AND_CRUD)
Expand All @@ -379,7 +379,7 @@ public class fflib_SecurityUtils {

/**
* Checks delete CRUD for the specified object type.
* @exception CrudException if the running uder does not have delete rights to the {@code objType} SObject.
* @exception CrudException if the running user does not have delete rights to the {@code objType} SObject.
**/
public static void checkObjectIsDeletable(SObjectType objType) {
if (BYPASS_INTERNAL_FLS_AND_CRUD)
Expand Down
115 changes: 110 additions & 5 deletions dlrs/libs/fflib-common/classes/fflib_SecurityUtilsTest.cls
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,99 @@

@isTest
private class fflib_SecurityUtilsTest {
@TestSetup
static void testSetup() {
// #315 Create a Permission Set that grants "Read" access to Account, Contact and Lead. We will use this in
// Spring '21 orgs that lack the "Read Only" Profile. See:
// https://help.salesforce.com/articleView?id=release-notes.rn_profiles_and_perms_read_only_new.htm&release=230&type=5).
PermissionSet ps = new PermissionSet(
Label = 'Read Only Permission Set',
Name = 'ReadOnlyPermissionSet'
);
insert ps;

// Grant Read access to the SObjects we use for CRUD tests
List<ObjectPermissions> objectPerms = new List<ObjectPermissions>();
objectPerms.add(
createObjectPermissions(ps.Id, 'Account', false, true, false, false)
);
objectPerms.add(
createObjectPermissions(ps.Id, 'Contact', false, true, false, false)
);
objectPerms.add(
createObjectPermissions(ps.Id, 'Lead', false, true, false, false)
);
insert objectPerms;

// Grant Read/Edit access to the SObject fields we use for FLS tests
List<FieldPermissions> fieldPerms = new List<FieldPermissions>();
fieldPerms.add(
createFieldPermissions(ps.Id, 'Contact', 'Birthdate', true, false)
);
fieldPerms.add(
createFieldPermissions(ps.Id, 'Contact', 'Email', true, false)
);
insert fieldPerms;
}

static Profile getProfile(String profileName) {
return [SELECT Id, Name FROM Profile WHERE Name = :profileName];
}

static ObjectPermissions createObjectPermissions(
Id permSetId,
String objectType,
Boolean canCreate,
Boolean canRead,
Boolean canUpdate,
Boolean canDelete
) {
return new ObjectPermissions(
ParentId = permSetId,
SobjectType = objectType,
PermissionsCreate = canCreate,
PermissionsRead = canRead,
PermissionsEdit = canUpdate,
PermissionsDelete = canDelete
);
}

static FieldPermissions createFieldPermissions(
Id permSetId,
String objectType,
String fieldName,
Boolean canRead,
Boolean canEdit
) {
return new FieldPermissions(
ParentId = permSetId,
SobjectType = objectType,
Field = objectType + '.' + fieldName,
PermissionsRead = canRead,
PermissionsEdit = canEdit
);
}

static User setupTestUser(String profileName) {
Profile p;
Boolean usedMinimumAccessProfile = false;
if (profileName == 'Read Only') {
try {
p = getProfile(profileName);
} catch (QueryException ex) {
if (
ex.getMessage().contains('List has no rows for assignment to SObject')
) {
// #315 If the "Read Only" Profile is absent, then assume it's a Spring '21 org and see if there's a
// "Minimum Access - Salesforce" Profile we can use instead.
p = getProfile('Minimum Access - Salesforce');
usedMinimumAccessProfile = true;
}
}
} else {
p = getProfile(profileName);
}

//username global uniqueness is still enforced in tests
//make sure we get something unique to avoid issues with parallel tests
String uniqueness = DateTime.now() + ':' + Math.random();
Expand All @@ -35,8 +127,7 @@ private class fflib_SecurityUtilsTest {
} catch (Exception e) {
uniqueness += e.getStackTraceString(); //includes the top level test method name without having to pass it
}
Profile p = [SELECT id, Name FROM Profile WHERE Name = :profileName];
User result = new User(
User usr = new User(
username = UserInfo.getUserId() +
'.' +
uniqueness.HashCode() +
Expand All @@ -52,8 +143,22 @@ private class fflib_SecurityUtilsTest {
profileid = p.Id,
timezonesidkey = 'America/Los_Angeles'
);
insert result;
return result;
insert usr;

if (usedMinimumAccessProfile) {
// #315 We need to assign the Perm Set to grant Account "Read" access
PermissionSet accountReadPS = [
SELECT Id
FROM PermissionSet
WHERE Name = 'ReadOnlyPermissionSet'
];
PermissionSetAssignment psa = new PermissionSetAssignment(
AssigneeId = usr.Id,
PermissionSetId = accountReadPS.Id
);
insert psa;
}
return usr;
}

@isTest
Expand Down Expand Up @@ -239,7 +344,7 @@ private class fflib_SecurityUtilsTest {
try {
fflib_SecurityUtils.checkRead(
Contact.SObjectType,
new List<String>{ 'LastName', 'accountId', 'ownerId' }
new List<String>{ 'LastName', 'eMaiL', 'BirthDATE' }
);
} catch (fflib_SecurityUtils.SecurityException e) {
ex = e;
Expand Down
35 changes: 23 additions & 12 deletions dlrs/main/classes/RollupControllerTest.cls
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ private class RollupControllerTest {
if (!TestContext.isSupported())
return;

System.runAs(setupTestUser('Read Only')) {
System.runAs(setupTestUser()) {
// Metadata API web Service mock implementation for tests
Test.setMock(WebServiceMock.class, new WebServiceMockImpl());

Expand Down Expand Up @@ -167,7 +167,7 @@ private class RollupControllerTest {
if (!TestContext.isSupported())
return;

System.runAs(setupTestUser('Read Only')) {
System.runAs(setupTestUser()) {
// Metadata API web Service mock implementation for tests
Test.setMock(WebServiceMock.class, new WebServiceMockImpl());

Expand Down Expand Up @@ -299,7 +299,7 @@ private class RollupControllerTest {
if (!TestContext.isSupported())
return;

System.runAs(setupTestUser('Read Only')) {
System.runAs(setupTestUser()) {
// Metadata API web Service mock implementation for tests
Test.setMock(WebServiceMock.class, new WebServiceMockImpl());

Expand Down Expand Up @@ -351,7 +351,7 @@ private class RollupControllerTest {
if (!TestContext.isSupported())
return;

System.runAs(setupTestUser('Read Only')) {
System.runAs(setupTestUser()) {
// Metadata API web Service mock implementation for tests
Test.setMock(WebServiceMock.class, new WebServiceMockImpl());

Expand Down Expand Up @@ -501,16 +501,27 @@ private class RollupControllerTest {
}
}

static User setupTestUser(String profileName) {
static User setupTestUser() {
//username global uniqueness is still enforced in tests
//make sure we get something unique to avoid issues with parallel tests
String uniqueness = DateTime.now() + ':' + Math.random();
try {
throw new NullPointerException();
} catch (Exception e) {
uniqueness += e.getStackTraceString(); //includes the top level test method name without having to pass it
}
Profile p = [SELECT id, Name FROM Profile WHERE Name = :profileName];
uniqueness += new NullPointerException().getStackTraceString(); //includes the top level test method name without having to pass it

// officially, there is no Read Only Profile anymore;
// its present for packaging org and scratch org support
List<Profile> profiles = [
SELECT id, Name
FROM Profile
WHERE
Name = 'Read Only'
OR Name = 'ReadOnly'
OR Name = 'Standard User'
ORDER BY Name ASC
];
system.assert(
profiles.size() > 0,
'setupTestUser() requires either Read Only or Standard User Profile'
);
User result = new User(
username = UserInfo.getUserId() +
'.' +
Expand All @@ -524,7 +535,7 @@ private class RollupControllerTest {
lastname = 'Testing',
languagelocalekey = 'en_US',
localesidkey = 'en_US',
profileid = p.Id,
profileid = profiles[0].Id,
timezonesidkey = 'America/Los_Angeles'
);
insert result;
Expand Down

0 comments on commit 5e06e42

Please sign in to comment.