-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,13 @@ | |
* This file is part of the Nebula Framework project, released under the MIT License. * | ||
* See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * | ||
*************************************************************************************************/ | ||
public virtual class DML extends NebulaCore implements IDML { | ||
public abstract class DML extends NebulaCore implements IDML { | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
jongpie
Author
Owner
|
||
|
||
private Schema.SObjectType sobjectType; | ||
|
||
public DML(Schema.SObjectType sobjectType) { | ||
this.sobjectType = sobjectType; | ||
} | ||
|
||
public virtual void insertRecords(SObject record) { | ||
this.insertRecords(new List<SObject>{record}); | ||
|
@@ -21,13 +27,7 @@ public virtual class DML extends NebulaCore implements IDML { | |
} | ||
|
||
public virtual void upsertRecords(SObject record) { | ||
// Salesforce will only allow upsert calls for SObjects if a declared-type list is passed in. | ||
// This is fine for the bulk method, where we can assume the caller is passing in an explicit list, but for a single record, | ||
// the only way to successfully perform the upsert is to dynamically spin up a list of the SObject's type | ||
String listType = 'List<' + record.getSObjectType() + '>'; | ||
List<SObject> castRecords = (List<SObject>)Type.forName(listType).newInstance(); | ||
castRecords.add(record); | ||
this.upsertRecords(castRecords); | ||
this.upsertRecords(this.castRecords(record)); | ||
} | ||
|
||
public virtual void upsertRecords(List<SObject> records) { | ||
|
@@ -59,4 +59,26 @@ public virtual class DML extends NebulaCore implements IDML { | |
if(!records.isEmpty()) Database.emptyRecycleBin(records); | ||
} | ||
|
||
// Not all objects will have external ID fields, so these methods are protected (instead of public) | ||
// Any object that needs an upsert by external ID can expose these methods in their repos | ||
protected virtual void upsertRecords(SObject record, Schema.SObjectField externalIdField) { | ||
this.upsertRecords(this.castRecords(record), externalIdField); | ||
} | ||
|
||
protected virtual void upsertRecords(List<SObject> records, Schema.SObjectField externalIdField) { | ||
Database.upsert(records, externalIdField); | ||
} | ||
|
||
private List<SObject> castRecords(SObject record) { | ||
// Salesforce will only allow upsert calls for SObjects if a declared-type list is passed in. | ||
// This is fine for the bulk method, where we can assume the caller is passing in an explicit list, but for a single record, | ||
// the only way to successfully perform the upsert is to dynamically spin up a list of the SObject's type | ||
|
||
String listType = 'List<' + this.sobjectType + '>'; | ||
List<SObject> castRecords = (List<SObject>)Type.forName(listType).newInstance(); | ||
castRecords.add(record); | ||
|
||
return castRecords; | ||
} | ||
|
||
} |
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,25 +32,21 @@ private class SObjectRepository_Tests { | |
) | ||
.getFirstQueryResult(); | ||
} | ||
} | ||
|
||
public override void upsertRecords(SObject record) { | ||
Account account = (Account)record; | ||
upsert account; | ||
} | ||
|
||
public override void upsertRecords(List<SObject> recordList) { | ||
List<Account> accountList = (List<Account>)recordList; | ||
upsert accountList; | ||
public with sharing class ContactRepository extends SObjectRepository { | ||
public ContactRepository() { | ||
super(Schema.Contact.SObjectType, new List<Schema.SObjectField>{Schema.Contact.Id, Schema.Contact.AccountId, Schema.Contact.Email, Schema.Contact.LastName}); | ||
} | ||
} | ||
|
||
public override void updateRecords(SObject record) { | ||
TestingUtils.updatedRecords.add(record); | ||
private without sharing class UserRepository extends SObjectRepository { | ||
public UserRepository() { | ||
super(Schema.User.SObjectType, new List<Schema.SObjectField>{Schema.User.Id, Schema.User.Name}); | ||
} | ||
} | ||
|
||
public with sharing class ContactRepository extends SObjectRepository { | ||
public ContactRepository() { | ||
super(Schema.Contact.SObjectType); | ||
public override void upsertRecords(SObject record, Schema.SObjectField externalIdField) { | ||
super.upsertRecords(record, externalIdField); | ||
} | ||
} | ||
|
||
|
@@ -185,7 +181,7 @@ private class SObjectRepository_Tests { | |
static void it_should_return_account_and_contacts_with_emails_as_children_records() { | ||
Account account = [SELECT Id FROM Account LIMIT 1]; | ||
|
||
Contact contact = createContact(Account.Id); | ||
Contact contact = createContact(account.Id); | ||
contact.Email = '[email protected]'; | ||
insert contact; | ||
contact = [SELECT Id, AccountId, Email FROM Contact WHERE Id = :contact.Id]; | ||
|
@@ -258,7 +254,47 @@ private class SObjectRepository_Tests { | |
new SObjectRepository_Tests.AccountRepository().updateRecords(existingAccount); | ||
Test.stopTest(); | ||
|
||
System.assert((Account)TestingUtils.updatedRecords[0] == existingAccount); | ||
existingAccount = [SELECT Id, LastModifiedDate FROM Account LIMIT 1]; | ||
System.assert(existingAccount.LastModifiedDate > originalLastModifiedDate, existingAccount); | ||
} | ||
|
||
@isTest | ||
static void it_should_upsert_a_single_new_record() { | ||
Account newAccount = createAccount(); | ||
System.assertEquals(null, newAccount.Id); | ||
|
||
Test.startTest(); | ||
new SObjectRepository_Tests.AccountRepository().upsertRecords(newAccount); | ||
Test.stopTest(); | ||
|
||
System.assertNotEquals(null, newAccount.Id); | ||
} | ||
|
||
@isTest | ||
static void it_should_upsert_a_single_existing_record() { | ||
Account existingAccount = [SELECT Id, LastModifiedDate FROM Account LIMIT 1]; | ||
Datetime originalLastModifiedDate = existingAccount.LastModifiedDate; | ||
|
||
Test.startTest(); | ||
new SObjectRepository_Tests.AccountRepository().upsertRecords(existingAccount); | ||
Test.stopTest(); | ||
|
||
existingAccount = [SELECT Id, LastModifiedDate FROM Account LIMIT 1]; | ||
System.assert(existingAccount.LastModifiedDate > originalLastModifiedDate, existingAccount); | ||
} | ||
|
||
@isTest | ||
static void it_should_upsert_a_single_existing_record_with_external_id() { | ||
User existingUser = [SELECT Id, LastModifiedDate, Username FROM User WHERE Id = :UserInfo.getUserId()]; | ||
Datetime originalLastModifiedDate = existingUser.LastModifiedDate; | ||
System.assertNotEquals(null, existingUser.Username); | ||
|
||
Test.startTest(); | ||
new SObjectRepository_Tests.UserRepository().upsertRecords(existingUser, Schema.User.Username); | ||
Test.stopTest(); | ||
|
||
existingUser = [SELECT Id, LastModifiedDate FROM User LIMIT 1]; | ||
System.assert(existingUser.LastModifiedDate > originalLastModifiedDate, existingUser); | ||
} | ||
|
||
@isTest | ||
|
taking a look at these changes on my phone so I will probably just leave this one comment - I like the idea of DML being abstract, but in practice we have many times outside of the handlers (or, through the handlers but by DI with a separate class) injected just the DML class if no querying is necessary but the database update / insert / delete, etc call is going to be used. The whole idea of the DML calls always being mocked outside of our more complicated integration tests has been the key to our test speed success, but for clarity's sake we don't inject the full repo for an object if it was only going to be used for crud.