From 43b07b548ed84ac83fa378da4de5817977a686ef Mon Sep 17 00:00:00 2001 From: Rob Baillie <90244351+rob-baillie-ortoo@users.noreply.github.com> Date: Thu, 5 May 2022 14:04:21 +0100 Subject: [PATCH] Feature/dirty fields on upsert (#2) * Squashed 'subfolders/fflib/' content from commit 6eb7db0 git-subtree-dir: subfolders/fflib git-subtree-split: 6eb7db07e13db49df9e80729a219a9044495826c * removed odd fflib addition again * Revert "removed fflib again" This reverts commit 47d1162abc59517f1e586f982107ed15c59465c8. * Put apex-mocks and apex-extensions into the fflib directory * Removed external dependencies from the fflib extensions * Added reference to the fflib subfolder into the build * Squashed 'subfolders/fflib/' changes from 6eb7db0..573efa3 573efa3 Merge pull request #1 from OrtooApps/feature/include-mocks-and-ext 869710a Merge branch 'feature/include-mocks-and-ext' into feature/ortoo 8730eea Added ortoo build script to repo 1a7964e Removed external dependencies from the fflib extensions 1b3ba7b Put apex-mocks and apex-extensions into the fflib directory e33b28e Merge commit '07a3aafda9332ed74397bbec9319589bd3e0bec3' as 'subfolders/fflib' 47d1162 removed fflib again cc23295 Merge commit '0598deef2560f7365d9c6c4a78b1846d75d6a6f1' as 'framework/fflib' 0598dee Squashed 'framework/fflib/' content from commit baaf94d 10bb547 removed fflib again ad7ad4b Merge commit '2cadfa684480e349737d01f051c89ab7a425fda4' as 'subfolders/fflib' 2cadfa6 Squashed 'subfolders/fflib/' content from commit 6eb7db0 b42821a Fixed sharing on service snippets 3a8cd5e Fixed snippets issues 8d63341 Added a VSCode snippets file 1123e65 Avoid conflicting conditions being set e19d5ea Added the ability to specify the condition on a queryfactory by using a 'ISearchCriteria' d36342e Merge pull request #41 from OrtooApps/feature/timestamped-cache-entries 2601d46 Improved hiding of date literal setters eab9439 Merge pull request #40 from OrtooApps/temp/demo-2 37c6223 Another change d6caada Merge pull request #39 from OrtooApps/temp/demo 8c028af Added some notes... temp commit 933fe55 Merge pull request #38 from OrtooApps/feature/timestamped-cache-entries cd363c5 Removed redundant TODOs cb8cea0 Added test that re-querying aged results will put them back into the cache 97870e0 Added ability to specify maxAge and minDate in Dynamic Selector query calls 53edd71 Added test for age based cache retrieval in CachedSoqlExecutor d122285 Added the ability to specify a maximum age or minimum date added on the CachedSoqlExecutor when querying ba3a74c Updated TestDateTimeUtils so that now and today are always in step 807239c Added test and documentation for the cache entry 08124af Improved split between DateLiterals and DateTimeUtils e8d1113 Added ageing capabilities to SessionCache and NullCache 14fc597 Added tests for OrgCache ageing and improved behaviours 3a26bd2 Started to build 'max age' on cache retrieval a6c4484 Merge pull request #37 from OrtooApps/feature/slight-improvement-to-session-cache efc4672 Added removeAll to session cache 4683f0b Merge pull request #36 from OrtooApps/feature/standardise-build-in-framework e76cf0f Standardised the build - particularly running apex and getting the code coverage numbers 4938a08 Merge pull request #35 from OrtooApps/temp/support-qassign-integration a788274 Fixed merge error in test 2685af3 Simplified passing of mapping into SearchOrderBy and added test for the Null config 502b1c6 Merge branch 'main' into temp/support-qassign-integration bb0d696 Merge pull request #34 from OrtooApps/feature/add-credits-file 3dc4fcf Merge pull request #33 from OrtooApps/feature/add-dynamic-uow 05c0b92 Tidy of TODO file 4f4b7d1 Minor update to credits file - do not imply that the license file linked is the one that is appropriate a86e3f8 Added OS licenses as static resource 93d5fff Added factory methods for the Dynamic Unit of Work 01ca71e Merge pull request #32 from OrtooApps/feature/add-dynamic-uow fdc89e8 Added direct test for the dynamic unit of work ordering resolution be4e4c4 Added output of better exception when a circular reference is found in the Dynamic UOW 0bf55d4 Added tests for the dynamic unit of work ab04fad Started the sketch of the dynamic SObject UOW bf2ffc9 Merge pull request #31 from OrtooApps/feature/block-duplicate-configs baaf94d Block duplicate config records 52c2442 Merge pull request #30 from OrtooApps/feature/remove-example-services 9b099b5 Added custom metadata for the permissions service, so it cannot be overridden by customers 118f8d6 Added back the permissions service 20bface Removed the example services 8dd4482 Merge pull request #29 from OrtooApps/feature/improve-applicaiton-performance e366958 Added tests for the new getSobjectLocalName 9534a6f Fixed code that got the Selector type for a given Sobject Type 20d029e Fixed issue with selector factory not getting local namespaced SObject names correctly e303575 Added tests for stripLocalNamespace 0dcd5ea Applied string based factory configurations to more factories, to improve startup performance 75e4e54 Improved the startup performance when AppLogic, ChildRecordFinder or MessageRenderer configurations exist a82924a Improved the startup performance when AppLogic, ChildRecordFinder or MessageRenderer configurations exist 5bae05d FIxed bug with performance improvements in the logger service - null level should be regarded as ERROR d4c4ad3 Fixed test dd53ebb Attempting to improve startup and logging service performance f10210a Lazy load the Unit of Work, so it doesn't get built when it isn't needed bac4ce7 Made tests more robust 56df48d Merge pull request #28 from OrtooApps/feature/simple-logging-module 04d8b4b Corrected test execution 88d1827 Improved comparing of exceptions in tests 3e3cffc Simplified the setup of the test logger instance 912d43f Simplified the setup of the test logger instance c7a6997 Added test for ServiceUtils.logAndRethrow 5ae972a Moved logger service into core 875b961 Removed redundant TODO bd86ba2 Move the logger service test over to use the new test utils 30d2f09 Added assertion capabilities to the TestLoggerService 145d9f6 Started to add testing around logging in the CachedSoqlExecutor 0870a6d Added TestLoggerUtils to make it easier to switch logging to the right level in tests 276ee44 Moved the FakeLogger out to TestLoggerService so other classes can use it ab5112f Added the ability to strip context from ortoo_Exceptions 6238a10 Moved some code over to use the logger service 41658f9 Added test for the static method based LoggerService 778cafc Added testing for the logger implementation 53caeec Set the default logging level in the logging service implementation ae77a3e Improved logging when failing to generate a logger 7c25516 Make the logger service more robust to exceptions 338f1a7 Added clarity of the log level for exceptions - handled in the static class b53423e Added custom setting that allows for logging levels to be specified ad9f7bc Improvements to the logger output 81f916b Starting to build a simple logger acdf375 Merge pull request #27 from OrtooApps/feature/defaulting-implementations 51f5c25 Added testing for the internals that determine the default Service name 7d7a255 Added tests for the defaulting of service implementations based on their name da86bd7 Added tests for the defaulting of service implementations based on their name 278cca8 Implement defaulting of the Service implementation based on naming convention d1e13aa Merge pull request #22 from OrtooApps/feature/sobject-cache de09d1a Merge branch 'main' into feature/sobject-cache 68b8323 Merge pull request #26 from OrtooApps/feature/updates-to-support-example-app-repo 0009267 Added unit test for new errorRenderer capabilitites 65e34a1 Added unit test for the new configure labels on datatableHelper 69216ef Added capability to populate datatable columns with sobject definition based labels f034717 Added capability to populate datatable columns with sobject definition based labels 868890c Added the ability to mark only certain fields as dirty when first adding a record to the DML 5e008f5 Improved output of inaccessible fields on security violation 62b2089 Added ability to convert a Set of Strings to a List of Strings c818af7 Fixed sharing on class 0626cd9 Committing whitespace removal 0ad54fd Added test bd555da Added the ability to get the Id of an Sobject saved via the DmlService 2a2e9b1 Added proper ortoo id to the save buttons in the view and edit form dbfd232 Changed generateRowId to generateId, since that's more accurate d29484d Changed generateRowId to generateId, since that's more accurate 9fa245e Added some generic labels 8c9d06a Added assertion comment 1380b62 Added objectHelper - which allows us to generate ids for use in the UI fc8c930 Merge pull request #25 from OrtooApps/feature/updates-to-support-example-app-repo 4e86c73 Added passing through of labels into the buttons in the view and edit form 2ccc43a Merge pull request #24 from OrtooApps/main 69a9fea Merge pull request #23 from OrtooApps/feature/updates-to-support-example-app-repo a1c58d9 Added clearance of the object register when fabricated Sobjects are persisted. Allows multiple persists to be called 0d88df9 Added ability to add sub criteria without specifying the logic condition 5fe2224 Added the ability to get the Id of a persisted fabricatedSobject 63c0a18 Added ability to find if a DmlDefiner will delete other records f1b0c09 Added tests for DmlRecord.getChildSobjects 0b6d115 Added docs and clarified name for DmlRecord method to get child sobjects a99c196 Added getChildren to the DmlRecord 34e2cce Removed bad update 72b4caf Pointless TODO update for testing c7c7a7c Moved the default error prefix over to a label 1901e0b Added tests to SearchOrderBy with no config cec3bf8 Fixed unit tests with literal strings for the prefix on errors 37801e0 Merge branch 'temp/support-qassign-integration' of github.com:OrtooApps/framework into temp/support-qassign-integration 7401666 Added static messagePrefix to error messages 77c0007 Added ability to configure the SearchOrderByWithout a config 999d1de Added static messagePrefix to error messages aedd0a1 Added exception tests for put 678acfd Added single object puts c40e580 Remove chance of blank keys and ids in the cache 6d1013e Added tests for getKeys 72e1eb9 Simplify ObjectCache to use a cache key per put 7bc1289 Finished ObjectCacheTest 00823a1 Started to test 'remove' methods 36dcd3d Adding more testing, removing some defunct code and clarifying error cases 475bf33 Fixed unit test fragility dc44f8d Started testing get on objectCache 776d691 Started to write and sketch the test for ObjectCache c743113 Fixed compile error e456765 Reformatted tests to ensure Clayton picks up testing of the methods 3b80f95 Reformatted tests so they do not include fluent calls to the tested method 86f84bc Added tests for the OrgCache 525aa2c Added test and robustness to SessionCache 3906e1a Added a test for NullCache c60ecc3 Added removeAll to the OrgCache and fixed test for setting ORG scope 8a5dfda Renamed the BulkObjectCache to the correct ObjectCache 377619d Changed permissions service test to test the actual implementation (less risky) 3658177 Abstraction of permissions check for access to the platform cache 5d00e7f Removed need to pass the namespace in to get the qualified keys 48259c6 Removed redundant code 72a988f Moved both types of cache into the same partition (core) 9e347aa Made the SObjectCache more generic. Added docs 9b31ce9 Implemented a bulk version - everything in Maps... 577e8b0 Implemented version with a cache entry per sobject 6a6ba9f Sketching out the idea of the sobject cache 3f1ea97 Merge pull request #21 from OrtooApps/feature/cached-soql-executor b7ed664 Added PMD suppressions for valid violations c4190c3 Tidy messages so cache misses are not reported when a NullCache is used ef541de Added tests for NONE cache scope b447dad Fixing issues with the use of other cache types 58625db Enhanced the cached soql executor 9c5361c Merge pull request #20 from OrtooApps/feature/cached-soql-executor a2f3aa6 Added cacheability to the dynamic Sobject Selector a188674 Added tests and robustness to the org cache soql executor 6fcf832 Added simple test for Permissions Service a27eb51 Formatting fixes on the Organisation Service 58624e0 Only allow access to the SOQL cache if the user has a custom permission 563ae2e Added a permissions service that allows us to find if a user has a custom permission 2e30935 Added cache partition bff4b34 Co-authored-by: SundusShahbaz 513d3d9 Started experimentation with Org Wide SOQL results cache 50aab44 Removed ortoo_qra reference 93b8eb2 Merge pull request #19 from OrtooApps/feature/criteria-with-date-literals fb6a7be Removed codecoverage from the apex test runs (buggy) 38da713 Added test and documentation to fflib objects relating to DateLiteral changes 49d47c9 Finished the date literal tests and fixed a boundary bug 8ae7cb9 Added tests for last / next n e31bdd7 Added tests for DateLiterals for months and next / last 90 days 432925a Added tests for DateLiterals for weeks (and tomorrow) 796ca56 Started to add tests for the comparables in date literals c0794ac Added some edge case tests 9850046 More tests on date literals - covering months 9c674fb Added more dateLiteral tests (this and next week) ac74a5f Started a date literals test 8849cc8 Added ortoo_Asserts and moved all Amoss_Asserts calls over to ortoo e49e2bc Formatting updated to standards 9323e95 Removed defunct 'evaluator' d4d8519 More experiments with comparing dates b858500 Started playing with evaluating things against date literals 6907297 Added ability to pass in literals to fflib_Criteria 234dc61 Merge pull request #18 from OrtooApps/feature/more-robust-criteria 018946e Fixed typo 746fc54 Added a lot of tests for ortoo_Criteria 66303ff Minor improvement to uriUtils test to check console.error output f392e92 Added remaining public methods from fflib_Criteria to ortoo_Criteria 2cb008c Started the process of fully wrapping fflib_Criteria b68bd66 Merge pull request #17 from OrtooApps/feature/improve-pagination-control 68306b3 Removed possibility of null offset in search window 11690fc Removed chances of NaN being displayed on pagination controls 040c5d1 Merge pull request #16 from OrtooApps/feature/improve-criteria 45e3498 Added 'notInSet' for related fields into criteria 58a1c78 Added ability to specify criteria for related fields with IN dd6c931 Merge pull request #15 from OrtooApps/feature/access-uri-fragments b88cb53 Added ability to reference the fragment / hash on a URI bfc8c10 Added ability to reference the fragment / hash on a URI 33b42fa Merge pull request #14 from OrtooApps/feature/filter-and-paging-backend 7ebdb39 Merge pull request #13 from OrtooApps/feature/remove-package-lock cfb8296 Remove package lock and ignore it 329321c Rename tabPageName to tabName - removing references to 'page' 778c28b Merge pull request #12 from OrtooApps/feature/rename-tab-page-controller 7b48b6f Rename Tab Page Controller 10282b1 Merge pull request #11 from OrtooApps/feature/more-core-lwc-work b65a435 Added label for the lwcListViewWrapper 'old link' error c746627 Formatting tidy 658ce8c Added editor config for formatting the PRs in github b9bc589 Added unit tests for the redirect to tab page controller 71e5297 Added tests for getEpochTime 19601ba Upgraded Jest environment 0f90c2e Initial version of an LWC List View wrapper c3e29f5 Added ability to disable the save button in saveButtons 0625bcb Added generic LWC to render a highlighted message with similar styling to a toast 9dcd0f6 Started work on a generic LWC that allows an LWC to be included in a Tab Page 7aef99b Merge branch 'main' into feature/filter-and-paging-backend 7432b57 Added overflow to modal so picklists render properly 4a2e1d4 Merge pull request #10 from OrtooApps/feature/tweaks-to-error-renderer a2c351c Added options to displayError so it can be adjusted on call 82c163d Added options to displayError so it can be adjusted on call ae97c73 Merge pull request #9 from OrtooApps/feature/filter-and-paging 103bfdf Enhancements to the filter and results 89bffc0 Merge branch 'feature/filter-and-paging-backend' into feature/filter-and-paging df408a0 Merge branch 'main' into feature/filter-and-paging f71596b Fixed issue with empty result sets resulting in error trying to find the domain f1d6420 Merge pull request #8 from OrtooApps/feature/filter-and-paging-backend 3448786 Fixed check on message 0745ef4 Brought in more changes from the example-app implementation of filter + results 4ce9495 Added framework changes from the example-app implementation of the filter and paging screen 6f868a8 Merge pull request #7 from OrtooApps/feature/filter-and-paging 5d07aed Update to TODO 2965d97 Added test for filter and results 4be141b Tidied the paginationControls navigate event a little a5a38ab Added test for the page info 88761c4 Added tests for the pagination controls 56f3140 Removed redundant parameter 2700d69 Removed redundant wire import 49e8aa9 Improved communication from the filter and pagination controls 8aa25c8 Removed inline style from pagination controls 6772f4f Removed accidental call to apex 6d965e5 Added the initial 'filter and results' layout LWC 9eb4f45 Added an initial design for pagination controls 32dff13 Merge pull request #6 from OrtooApps/feature/updates-after-qassign-implementations f129751 Fixed TODO 329af98 Updated ids to the standard in the self configured combobox 897e31a Updated ids to the standard in the view and edit form c5a1dd5 Fixed tests relating to the new version of the name of buttons b283f97 Added ortoo Id generation to the confirmation dialog 0916a86 Improved test describes for the library functions c3f53fe Added the elementFinder library functions bfcd7df Removed pointless html from libraries 47a4c93 Fix spacing around the confirmation screen buttons 141f1ef Standardised saveButtons and reference labels properly 19322de Added additional edit buttons back into the view and edit form 2d8ddc3 Moved a load of tests lets over to const, because they should be 87088b5 Added the test for the elementIdGenerator 94dced8 Improved options on and testing of the formValidator eb67937 Completed the saveButtons test and removed some defunct code from the modal test 1aa2762 Added a save buttons component and added it to the view and edit form c8dac7b Added element Id generation to the modal and increased the padding a little e5155c4 Improved error renderer so it will render Javascript errors 1286e62 Removed done TODOs 7151a74 Merge pull request #5 from OrtooApps/feature/dynamic-selector c155680 Added the Dynamic Sobject Selector, for building SOQL statements based on configuration 8b15261 Fixed bug with the query factory not recognising an empty criteria when deciding if where clause should be added 6343a26 Merge pull request #4 from OrtooApps/feature/add-initial-base-lwcs d662d81 Fixed tests and more formatting on the view and edit form 164d22d Standardised some formatting of attributes fd78481 Added tests for the formValidator 3014a98 Added tests for clicking buttons in the view and edit form 3affc08 Added tests for the errorRenderer e8692bc Minor adds to the TODO 49ccd1a Added ability to put header buttons into a modal. 03cbe45 Hide the empty footer on the modal version of the view form 9c285b0 Added ability to hide the footer on the modal 521a494 Starting to build a modal mode for the view and edit form b39e23c Move some view form text over to labels 61a02a3 Added more generic LWC components 16d8817 Removed defunct comment and updated TODOs e2f17b7 Adde centring of the content in the modal footer 8f2707d Minor TODO added b4d2326 Fixed error report on invalid type b5cb6f1 Trying Jest tests in the build 0f2ad8c Added types to the confirmation dialog and improved its testing 6ef85c9 Started to add TODOs for LWCs 412cc55 Moved reference in test from aria-id to data-name in order to introduce a standard 18d97f8 Moved reference in test from aria-id to data-name in order to introduce a standard 62ea46a Improving the testing of the confirmation dialog d8c3ace Changed the message on confirmationDialog to a slot - so we can have HTML in there e86665d Added label for the 'close' text on the modal aa746de Removed formatted-modal f782fdf Removed linter pre-commit hook fad409d Added initial versions of base LWCs, including a confirmation dialog and a formatted modal 78dabd3 Removed defunct TODO 144077b Merge pull request #3 from OrtooApps/feature/adding-crud-and-dml-fls d0c5af3 Added code coverage numbers back again, as this is the only reliable way of getting numbers out of Salesforce 07f5c87 Added test for the elevation in UOW bc87fc0 Tidy of TODO 44dc4e7 Added ability to 'elevateContext' inside a Unit of Work 04128c9 Added docs to sobject selector fc34884 Update to TODO c840617 Changed method names to match template from fflib becbac6 Added test for SobjectSelector e2be197 Enforce FLS in Selectors by default 84da6ec Tweak to SecureDml to make publish rights slightly distinct 95e6654 Added unit test for secure deletes 757549f Added tests for update in SecureDml dbe8a39 Fixed bug with recognising when fields have been removed 6ba0371 Removed actual DML (and therefore fragility) from SecureDmlTest 20f1106 Testing FLS configurations in SecureDml f91edf4 Preparing SecureDml so that the FLS behaviours can be tested 4b2968e Started to test SecureDml 98d3472 Preparing SecureDml to be testable 80afd48 Added tests and documentation for the reseting of the stack trace on an exception 0cedce8 Added simple test for UnsecureDml 0f64657 Added testing and documentation to SobjectUtils method 31cf34d Tidy and documentation of new SobjectUtils methods c1f7fa1 Removed temporary code d1f8cf0 Removed temporary code aabf774 Moved literal strings in SecureDml over to labels 3d11521 Added constant based error codes for DML violations 89b38df Documentation in the SecureDml class a09bb9f Fixed null pointer bug in FLS checking 81cf3ac Removed defunct Dml Configuration class a676317 Reshape SecureDml so it's possible to disable FLS for individual fields 3591292 Split SecureDml error handlers. Tidy IDml references. 7e057f4 Default UoW now is secure - both FLS and CUD bdba10d Added sketch of SecureDml - an Idml implementation for use with the Unit of Work 0797902 Merge pull request #2 from OrtooApps/feature/adding-sobject-fabricator 287e8c2 Removed TODO entries fc6a887 Added a missing unit test to Fabricated SObject f0bbfd1 Added documentation to the object describer and tried making an internal class private 1d0d3c9 Added tests for the persistence of fabricated SObjects 4a244d4 Added tests for the persistence of fabricated SObjects b511b52 Added docs to the register 96dd85e Further reduction in code footprint 7427970 Reducing the footprint of sfab b9e86a5 Reworked the singleton nature of SobjectRegister 533b206 Update to TODO 918873c Removed the ortoo_FabricatedSObject - have to accept that the library is bespoke ca66375 Updated TODO 32b1f5e Fixed the fabricator tests 9ce7271 Can persist an object with a parent specified da10ff0 Resolve the relationships on persist of fabricated sobjects cd5e5b8 Added calculation of DML order in fabricatedSObjectRegister using a DirectedGraph e61067f Starting to sketch out how to persist objects that have been fabricated 866bd72 Moved fabricator tests into a sub-directory 5f3f525 Added basic sobject fabricator c7c3ec6 reversed old commit 932db0c this is a tiny reversable commit dfbc9bc Improving test coverage and fixing bugs 47050d0 Merge branch 'main' of github.com:OrtooApps/framework 7833094 Always delete the scratch org, and only keep them for 3 days, just in case 8ae1762 Merge pull request #1 from OrtooApps/feature/test-a-failing-test ae2dee9 Fixed the failing test 7d328de Added back grep and put in a pipestatus 0e921fe Remove grep, see if the build fails d466b70 Back to the original format - you get more info on failure 680e5d5 fix branches matching 9eb17b5 Added a purposeful fail to see the rendering ddfcb0b Add publish of results as JUnit 5524377 Grep out the passes in the automated build and run test d44b177 Reference the framework folder in the deploy 59624e4 Removed force-app from the deploy command - not sure we need to specify anything 12f89dc Added execute permissions to build script 8cfb4f5 Added code coverage request to the command line run all 6f449d4 Fixed package utils bug that points out how bad the unit test is. Shame - but can't test namespaces 35c4928 Added script for running all tests and only showing the fails 8e04a6a Removed bad utils class left over from experiments 8654fd2 Added auto-deploy and test in git 8109082 Initial Commit to new repo git-subtree-dir: subfolders/fflib git-subtree-split: 573efa3b965e444ab1899529cba2f9588ac42fc3 * moved subfolders over to subtrees * fixed deploy paths and swtiched from deploy to push * removed fflib so we can more it * Added PoC of dirty fields added to registerNew and registerUpsert * Remove dirty fields from registerNew - don't think it ever makes sense * Added tests for the new upsert with dirty fields * Added new upsert with dirty fields to the UOW interface and dynamic version Added docs to upsert with dirty fields * Added missing mock methods * Fixed broken test and clarified behaviour in doc comments --- .../main/classes/fflib_ISObjectUnitOfWork.cls | 22 +++- .../main/classes/fflib_SObjectUnitOfWork.cls | 54 +++++++- .../classes/fflib_SObjectUnitOfWorkTest.cls | 118 ++++++++++++++++++ .../test/classes/mocks/fflib_SObjectMocks.cls | 10 ++ 4 files changed, 198 insertions(+), 6 deletions(-) diff --git a/sfdx-source/apex-common/main/classes/fflib_ISObjectUnitOfWork.cls b/sfdx-source/apex-common/main/classes/fflib_ISObjectUnitOfWork.cls index a18e1256f40..332241c4b4a 100644 --- a/sfdx-source/apex-common/main/classes/fflib_ISObjectUnitOfWork.cls +++ b/sfdx-source/apex-common/main/classes/fflib_ISObjectUnitOfWork.cls @@ -94,7 +94,9 @@ public interface fflib_ISObjectUnitOfWork * If the records are previously registered as dirty, the dirty fields on the records in this call will overwrite * the values of the previously registered dirty records * - * @param records A list of existing records + * Note: you should avoid mixing SObject types when registering dirty fields + * + * @param records A list of existing records * @param dirtyFields The fields to update if record is already registered **/ void registerDirty(List records, List dirtyFields); @@ -141,12 +143,30 @@ public interface fflib_ISObjectUnitOfWork * @param record An new or existing record **/ void registerUpsert(SObject record); + /** + * Register a new or existing record to be inserted/updated during the commitWork method, with a specific list of dirty fields to update / commit + * + * NOTE: If the record requires an insert, the dirtyFields list is ignored and ALL field values are included on the insert + * + * @param record A new or existing record + * @param dirtyFields A list of modified fields + **/ + void registerUpsert( SObject record, List dirtyFields ); /** * Register a list of mix of new and existing records to be upserted during the commitWork method * * @param records A list of mix of existing and new records **/ void registerUpsert(List records); + /** + * Register a list of mix of new and existing records to be inserted updated during the commitWork method, with a specific list of dirty fields to update / commit + * + * NOTE: If a record requires an insert, the dirtyFields list is ignored and ALL field values are included on the insert + * + * @param records A list of mix of new and existing records + * @param dirtyFields A list of modified fields + **/ + void registerUpsert( List records, List dirtyFields ); /** * Register an existing record to be deleted during the commitWork method * diff --git a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls index e5b85b32dd0..065397c8fa0 100644 --- a/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls +++ b/sfdx-source/apex-common/main/classes/fflib_SObjectUnitOfWork.cls @@ -349,8 +349,10 @@ public virtual class fflib_SObjectUnitOfWork } /** - * Registers the entire records as dirty or just only the dirty fields if the record was already registered + * Registers the stated fields on the records as dirty * + * Note: you should avoid mixing SObject types when registering dirty fields + * * @param records SObjects to register as dirty * @param dirtyFields A list of modified fields */ @@ -363,7 +365,7 @@ public virtual class fflib_SObjectUnitOfWork } /** - * Registers the entire record as dirty or just only the dirty fields if the record was already registered + * Registers the stated fields on the record as dirty * * @param record SObject to register as dirty * @param dirtyFields A list of modified fields @@ -395,9 +397,7 @@ public virtual class fflib_SObjectUnitOfWork // Update the registered record's fields SObject registeredRecord = m_dirtyMapByType.get(sObjectType).get(record.Id); - for (SObjectField dirtyField : dirtyFields) { - registeredRecord.put(dirtyField, record.get(dirtyField)); - } + copyFields( record, registeredRecord, dirtyFields ); m_dirtyMapByType.get(sObjectType).put(record.Id, registeredRecord); } @@ -448,6 +448,26 @@ public virtual class fflib_SObjectUnitOfWork } } + /** + * Register a new or existing record to be inserted/updated during the commitWork method, with a specific list of dirty fields to update / commit + * + * NOTE: If the record requires an insert, the dirtyFields list is ignored and ALL field values are included on the insert + * + * @param record A new or existing record + * @param dirtyFields A list of modified fields + **/ + public virtual void registerUpsert( SObject record, List dirtyFields ) + { + if ( record.Id == null ) + { + registerNew( record, null, null ); + } + else + { + registerDirty( record, dirtyFields ); + } + } + /** * Register a list of mix of new and existing records to be inserted updated during the commitWork method * @@ -461,6 +481,22 @@ public virtual class fflib_SObjectUnitOfWork } } + /** + * Register a list of mix of new and existing records to be inserted updated during the commitWork method, with a specific list of dirty fields to update / commit + * + * NOTE: If a record requires an insert, the dirtyFields list is ignored and ALL field values are included on the insert + * + * @param records A list of mix of new and existing records + * @param dirtyFields A list of modified fields + **/ + public virtual void registerUpsert( List records, List dirtyFields ) + { + for ( SObject record : records ) + { + this.registerUpsert( record, dirtyFields ); + } + } + /** * Register an existing record to be deleted during the commitWork method * @@ -783,6 +819,14 @@ public virtual class fflib_SObjectUnitOfWork } } + private void copyFields( Sobject fromRecord, Sobject toRecord, List fields ) + { + for ( SObjectField thisField : fields ) + { + toRecord.put( thisField, fromRecord.get( thisField ) ); + } + } + private class Relationships { private List m_relationships = new List(); diff --git a/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls b/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls index cf3edd08ba6..d10705fa804 100644 --- a/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls +++ b/sfdx-source/apex-common/test/classes/fflib_SObjectUnitOfWorkTest.cls @@ -658,6 +658,124 @@ private with sharing class fflib_SObjectUnitOfWorkTest System.assertEquals(1, mockDML.recordsForInsert.size()); } + @isTest + private static void testRegisterUpsertForInsertWithDirtyFields() { + + Opportunity newOpportunity = new Opportunity( Name = 'New Opportunity', StageName = 'Closed', CloseDate = System.today() ); + + Test.startTest(); + MockDML mockDML = new MockDML(); + fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(MY_SOBJECTS, mockDML); + uow.registerUpsert( newOpportunity, new List{ Opportunity.Name, Opportunity.StageName } ); + uow.commitWork(); + Test.stopTest(); + + System.assertEquals( 0, mockDML.recordsForUpdate.size() ); + System.assertEquals( 1, mockDML.recordsForInsert.size() ); + + System.assert( + !new fflib_MatcherDefinitions.SObjectsWith( + new List>{ + new Map + { + Opportunity.Id => newOpportunity.Id, + Opportunity.Name => newOpportunity.Name, + Opportunity.StageName => newOpportunity.CloseDate + } + } + ) + .matches( mockDML.recordsForInsert ), + 'registerUpsert with dirty fields will ignore the dirty fields when sending them for insert' + ); + } + + @isTest + private static void testRegisterUpsertForUpdateWithDirtyFields() { + + Opportunity updatedOpportunity = new Opportunity( + Id = fflib_IDGenerator.generate(Schema.Opportunity.SObjectType), + Name = 'Existing Opportunity', + StageName = 'Closed', + CloseDate = System.today()); + + Test.startTest(); + MockDML mockDML = new MockDML(); + fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(MY_SOBJECTS, mockDML); + uow.registerUpsert( updatedOpportunity, new List{ Opportunity.Name, Opportunity.StageName } ); + uow.commitWork(); + Test.stopTest(); + + System.assertEquals( 1, mockDML.recordsForUpdate.size() ); + System.assertEquals( 0, mockDML.recordsForInsert.size() ); + + System.assert( + !new fflib_MatcherDefinitions.SObjectsWith( + new List>{ + new Map + { + Opportunity.Id => updatedOpportunity.Id, + Opportunity.Name => updatedOpportunity.Name, + Opportunity.StageName => '' + } + } + ) + .matches( mockDML.recordsForUpdate ), + 'registerUpsert with dirty fields will send the dirty fields when sending the record them for update' + ); + } + + @isTest + private static void testRegisterUpsertWithMultipleRecordsAndDirtyFields() { + + Opportunity updatedOpportunity = new Opportunity( + Id = fflib_IDGenerator.generate(Schema.Opportunity.SObjectType), + Name = 'Existing Opportunity', + StageName = 'Closed', + CloseDate = System.today()); + + Opportunity newOpportunity = new Opportunity( Name = 'New Opportunity', StageName = 'Closed', CloseDate = System.today() ); + + Test.startTest(); + MockDML mockDML = new MockDML(); + fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(MY_SOBJECTS, mockDML); + uow.registerUpsert( new List{ updatedOpportunity, newOpportunity }, new List{ Opportunity.Name, Opportunity.StageName } ); + uow.commitWork(); + Test.stopTest(); + + System.assertEquals(1, mockDML.recordsForUpdate.size()); + System.assertEquals(1, mockDML.recordsForInsert.size()); + + System.assert( + !new fflib_MatcherDefinitions.SObjectsWith( + new List>{ + new Map + { + Opportunity.Id => newOpportunity.Id, + Opportunity.Name => newOpportunity.Name, + Opportunity.StageName => newOpportunity.CloseDate + } + } + ) + .matches( mockDML.recordsForInsert ), + 'registerUpsert with dirty fields will ignore the dirty fields when sending them for insert' + ); + + System.assert( + !new fflib_MatcherDefinitions.SObjectsWith( + new List>{ + new Map + { + Opportunity.Id => updatedOpportunity.Id, + Opportunity.Name => updatedOpportunity.Name, + Opportunity.StageName => '' + } + } + ) + .matches( mockDML.recordsForUpdate ), + 'registerUpsert with dirty fields will send the dirty fields when sending the record them for update' + ); + } + /** * Assert that actual events exactly match expected events (size, order and name) * and types match expected types diff --git a/sfdx-source/apex-common/test/classes/mocks/fflib_SObjectMocks.cls b/sfdx-source/apex-common/test/classes/mocks/fflib_SObjectMocks.cls index b9e13ab12cb..fbc7b1a56e1 100644 --- a/sfdx-source/apex-common/test/classes/mocks/fflib_SObjectMocks.cls +++ b/sfdx-source/apex-common/test/classes/mocks/fflib_SObjectMocks.cls @@ -155,6 +155,11 @@ public class fflib_SObjectMocks mocks.mockVoidMethod(this, 'registerUpsert', new List {List.class}, new List {record}); } + public void registerUpsert(SObject record, List dirtyFields ) + { + mocks.mockVoidMethod(this, 'registerUpsert', new List {List.class}, new List {record, dirtyFields}); + } + public void registerEmptyRecycleBin(SObject record) { mocks.mockVoidMethod(this, 'registerEmptyRecycleBin', new List {List.class}, new List {record}); @@ -170,6 +175,11 @@ public class fflib_SObjectMocks mocks.mockVoidMethod(this, 'registerUpsert', new List {List.class}, new List {records}); } + public void registerUpsert(List records, List dirtyFields ) + { + mocks.mockVoidMethod(this, 'registerUpsert', new List {List.class}, new List {records, dirtyFields}); + } + public void registerDeleted(SObject record) { mocks.mockVoidMethod(this, 'registerDeleted', new List {SObject.class}, new List {record});