├── tools ├── apexdoc │ ├── header.html │ ├── apexdoc.jar │ └── apexdoc.bat └── codeclimate │ └── apex │ ├── performance.xml │ ├── apexunit.xml │ └── style.xml ├── .gitignore ├── .atom-build.yml ├── nebula-app-framework ├── main │ ├── logger │ │ ├── classes │ │ │ ├── Logger.cls-meta.xml │ │ │ └── Logger.cls │ │ ├── tabs │ │ │ └── NebulaLog__c.tab-meta.xml │ │ ├── applications │ │ │ └── Nebula.app-meta.xml │ │ ├── objects │ │ │ ├── NebulaLoggerSettings__c │ │ │ │ ├── fields │ │ │ │ │ └── EnableLogging__c.field-meta.xml │ │ │ │ └── NebulaLoggerSettings__c.object-meta.xml │ │ │ └── NebulaLog__c │ │ │ │ ├── fields │ │ │ │ ├── InitialClass__c.field-meta.xml │ │ │ │ └── TransactionId__c.field-meta.xml │ │ │ │ ├── listViews │ │ │ │ ├── My_Logs.listView-meta.xml │ │ │ │ └── All_Logs.listView-meta.xml │ │ │ │ └── NebulaLog__c.object-meta.xml │ │ └── layouts │ │ │ └── NebulaLog__c-Log Layout.layout-meta.xml │ ├── utilities │ │ ├── classes │ │ │ ├── UUID.cls-meta.xml │ │ │ ├── Environment.cls-meta.xml │ │ │ ├── INebulaCore.cls-meta.xml │ │ │ ├── NebulaCore.cls-meta.xml │ │ │ ├── Scheduler.cls-meta.xml │ │ │ ├── CollectionUtils.cls-meta.xml │ │ │ ├── NebulaSettings.cls-meta.xml │ │ │ ├── ISObjectRecordTypes.cls-meta.xml │ │ │ ├── SObjectRecordTypes.cls-meta.xml │ │ │ ├── INebulaCore.cls │ │ │ ├── ISObjectRecordTypes.cls │ │ │ ├── NebulaCore.cls │ │ │ ├── Scheduler.cls │ │ │ ├── Environment.cls │ │ │ ├── UUID.cls │ │ │ ├── SObjectRecordTypes.cls │ │ │ ├── NebulaSettings.cls │ │ │ └── CollectionUtils.cls │ │ └── objects │ │ │ ├── NebulaSObjectQueryBuilderSettings__c │ │ │ ├── NebulaSObjectQueryBuilderSettings__c.object-meta.xml │ │ │ └── fields │ │ │ │ └── IncludeCommonFields__c.field-meta.xml │ │ │ └── NebulaRecordTypesSettings__c │ │ │ ├── NebulaRecordTypesSettings__c.object-meta.xml │ │ │ └── fields │ │ │ ├── IncludeManagedRecordTypes__c.field-meta.xml │ │ │ └── LazyLoad__c.field-meta.xml │ ├── dml-management │ │ └── classes │ │ │ ├── DML.cls-meta.xml │ │ │ ├── IDML.cls-meta.xml │ │ │ ├── IDML.cls │ │ │ └── DML.cls │ ├── query-and-search │ │ └── classes │ │ │ ├── QueryDate.cls-meta.xml │ │ │ ├── QueryField.cls-meta.xml │ │ │ ├── IQueryField.cls-meta.xml │ │ │ ├── IQueryFilter.cls-meta.xml │ │ │ ├── QueryBuilder.cls-meta.xml │ │ │ ├── QueryFilter.cls-meta.xml │ │ │ ├── QueryOperator.cls-meta.xml │ │ │ ├── QuerySortOrder.cls-meta.xml │ │ │ ├── ISObjectQueryBuilder.cls-meta.xml │ │ │ ├── ISearchQueryBuilder.cls-meta.xml │ │ │ ├── QueryDateLiteral.cls-meta.xml │ │ │ ├── QueryFilterScope.cls-meta.xml │ │ │ ├── QueryNullSortOrder.cls-meta.xml │ │ │ ├── QuerySearchGroup.cls-meta.xml │ │ │ ├── SObjectQueryBuilder.cls-meta.xml │ │ │ ├── SearchQueryBuilder.cls-meta.xml │ │ │ ├── IQueryArgumentFormatter.cls-meta.xml │ │ │ ├── QueryArgumentFormatter.cls-meta.xml │ │ │ ├── SObjectFieldDescriber.cls-meta.xml │ │ │ ├── AggregateResultQueryBuilder.cls-meta.xml │ │ │ ├── IAggregateResultQueryBuilder.cls-meta.xml │ │ │ ├── ISearchQueryBuilder.cls │ │ │ ├── QueryNullSortOrder.cls │ │ │ ├── QuerySortOrder.cls │ │ │ ├── IQueryField.cls │ │ │ ├── IQueryArgumentFormatter.cls │ │ │ ├── QuerySearchGroup.cls │ │ │ ├── QueryFilterScope.cls │ │ │ ├── IQueryFilter.cls │ │ │ ├── QueryOperator.cls │ │ │ ├── QueryField.cls │ │ │ ├── ISObjectQueryBuilder.cls │ │ │ ├── IAggregateResultQueryBuilder.cls │ │ │ ├── SObjectFieldDescriber.cls │ │ │ ├── SearchQueryBuilder.cls │ │ │ ├── QueryDate.cls │ │ │ ├── QueryArgumentFormatter.cls │ │ │ ├── QueryDateLiteral.cls │ │ │ ├── QueryBuilder.cls │ │ │ └── QueryFilter.cls │ ├── sobject-repositories │ │ └── classes │ │ │ ├── ISObjectRepository.cls-meta.xml │ │ │ ├── SObjectRepository.cls-meta.xml │ │ │ ├── ISObjectRepository.cls │ │ │ └── SObjectRepository.cls │ └── trigger-handlers │ │ ├── classes │ │ ├── ISObjectTriggerHandler.cls-meta.xml │ │ ├── SObjectTriggerHandler.cls-meta.xml │ │ └── ISObjectTriggerHandler.cls │ │ └── objects │ │ └── NebulaTriggerHandlerSettings__c │ │ ├── fields │ │ ├── PreventRecursion__c.field-meta.xml │ │ ├── HandlerClassesToSkip__c.field-meta.xml │ │ └── ExecuteTriggers__c.field-meta.xml │ │ └── NebulaTriggerHandlerSettings__c.object-meta.xml └── tests │ ├── logger │ └── classes │ │ ├── Logger_Tests.cls-meta.xml │ │ └── Logger_Tests.cls │ ├── utilities │ └── classes │ │ ├── UUID_Tests.cls-meta.xml │ │ ├── Scheduler_Tests.cls-meta.xml │ │ ├── CollectionUtils_Tests.cls-meta.xml │ │ ├── Environment_Tests.cls-meta.xml │ │ ├── NebulaSettings_Tests.cls-meta.xml │ │ ├── SObjectRecordTypes_Tests.cls-meta.xml │ │ ├── Scheduler_Tests.cls │ │ ├── Environment_Tests.cls │ │ ├── UUID_Tests.cls │ │ ├── NebulaSettings_Tests.cls │ │ └── SObjectRecordTypes_Tests.cls │ ├── dml-management │ └── classes │ │ ├── DMLMock.cls-meta.xml │ │ ├── DMLMock_Tests.cls-meta.xml │ │ ├── DMLMock_Tests.cls │ │ └── DMLMock.cls │ ├── testing-utilities │ └── classes │ │ ├── TestingUtils.cls-meta.xml │ │ └── TestingUtils.cls │ ├── query-and-search │ └── classes │ │ ├── QueryDate_Tests.cls-meta.xml │ │ ├── QueryField_Tests.cls-meta.xml │ │ ├── QueryFilter_Tests.cls-meta.xml │ │ ├── QueryOperator_Tests.cls-meta.xml │ │ ├── QueryDateLiteral_Tests.cls-meta.xml │ │ ├── SearchQueryBuilder_Tests.cls-meta.xml │ │ ├── QueryArgumentFormatter_Tests.cls-meta.xml │ │ ├── SObjectFieldDescriber_Tests.cls-meta.xml │ │ ├── SObjectQueryBuilder_Tests.cls-meta.xml │ │ ├── SObjectRepositoryMocks_Tests.cls-meta.xml │ │ ├── AggregateResultQueryBuilder_Tests.cls-meta.xml │ │ ├── QueryField_Tests.cls │ │ ├── SObjectRepositoryMocks_Tests.cls │ │ ├── QueryOperator_Tests.cls │ │ ├── SearchQueryBuilder_Tests.cls │ │ ├── SObjectFieldDescriber_Tests.cls │ │ ├── AggregateResultQueryBuilder_Tests.cls │ │ ├── QueryDate_Tests.cls │ │ ├── QueryFilter_Tests.cls │ │ └── QueryArgumentFormatter_Tests.cls │ ├── sobject-repositories │ └── classes │ │ ├── SObjectRepositoryMocks.cls-meta.xml │ │ ├── SObjectRepository_Tests.cls-meta.xml │ │ └── SObjectRepositoryMocks.cls │ └── trigger-handlers │ └── classes │ ├── SObjectTriggerHandler_Tests.cls-meta.xml │ └── SObjectTriggerHandler_Tests.cls ├── sfdx-project.json ├── .forceignore ├── .codeclimate.yml ├── .gitattributes ├── manifest └── package.xml ├── .travis.yml ├── LICENSE └── README.md /tools/apexdoc/header.html: -------------------------------------------------------------------------------- 1 | testing -------------------------------------------------------------------------------- /tools/apexdoc/apexdoc.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jongpie/NebulaFramework/HEAD/tools/apexdoc/apexdoc.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Folders to exclude 2 | .settings/ 3 | .sfdx/ 4 | .vscode/ 5 | 6 | # Files to exclude 7 | *.log 8 | 9 | -------------------------------------------------------------------------------- /tools/apexdoc/apexdoc.bat: -------------------------------------------------------------------------------- 1 | java -jar apexdoc.jar -s ..\..\src\classes -t ..\.. -p public;protected -g https://github.com/jongpie/NebulaFramework -a header.html -------------------------------------------------------------------------------- /.atom-build.yml: -------------------------------------------------------------------------------- 1 | cwd: '{PROJECT_PATH}' 2 | cmd: java -jar .\tools\apexdoc\apexdoc.jar -s .\src\classes -t . -p public;protected -g https://github.com/jongpie/NebulaFramework/blob/master/src/classes/ -a header.html -------------------------------------------------------------------------------- /nebula-app-framework/main/logger/classes/Logger.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/UUID.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/dml-management/classes/DML.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/dml-management/classes/IDML.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/Environment.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/INebulaCore.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/NebulaCore.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/Scheduler.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/logger/classes/Logger_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/utilities/classes/UUID_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryDate.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryField.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/CollectionUtils.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/NebulaSettings.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/dml-management/classes/DMLMock.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/utilities/classes/Scheduler_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/IQueryField.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/IQueryFilter.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryBuilder.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryFilter.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryOperator.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QuerySortOrder.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/ISObjectRecordTypes.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/SObjectRecordTypes.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/dml-management/classes/DMLMock_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/testing-utilities/classes/TestingUtils.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/utilities/classes/CollectionUtils_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/utilities/classes/Environment_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/utilities/classes/NebulaSettings_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/ISObjectQueryBuilder.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/ISearchQueryBuilder.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryDateLiteral.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryFilterScope.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryNullSortOrder.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QuerySearchGroup.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/SObjectQueryBuilder.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/SearchQueryBuilder.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/QueryDate_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/QueryField_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/QueryFilter_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/QueryOperator_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/utilities/classes/SObjectRecordTypes_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/IQueryArgumentFormatter.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryArgumentFormatter.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/SObjectFieldDescriber.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/sobject-repositories/classes/ISObjectRepository.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/sobject-repositories/classes/SObjectRepository.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/trigger-handlers/classes/ISObjectTriggerHandler.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/trigger-handlers/classes/SObjectTriggerHandler.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/QueryDateLiteral_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/SearchQueryBuilder_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/AggregateResultQueryBuilder.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/IAggregateResultQueryBuilder.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/QueryArgumentFormatter_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/SObjectFieldDescriber_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/SObjectQueryBuilder_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/SObjectRepositoryMocks_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/sobject-repositories/classes/SObjectRepositoryMocks.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/sobject-repositories/classes/SObjectRepository_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/trigger-handlers/classes/SObjectTriggerHandler_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/AggregateResultQueryBuilder_Tests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /nebula-app-framework/main/logger/tabs/NebulaLog__c.tab-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | false 5 | Custom83: Pencil 6 | 7 | -------------------------------------------------------------------------------- /sfdx-project.json: -------------------------------------------------------------------------------- 1 | { 2 | "packageDirectories": [ 3 | { 4 | "path": "nebula-app-framework", 5 | "default": true 6 | } 7 | ], 8 | "namespace": "Nebula", 9 | "sfdcLoginUrl": "https://nebulaframework-dev.my.salesforce.com", 10 | "sourceApiVersion": "39.0" 11 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/logger/applications/Nebula.app-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | NebulaLog__c 4 | Large 5 | 6 | NebulaLog__c 7 | 8 | -------------------------------------------------------------------------------- /.forceignore: -------------------------------------------------------------------------------- 1 | # List files or directories below to ignore them when running force:source:push, force:source:pull, and force:source:status 2 | # More information: https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_exclude_source.htm 3 | # 4 | 5 | package.xml 6 | 7 | # LWC configuration files 8 | **/jsconfig.json 9 | **/.eslintrc.json 10 | 11 | # LWC Jest 12 | **/__tests__/** -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | apexmetrics: 3 | enabled: true 4 | config: 5 | rulesets: "./tools/codeclimate/apex/apexunit.xml,./tools/codeclimate/apex/complexity.xml,./tools/codeclimate/apex/performance.xml,./tools/codeclimate/apex/security.xml,./tools/codeclimate/apex/style.xml" 6 | exclude_paths: 7 | - "**Tests.cls" 8 | ratings: 9 | paths: 10 | - "**.cls" 11 | - "**.trigger" 12 | -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/objects/NebulaSObjectQueryBuilderSettings__c/NebulaSObjectQueryBuilderSettings__c.object-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hierarchy 4 | false 5 | 6 | Public 7 | 8 | -------------------------------------------------------------------------------- /nebula-app-framework/main/logger/objects/NebulaLoggerSettings__c/fields/EnableLogging__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | EnableLogging__c 4 | true 5 | false 6 | 7 | false 8 | Checkbox 9 | 10 | -------------------------------------------------------------------------------- /nebula-app-framework/main/logger/objects/NebulaLoggerSettings__c/NebulaLoggerSettings__c.object-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hierarchy 4 | Controls the behavior of the class Logger.cls 5 | false 6 | 7 | Public 8 | 9 | -------------------------------------------------------------------------------- /nebula-app-framework/main/trigger-handlers/objects/NebulaTriggerHandlerSettings__c/fields/PreventRecursion__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | PreventRecursion__c 4 | true 5 | false 6 | 7 | false 8 | Checkbox 9 | 10 | -------------------------------------------------------------------------------- /nebula-app-framework/main/trigger-handlers/objects/NebulaTriggerHandlerSettings__c/fields/HandlerClassesToSkip__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | HandlerClassesToSkip__c 4 | false 5 | 6 | false 7 | false 8 | TextArea 9 | 10 | -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/objects/NebulaSObjectQueryBuilderSettings__c/fields/IncludeCommonFields__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | IncludeCommonFields__c 4 | true 5 | false 6 | 7 | false 8 | Checkbox 9 | 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default line return behavior, in case people don't have core.autocrlf set. 2 | * text=auto eol=lf 3 | 4 | # Common git file types 5 | .gitattributes text eol=lf 6 | .gitignore text eol=lf 7 | *.md text eol=lf 8 | 9 | # Salesforce-specfic file types 10 | *.app text eol=lf 11 | *.cls text eol=lf 12 | *.cmp text eol=lf 13 | *.component text eol=lf 14 | *.css text eol=lf 15 | *.html text eol=lf 16 | *.js text eol=lf 17 | *.page text eol=lf 18 | *.trigger text eol=lf 19 | *.xml text eol=lf -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/objects/NebulaRecordTypesSettings__c/NebulaRecordTypesSettings__c.object-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hierarchy 4 | Controls the behavior of the class SObjectRecordTypes.cls 5 | false 6 | 7 | Public 8 | 9 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/ISearchQueryBuilder.cls: -------------------------------------------------------------------------------- 1 | public interface ISearchQueryBuilder { 2 | 3 | ISearchQueryBuilder cacheResults(); 4 | 5 | ISearchQueryBuilder inQuerySearchGroup(QuerySearchGroup searchGroup); 6 | ISearchQueryBuilder withHighlight(Boolean withHighlight); 7 | ISearchQueryBuilder withSpellCorrection(Boolean withSpellCorrection); 8 | 9 | String getQuery(); 10 | 11 | List getFirstSearchResult(); 12 | List> getSearchResults(); 13 | 14 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/trigger-handlers/objects/NebulaTriggerHandlerSettings__c/NebulaTriggerHandlerSettings__c.object-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hierarchy 4 | Controls the behavior of the class SObjectTriggerHandler.cls 5 | false 6 | 7 | Public 8 | 9 | -------------------------------------------------------------------------------- /nebula-app-framework/main/logger/objects/NebulaLog__c/fields/InitialClass__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | InitialClass__c 4 | false 5 | 6 | 255 7 | false 8 | false 9 | false 10 | Text 11 | false 12 | 13 | -------------------------------------------------------------------------------- /nebula-app-framework/main/logger/objects/NebulaLog__c/listViews/My_Logs.listView-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | My_Logs 4 | NAME 5 | TransactionId__c 6 | InitialClass__c 7 | CREATED_DATE 8 | UPDATEDBY_USER 9 | LAST_UPDATE 10 | Mine 11 | 12 | 13 | -------------------------------------------------------------------------------- /nebula-app-framework/main/logger/objects/NebulaLog__c/fields/TransactionId__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | TransactionId__c 4 | false 5 | true 6 | 7 | 36 8 | true 9 | false 10 | false 11 | Text 12 | true 13 | 14 | -------------------------------------------------------------------------------- /nebula-app-framework/main/logger/objects/NebulaLog__c/listViews/All_Logs.listView-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | All_Logs 4 | NAME 5 | TransactionId__c 6 | InitialClass__c 7 | CREATEDBY_USER 8 | CREATED_DATE 9 | UPDATEDBY_USER 10 | LAST_UPDATE 11 | Everything 12 | 13 | 14 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryNullSortOrder.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public enum QueryNullSortOrder { FIRST, LAST } -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QuerySortOrder.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public enum QuerySortOrder { ASCENDING, DESCENDING } -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/IQueryField.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public interface IQueryField { 14 | 15 | String getValue(); 16 | 17 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/trigger-handlers/classes/ISObjectTriggerHandler.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Trigger Handler 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public interface ISObjectTriggerHandler { 14 | 15 | void execute(); 16 | 17 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/IQueryArgumentFormatter.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public interface IQueryArgumentFormatter { 14 | 15 | String getValue(); 16 | 17 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QuerySearchGroup.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public enum QuerySearchGroup { ALL_FIELDS, NAME_FIELDS, EMAIL_FIELDS, PHONE_FIELDS, SIDEBAR_FIELDS } -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/INebulaCore.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Configuration 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public interface INebulaCore { 14 | 15 | String getClassName(); 16 | NebulaCore.Module getClassModule(); 17 | 18 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/ISObjectRecordTypes.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Record Types 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public interface ISObjectRecordTypes { 14 | 15 | // Setup methods 16 | Schema.SObjectType getSObjectType(); 17 | 18 | // Getter methods 19 | Map getAllById(); 20 | Map getAllByDeveloperName(); 21 | 22 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/objects/NebulaRecordTypesSettings__c/fields/IncludeManagedRecordTypes__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | IncludeManagedRecordTypes__c 4 | true 5 | Determines if managed record types are included in queries. If you do not use any managed record types, then you can disable this. 6 | false 7 | Determines if managed record types are included in queries. If you do not use any managed record types, then you can disable this. 8 | 9 | false 10 | Checkbox 11 | 12 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryFilterScope.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description Enum of possible values for SOQL's optional USING SCOPE 11 | * Salesforce docs: developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_using_scope.htm 12 | * 13 | */ 14 | public enum QueryFilterScope { EVERYTHING, DELEGATED, TEAM, MINE, MY_TERRITORY, MY_TEAM_TERRITORY } -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/objects/NebulaRecordTypesSettings__c/fields/LazyLoad__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | LazyLoad__c 4 | true 5 | Determines if a separate query is executed for each SObject (lazy load enabled), or if a single query is used to pre-load all record types (lazy load disabled) 6 | false 7 | Determines if a separate query is executed for each SObject (lazy load enabled), or if a single query is used to pre-load all record types (lazy load disabled) 8 | 9 | false 10 | Checkbox 11 | 12 | -------------------------------------------------------------------------------- /nebula-app-framework/main/trigger-handlers/objects/NebulaTriggerHandlerSettings__c/fields/ExecuteTriggers__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ExecuteTriggers__c 4 | true 5 | Controls if any trigger handler classes run its execute() method. This should always be enabled unless you are doing a data fix, etc and need to temporarily disable all triggers. 6 | false 7 | Controls if any trigger handler classes run its execute() method. This should always be enabled unless you are doing a data fix, etc and need to temporarily disable all triggers. 8 | 9 | false 10 | Checkbox 11 | 12 | -------------------------------------------------------------------------------- /manifest/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | * 5 | ApexClass 6 | 7 | 8 | Nebula 9 | CustomApplication 10 | 11 | 12 | NebulaLog__c 13 | NebulaLoggerSettings__c 14 | NebulaRecordTypesSettings__c 15 | NebulaSObjectQueryBuilderSettings__c 16 | NebulaTriggerHandlerSettings__c 17 | CustomObject 18 | 19 | 20 | NebulaLog__c 21 | CustomTab 22 | 23 | 24 | NebulaLog__c-Log Layout 25 | Layout 26 | 27 | 39.0 28 | 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: true 2 | os: trusty 3 | cache: false 4 | 5 | env: 6 | - URL=https://developer.salesforce.com/media/salesforce-cli/sfdx-linux-amd64.tar.xz 7 | 8 | before_install: 9 | #- openssl aes-256-cbc -K $encrypted_b1fbf710b918_key -iv $encrypted_b1fbf710b918_iv 10 | # -in assets/server.key.enc -out assets/server.key -d 11 | - export SFDX_AUTOUPDATE_DISABLE=false 12 | - export SFDX_USE_GENERIC_UNIX_KEYCHAIN=true 13 | - export SFDX_DOMAIN_RETRY=300 14 | - export SFDX_DISABLE_APP_HUB=true 15 | - export SFDX_LOG_LEVEL=DEBUG 16 | - mkdir sfdx 17 | - wget -qO- $URL | tar xJ -C sfdx --strip-components 1 18 | - "./sfdx/install" 19 | - export PATH=./sfdx/$(pwd):$PATH 20 | - sfdx --version 21 | - sfdx plugins --core 22 | - echo $SFDX_URL > sfdx_url_file 23 | - sfdx force:auth:sfdxurl:store --sfdxurlfile sfdx_url_file --setalias nebula_ci 24 | 25 | script: 26 | - sfdx force:config:set apiVersion=39.0 27 | - sfdx force:source:deploy --checkonly --manifest ./manifest/package.xml --testlevel RunLocalTests --targetusername nebula_ci --verbose 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Jonathan Gillespie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/QueryField_Tests.cls: -------------------------------------------------------------------------------- 1 | @isTest 2 | private class QueryField_Tests { 3 | 4 | @isTest 5 | static void it_should_return_string_for_sobject_field_name() { 6 | System.assertEquals('CreatedDate', new QueryField(Schema.Lead.CreatedDate).getValue()); 7 | } 8 | 9 | @isTest 10 | static void it_should_return_string_for_parent_sobject_field_name() { 11 | List fieldChain = new List{ 12 | Schema.Contact.AccountId, Schema.Account.CreatedById, Schema.User.Name 13 | }; 14 | System.assertEquals('Account.CreatedBy.Name', new QueryField(fieldChain).getValue()); 15 | } 16 | 17 | @isTest 18 | static void it_should_be_callable_multiple_times_without_pop_removing_field_references() { 19 | List fieldChain = new List{ 20 | Schema.Contact.AccountId, Schema.Account.Name 21 | }; 22 | QueryField queryField = new QueryField(fieldChain); 23 | String expected = 'Account.Name'; 24 | for(Integer i = 0; i < 5; i++) { 25 | System.assertEquals(expected, queryField.getValue()); 26 | } 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/IQueryFilter.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public interface IQueryFilter { 14 | 15 | // Setter methods 16 | IQueryFilter filterByField(QueryField queryField, QueryOperator operator, Object providedValue); 17 | IQueryFilter filterByQueryDate(QueryDate queryDateToFilter, QueryOperator operator, Integer providedValue); 18 | IQueryFilter filterBySubquery(QueryOperator inOrNotIn, Schema.SObjectField lookupFieldOnRelatedSObject); 19 | IQueryFilter filterBySubquery(Schema.SObjectField lookupField, QueryOperator inOrNotIn, Schema.SObjectField lookupFieldOnRelatedSObject); 20 | 21 | IQueryFilter andFilterBy(List queryFilters); 22 | IQueryFilter orFilterBy(List queryFilters); 23 | 24 | // Getter methods 25 | String getValue(); 26 | 27 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/sobject-repositories/classes/ISObjectRepository.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Repository 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public interface ISObjectRepository { 14 | 15 | // Setup methods 16 | Schema.SObjectType getSObjectType(); 17 | 18 | // SOQL 19 | SObject getById(Id recordId); 20 | List getById(List recordIdList); 21 | List get(IQueryFilter queryFilter); 22 | List get(List queryFilters); 23 | List getByIdAndQueryFilters(Set idSet, List queryFilters); 24 | List getByIdAndQueryFilters(List idList, List queryFilters); 25 | 26 | // SOSL 27 | List getSearchResults(String searchTerm); 28 | List getSearchResults(String searchTerm, QuerySearchGroup searchGroup); 29 | List getSearchResults(String searchTerm, QuerySearchGroup searchGroup, List queryFilters); 30 | 31 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/NebulaCore.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Configuration 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public abstract class NebulaCore implements INebulaCore { 14 | 15 | public enum Module { QUERY_BUILDER, RECORD_TYPES, REPOSITORY, SETTINGS, TRIGGER_HANDLER } 16 | 17 | public static final String TRANSACTION_ID; 18 | public static String INITIAL_CLASS {get; private set;} 19 | 20 | static { 21 | NebulaCore.TRANSACTION_ID = new UUID().getValue(); 22 | System.debug('NebulaCore.TRANSACTION_ID=' + NebulaCore.TRANSACTION_ID); 23 | } 24 | 25 | protected final Module currentModule; 26 | 27 | protected NebulaCore() { 28 | if(NebulaCore.INITIAL_CLASS == null) NebulaCore.INITIAL_CLASS = this.getClassName(); 29 | } 30 | 31 | public String getClassName() { 32 | return String.valueOf(this).split(':')[0]; 33 | } 34 | 35 | public NebulaCore.Module getClassModule() { 36 | return this.currentModule; 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/dml-management/classes/IDML.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Repository 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public interface IDML { 14 | 15 | List insertRecords(SObject record); 16 | List insertRecords(List recordList); 17 | List updateRecords(SObject record); 18 | List updateRecords(List recordList); 19 | List upsertRecords(SObject record); 20 | List upsertRecords(List recordList); 21 | List undeleteRecords(SObject record); 22 | List undeleteRecords(List recordList); 23 | List deleteRecords(SObject record); 24 | List deleteRecords(List recordList); 25 | List hardDeleteRecords(SObject record); 26 | List hardDeleteRecords(List recordList); 27 | 28 | } -------------------------------------------------------------------------------- /nebula-app-framework/tests/utilities/classes/Scheduler_Tests.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | @isTest 6 | private class Scheduler_Tests { 7 | 8 | private static final String SCHEDULABLE_JOB_ID = '7'; 9 | private static final String DAILY_CRON_EXP = '0 59 23 * * ?'; 10 | 11 | private class TestSchedulable implements Schedulable { 12 | public void execute(SchedulableContext sc) {} 13 | } 14 | 15 | @isTest 16 | static void it_should_successfully_schedule_daily() { 17 | String jobId = new Scheduler(new TestSchedulable()).scheduleDaily(SCHEDULABLE_JOB_ID, '59', '23'); 18 | CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered, NextFireTime FROM CronTrigger WHERE Id = :jobId]; 19 | System.assertEquals(DAILY_CRON_EXP, ct.CronExpression); 20 | } 21 | 22 | @isTest 23 | static void it_should_successfully_schedule_hourly() { 24 | String jobId = new Scheduler(new TestSchedulable()).scheduleHourly(SCHEDULABLE_JOB_ID, '59'); 25 | CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered, NextFireTime FROM CronTrigger WHERE Id = :jobId]; 26 | System.assertEquals(String.format(Scheduler.HOURLY_CRON, new List{'59'}), ct.CronExpression); 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/Scheduler.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Asynchronous Apex 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public class Scheduler { 14 | 15 | //CRON order - Seconds, Minutes, Hours, Day_of_month, Month, Day_of_week, Optional_year 16 | public final static String DAILY_CRON = '0 {0} {1} * * ?'; 17 | public final static String HOURLY_CRON = '0 {0} * * * ?'; 18 | private final Schedulable scheduledClass; 19 | 20 | public Scheduler (Schedulable scheduledClass) { 21 | this.scheduledClass = scheduledClass; 22 | } 23 | 24 | public String scheduleHourly(String jobName, String startingMinuteInHour) { 25 | String hourlyCRON = String.format(HOURLY_CRON, new List{startingMinuteInHour}); 26 | return this.schedule(jobName, hourlyCRON); 27 | } 28 | 29 | public String scheduleDaily(String jobName, String startingHour, String startingMinute) { 30 | String dailyCRON = String.format(DAILY_CRON, new List{startingHour, startingMinute}); 31 | return this.schedule(jobName, dailyCRON); 32 | } 33 | 34 | public String schedule(String jobName, String cronExpression) { 35 | return System.schedule(jobName, cronExpression, this.scheduledClass); 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /nebula-app-framework/tests/utilities/classes/Environment_Tests.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | @isTest 6 | private class Environment_Tests { 7 | 8 | @isTest 9 | static void it_should_return_base_url() { 10 | System.assert(Environment.BaseUrl.endsWithIgnoreCase('.salesforce.com')); 11 | } 12 | 13 | @isTest 14 | static void it_should_return_instance_name() { 15 | Organization org = [SELECT Id, InstanceName FROM Organization]; 16 | System.assertEquals(org.InstanceName, Environment.InstanceName); 17 | } 18 | 19 | @isTest 20 | static void it_should_return_is_sandbox() { 21 | Organization org = [SELECT Id, IsSandbox FROM Organization]; 22 | System.assertEquals(org.IsSandbox, Environment.IsSandbox); 23 | } 24 | 25 | @isTest 26 | static void it_should_return_name() { 27 | Organization org = [SELECT Id, Name FROM Organization]; 28 | System.assertEquals(org.Name, Environment.Name); 29 | } 30 | 31 | @isTest 32 | static void it_should_return_namespace_prefix() { 33 | Organization org = [SELECT Id, NamespacePrefix FROM Organization]; 34 | System.assertEquals(org.NamespacePrefix, Environment.NamespacePrefix); 35 | } 36 | 37 | @isTest 38 | static void it_should_return_type() { 39 | Organization org = [SELECT Id, OrganizationType FROM Organization]; 40 | System.assertEquals(org.OrganizationType, Environment.Type); 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/Environment.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Metadata 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public without sharing class Environment { 14 | 15 | public static String BaseUrl { 16 | get {return URL.getSalesforceBaseUrl().toExternalForm();} 17 | private set; 18 | } 19 | 20 | public static String InstanceName { 21 | get {return organization.InstanceName;} 22 | private set; 23 | } 24 | 25 | /** 26 | * @description Specifies if the org is a production environment or sandbox 27 | * returns true if it's a sandbox 28 | */ 29 | public static Boolean IsSandbox { 30 | get {return organization.IsSandbox;} 31 | private set; 32 | } 33 | 34 | public static String Name { 35 | get {return organization.Name;} 36 | private set; 37 | } 38 | 39 | public static String NamespacePrefix { 40 | get {return organization.NamespacePrefix;} 41 | private set; 42 | } 43 | 44 | public static String Type { 45 | get {return organization.OrganizationType;} 46 | private set; 47 | } 48 | 49 | private static Organization organization { 50 | get { 51 | if(organization == null) organization = [SELECT Id, InstanceName, IsSandbox, Name, NamespacePrefix, OrganizationType FROM Organization]; 52 | return organization; 53 | } 54 | private set; 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /tools/codeclimate/apex/performance.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | The Performance ruleset contains a collection of good practices which should be followed. 9 | 10 | 11 | 16 | 17 | New objects created within loops should be checked to see if they can created outside them and reused. 18 | 19 | 3 20 | 21 | accounts = [SELECT Id FROM Account]; 25 | } 26 | } 27 | } 28 | ]]> 29 | 30 | 31 | 32 | 37 | Avoid DML statements inside loops to avoid hitting the DML governor limit. Instead, try to batch up the data into a list and invoke your DML once on that list of data outside the loop. 38 | 3 39 | 40 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/UUID.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Utils 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public without sharing class UUID { 14 | 15 | public static Boolean isEmpty(String uuid) { 16 | return String.isBlank(uuid) || uuid == '00000000-0000-0000-0000-000000000000'; 17 | } 18 | 19 | public static Boolean isValid(String uuid) { 20 | if(isEmpty(uuid)) return false; 21 | 22 | String uuidRegEx = '[A-F0-9]{8}(?:-[A-F0-9]{4}){3}-[A-F0-9]{12}'; 23 | Pattern uuidPattern = Pattern.compile(uuidRegEx); 24 | Matcher uuidMatcher = uuidPattern.matcher(uuid.toUpperCase()); 25 | 26 | return uuidMatcher.matches(); 27 | } 28 | 29 | private String value; 30 | 31 | public UUID() { 32 | this.setValue(); 33 | } 34 | 35 | public String getValue() { 36 | return value; 37 | } 38 | 39 | private void setValue() { 40 | Blob generatedBlob = Crypto.GenerateAESKey(128); 41 | String hex = EncodingUtil.ConvertTohex(generatedBlob); 42 | this.value = this.formatValue(hex); 43 | } 44 | 45 | private String formatValue(String unformattedValue) { 46 | // Remove any non-alphanumeric characters. Should be unnecessary, but better to be safe than sorry. 47 | unformattedValue = unformattedValue.replaceAll('[^a-zA-Z0-9]', ''); 48 | 49 | String formattedValue = unformattedValue.substring(0, 8) 50 | + '-' + unformattedValue.substring(8, 12) 51 | + '-' + unformattedValue.substring(12, 16) 52 | + '-' + unformattedValue.substring(16, 20) 53 | + '-' + unformattedValue.substring(20); 54 | 55 | return formattedValue.toUpperCase(); 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryOperator.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description Provides all of the operators needed for SOQL/SOSL queries and minimizes the use of strings within the framework 11 | * Salesforce docs: developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_comparisonoperators.htm 12 | * 13 | */ 14 | public without sharing class QueryOperator extends NebulaCore { 15 | 16 | private String value; 17 | 18 | private QueryOperator(String value) { 19 | this.currentModule = NebulaCore.Module.QUERY_BUILDER; 20 | 21 | this.value = value; 22 | } 23 | 24 | public String getValue() { 25 | return this.value; 26 | } 27 | 28 | public static final QueryOperator EQUALS = new QueryOperator('='); 29 | public static final QueryOperator NOT_EQUAL_TO = new QueryOperator('!='); 30 | public static final QueryOperator GREATER_THAN = new QueryOperator('>'); 31 | public static final QueryOperator GREATER_THAN_OR_EQUAL_TO = new QueryOperator('>='); 32 | public static final QueryOperator LESS_THAN = new QueryOperator('<'); 33 | public static final QueryOperator LESS_THAN_OR_EQUAL_TO = new QueryOperator('<='); 34 | public static final QueryOperator IS_IN = new QueryOperator('IN'); 35 | public static final QueryOperator IS_NOT_IN = new QueryOperator('NOT IN'); 36 | public static final QueryOperator INCLUDES = new QueryOperator('INCLUDES'); 37 | public static final QueryOperator EXCLUDES = new QueryOperator('EXCLUDES'); 38 | public static final QueryOperator IS_LIKE = new QueryOperator('LIKE'); 39 | public static final QueryOperator IS_NOT_LIKE = new QueryOperator('NOT LIKE'); 40 | 41 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryField.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description Used to dynamically generate field string for SObject fields, including parent fields. 11 | * 12 | */ 13 | public without sharing class QueryField extends NebulaCore implements IQueryField, Comparable { 14 | 15 | private final List fields; 16 | private final String value; 17 | 18 | public QueryField(SObjectField field) { 19 | this(new List{field}); 20 | } 21 | 22 | public QueryField(List fields) { 23 | this.currentModule = NebulaCore.Module.QUERY_BUILDER; 24 | 25 | this.fields = fields; 26 | this.value = this.parseFields(); 27 | } 28 | 29 | public String getValue() { 30 | return this.value; 31 | } 32 | 33 | private String parseFields() { 34 | if(this.fields.size() == 1) return String.valueOf(this.fields[0]); 35 | 36 | //Remove the last field from the list to iterate through so only the parent relationships are hopped 37 | List fieldsToIterate = this.fields.clone(); 38 | SObjectField lastField = (SObjectField) CollectionUtils.pop(fieldsToIterate); 39 | List fieldChain = new List(); 40 | for(SObjectField parentField : fieldsToIterate) { 41 | fieldChain.add(parentField.getDescribe().getRelationshipName()); 42 | } 43 | // Return the fully qualified field name 44 | return String.join(fieldChain, '.') + '.' + lastField.getDescribe().getName(); 45 | } 46 | 47 | 48 | public Integer compareTo(Object compareTo) { 49 | QueryField compareToQueryField = (QueryField)compareTo; 50 | if(this.getValue() == compareToQueryField.getValue()) return 0; 51 | if(this.getValue() > compareToQueryField.getValue()) return 1; 52 | return -1; 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/SObjectRepositoryMocks_Tests.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | @isTest 6 | private class SObjectRepositoryMocks_Tests { 7 | 8 | @isTest 9 | static void it_should_fake_returning_by_id() { 10 | Id testId = getTestId(); 11 | 12 | SObject returnedObj = new SObjectRepositoryMocks.Base(null).getById(testId); 13 | 14 | System.assert(returnedObj.Id == testId); 15 | } 16 | 17 | @isTest 18 | static void it_should_fake_returning_by_field_and_value() { 19 | Id testId = getTestId(); 20 | QueryField field = getField(); 21 | 22 | List queryFilters = new List{new QueryFilter().filterByField(field, QueryOperator.EQUALS, getFieldValue())}; 23 | SObject returnedObj = new SObjectRepositoryMocks.Base(null).getByIdAndQueryFilters(new Set{testId}, queryFilters)[0]; 24 | 25 | //System.assertEquals(getFieldValue(), returnedObj.get(String.valueOf(field.getValue()))); 26 | } 27 | 28 | @isTest 29 | static void it_should_return_list_of_sobjects_when_mocking_sosl_search() { 30 | System.assert(new SObjectRepositoryMocks.Base().getSearchResults(getFieldValue(), QuerySearchGroup.ALL_FIELDS) instanceof List); 31 | } 32 | 33 | @isTest 34 | static void it_should_return_list_of_sobjects_when_mocking_sosl_search_with_passed_objects() { 35 | Contact con = new Contact(); 36 | ISObjectRepository base = new SObjectRepositoryMocks.Base().with(new List{con}); 37 | 38 | System.assert(base.getSearchResults(getFieldValue(),QuerySearchGroup.ALL_FIELDS) instanceof List); 39 | } 40 | 41 | static QueryField getField() { 42 | return new QueryField(Schema.Lead.LeadSource); 43 | } 44 | 45 | static String getFieldValue() { 46 | return 'Web'; 47 | } 48 | 49 | static Id getTestId() { 50 | return TestingUtils.generateId(Schema.Lead.SObjectType); 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /nebula-app-framework/tests/utilities/classes/UUID_Tests.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | @isTest 6 | private class UUID_Tests { 7 | 8 | @isTest 9 | static void it_should_consider_all_zeroes_string_to_be_empty() { 10 | System.assert(UUID.isEmpty('00000000-0000-0000-0000-000000000000')); 11 | } 12 | 13 | @isTest 14 | static void it_should_create_a_new_UUID() { 15 | String generatedUUID = new UUID().getValue(); 16 | System.assertEquals(36, generatedUUID.length()); 17 | } 18 | 19 | @isTest 20 | static void it_should_reuse_a_UUID_on_subsequent_calls_to_getValue() { 21 | UUID theUUID = new UUID(); 22 | String originalValue = theUUID.getValue(); 23 | 24 | for(Integer i = 0; i < 5; i++) { 25 | System.assertEquals(originalValue, theUUID.getValue()); 26 | } 27 | } 28 | 29 | @isTest 30 | static void it_should_verify_that_a_UUID_is_a_UUID() { 31 | String generatedUUID = new UUID().getValue(); 32 | System.assert(UUID.isValid(generatedUUID)); 33 | } 34 | 35 | @isTest 36 | static void it_should_consider_null_string_an_empty_UUID() { 37 | System.assert(UUID.isEmpty(null)); 38 | } 39 | 40 | @isTest 41 | static void it_should_not_consider_an_empty_string_a_UUID() { 42 | System.assertEquals(false, UUID.isValid('')); 43 | } 44 | 45 | @isTest 46 | static void it_should_not_consider_null_a_UUID() { 47 | System.assertEquals(false, UUID.isValid(null)); 48 | } 49 | 50 | @isTest 51 | static void it_should_validate_a_UUID_in_upper_case() { 52 | String exampleUUID = 'f3665813-1a60-4924-ad9b-23a9cef17d80'.toUpperCase(); 53 | System.assertEquals(true, UUID.isValid(exampleUUID)); 54 | } 55 | 56 | @isTest 57 | static void it_should_validate_a_UUID_in_lower_case() { 58 | String exampleUUID = 'f3665813-1a60-4924-ad9b-23a9cef17d80'.toLowerCase(); 59 | System.assertEquals(true, UUID.isValid(exampleUUID)); 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/ISObjectQueryBuilder.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public interface ISObjectQueryBuilder { 14 | 15 | ISObjectQueryBuilder cacheResults(); 16 | 17 | // Field methods 18 | ISObjectQueryBuilder addAllFields(); 19 | ISObjectQueryBuilder addAllStandardFields(); 20 | ISObjectQueryBuilder addAllCustomFields(); 21 | ISObjectQueryBuilder addAllReadableFields(); 22 | ISObjectQueryBuilder addAllEditableFields(); 23 | ISObjectQueryBuilder addFields(List queryFields); 24 | ISObjectQueryBuilder addFields(Schema.FieldSet fieldSet); 25 | 26 | // Parent-to-child relationship query methods 27 | ISObjectQueryBuilder includeChildrenRecords(Schema.SObjectField childToParentRelationshipField, ISObjectQueryBuilder sobjectQueryBuilder); 28 | 29 | // Filter methods 30 | ISObjectQueryBuilder filterBy(IQueryFilter queryFilter); 31 | ISObjectQueryBuilder filterBy(List queryFilters); 32 | 33 | // Order By methods 34 | ISObjectQueryBuilder orderBy(IQueryField orderByQueryField); 35 | ISObjectQueryBuilder orderBy(IQueryField orderByQueryField, QuerySortOrder sortOrder); 36 | ISObjectQueryBuilder orderBy(IQueryField orderByQueryField, QuerySortOrder sortOrder, QueryNullSortOrder nullsSortOrder); 37 | 38 | // Additional query option methods 39 | ISObjectQueryBuilder limitCount(Integer limitCount); 40 | ISObjectQueryBuilder offset(Integer numberOfRowsToSkip); 41 | ISObjectQueryBuilder forReference(); 42 | ISObjectQueryBuilder forUpdate(); 43 | ISObjectQueryBuilder forView(); 44 | ISObjectQueryBuilder usingScope(QueryFilterScope filterScope); 45 | 46 | // Query string methods 47 | Database.QueryLocator getQueryLocator(); 48 | String getQuery(); 49 | String getSearchQuery(); 50 | String getChildQuery(Schema.SObjectField childToParentRelationshipField); 51 | 52 | // Query execution methods 53 | SObject getFirstQueryResult(); 54 | List getQueryResults(); 55 | 56 | } -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/QueryOperator_Tests.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | @isTest 6 | private class QueryOperator_Tests { 7 | 8 | @isTest 9 | static void it_should_return_EQUALS_string() { 10 | System.assertEquals('=', QueryOperator.EQUALS.getValue()); 11 | } 12 | 13 | @isTest 14 | static void it_should_return_NOT_EQUAL_TO_string() { 15 | System.assertEquals('!=', QueryOperator.NOT_EQUAL_TO.getValue()); 16 | } 17 | 18 | @isTest 19 | static void it_should_return_GREATER_THAN_string() { 20 | System.assertEquals('>', QueryOperator.GREATER_THAN.getValue()); 21 | } 22 | 23 | @isTest 24 | static void it_should_return_GREATER_THAN_OR_EQUAL_TO_string() { 25 | System.assertEquals('>=', QueryOperator.GREATER_THAN_OR_EQUAL_TO.getValue()); 26 | } 27 | 28 | @isTest 29 | static void it_should_return_LESS_THAN_string() { 30 | System.assertEquals('<', QueryOperator.LESS_THAN.getValue()); 31 | } 32 | 33 | @isTest 34 | static void it_should_return_LESS_THAN_OR_EQUAL_TO_string() { 35 | System.assertEquals('<=', QueryOperator.LESS_THAN_OR_EQUAL_TO.getValue()); 36 | } 37 | 38 | @isTest 39 | static void it_should_return_IS_IN_string() { 40 | System.assertEquals('IN', QueryOperator.IS_IN.getValue()); 41 | } 42 | 43 | @isTest 44 | static void it_should_return_IS_NOT_IN_string() { 45 | System.assertEquals('NOT IN', QueryOperator.IS_NOT_IN.getValue()); 46 | } 47 | 48 | @isTest 49 | static void it_should_return_INCLUDES_string() { 50 | System.assertEquals('INCLUDES', QueryOperator.INCLUDES.getValue()); 51 | } 52 | 53 | @isTest 54 | static void it_should_return_EXCLUDES_string() { 55 | System.assertEquals('EXCLUDES', QueryOperator.EXCLUDES.getValue()); 56 | } 57 | 58 | @isTest 59 | static void it_should_return_IS_LIKE_string() { 60 | System.assertEquals('LIKE', QueryOperator.IS_LIKE.getValue()); 61 | } 62 | 63 | @isTest 64 | static void it_should_return_IS_NOT_LIKE_string() { 65 | System.assertEquals('NOT LIKE', QueryOperator.IS_NOT_LIKE.getValue()); 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /tools/codeclimate/apex/apexunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | These rules deal with different problems that can occur with Apex unit tests. 9 | 10 | 11 | 16 | 17 | Apex unit tests should include at least one assertion. This makes the tests more robust, and using assert 18 | with messages provide the developer a clearer idea of what the test does. 19 | 20 | 3 21 | 22 | 33 | 34 | 35 | 40 | 41 | Apex unit tests should not use @isTest(seeAllData=true) because it opens up the existing database data for unexpected modification by tests. 42 | 43 | 3 44 | 45 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/testing-utilities/classes/TestingUtils.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Utils 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | @isTest 14 | public class TestingUtils { 15 | 16 | public static List insertedRecords = new List(); 17 | public static List upsertedRecords = new List(); 18 | public static List updatedRecords = new List(); 19 | public static List deletedRecords = new List(); 20 | public static List undeletedRecords = new List(); 21 | 22 | private static Integer startingNumber = 1; 23 | 24 | public static String generateId(Schema.SObjectType sobjectType) { 25 | String result = String.valueOf(startingNumber++); 26 | return sobjectType.getDescribe().getKeyPrefix() + '0'.repeat(12-result.length()) + result; 27 | } 28 | 29 | public static void generateIds(List records) { 30 | for(SObject record : records) record.Id = record.Id != null ? record.Id : generateId(record.getSObjectType()); 31 | } 32 | 33 | public static SObject setReadOnlyField(SObject sobj, Schema.SObjectField fieldName, Object value) { 34 | return setReadOnlyField(sobj, new Map{fieldName => value}); 35 | } 36 | 37 | public static SObject setReadOnlyField(SObject sobj, Map changesToFields) { 38 | String serializedRecord = JSON.serialize(sobj); 39 | Map deserializedRecordMap = (Map)JSON.deserializeUntyped(serializedRecord); 40 | 41 | // Loop through the deserialized record map and put the field & value 42 | // Since it's a map, if the field already exists on the SObject, it's updated (or added if it wasn't there already) 43 | for(Schema.SObjectField sobjectField : changesToFields.keySet()) { 44 | String fieldName = sobjectField.getDescribe().getName(); 45 | deserializedRecordMap.put(fieldName, changesToFields.get(sobjectField)); 46 | } 47 | 48 | serializedRecord = JSON.serialize(deserializedRecordMap); 49 | return (SObject)JSON.deserialize(serializedRecord, SObject.class); 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/SearchQueryBuilder_Tests.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | @isTest 6 | private class SearchQueryBuilder_Tests { 7 | 8 | @isTest 9 | static void it_should_be_usable_after_construction() { 10 | // Query builders should be usable as soon as it's constructed - it should be able to execute a query with some default values 11 | String searchTerm = 'test'; 12 | ISObjectQueryBuilder opportunityQueryBuilder = new SObjectQueryBuilder(Schema.Opportunity.SObjectType); 13 | 14 | ISearchQueryBuilder searchQueryBuilder = new SearchQueryBuilder(searchTerm, new List{opportunityQueryBuilder}); 15 | 16 | Test.startTest(); 17 | 18 | List> results = (List>)searchQueryBuilder.getSearchResults(); 19 | 20 | Test.stopTest(); 21 | } 22 | 23 | @isTest 24 | static void it_should_cache_results() { 25 | String searchTerm = 'test'; 26 | ISObjectQueryBuilder opportunityQueryBuilder = new SObjectQueryBuilder(Schema.Opportunity.SObjectType); 27 | 28 | ISearchQueryBuilder searchQueryBuilder = new SearchQueryBuilder(searchTerm, new List{opportunityQueryBuilder}); 29 | searchQueryBuilder.cacheResults(); 30 | 31 | Test.startTest(); 32 | 33 | System.assertEquals(0, Limits.getSoslQueries()); 34 | for(Integer i = 0; i < 10; i++) { 35 | System.debug(searchQueryBuilder.getSearchResults()); 36 | } 37 | 38 | System.assertEquals(1, Limits.getSoslQueries()); 39 | 40 | Test.stopTest(); 41 | } 42 | 43 | @isTest 44 | static void it_should_set_search_group() { 45 | // TODO finish writing tests! 46 | } 47 | 48 | @isTest 49 | static void it_should_add_sobject_query_builder() { 50 | // TODO finish writing tests! 51 | } 52 | 53 | @isTest 54 | static void it_should_get_query() { 55 | // TODO finish writing tests! 56 | } 57 | 58 | @isTest 59 | static void it_should_get_first_query_results() { 60 | // TODO finish writing tests! 61 | } 62 | 63 | @isTest 64 | static void it_should_get_query_results() { 65 | // TODO finish writing tests! 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/SObjectFieldDescriber_Tests.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | @isTest 6 | private class SObjectFieldDescriber_Tests { 7 | 8 | @isTest 9 | static void it_should_return_the_sobject_type() { 10 | Schema.SObjectType expectedSObjectType = Schema.Lead.SObjectType; 11 | Schema.SObjectField sobjectField = Schema.Lead.Id; 12 | 13 | Test.startTest(); 14 | 15 | SObjectFieldDescriber sobjectFieldDescriber = new SObjectFieldDescriber(sobjectField); 16 | System.assertEquals(expectedSObjectType, sobjectFieldDescriber.getSObjectType()); 17 | 18 | Test.stopTest(); 19 | } 20 | 21 | @isTest 22 | static void it_should_validate_the_expected_sobject_type() { 23 | Schema.SObjectType expectedSObjectType = Schema.Lead.SObjectType; 24 | Schema.SObjectField sobjectField = Schema.Lead.Id; 25 | 26 | Test.startTest(); 27 | 28 | SObjectFieldDescriber sobjectFieldDescriber = new SObjectFieldDescriber(sobjectField); 29 | System.assert(sobjectFieldDescriber.validateSObjectType(expectedSObjectType)); 30 | 31 | Test.stopTest(); 32 | } 33 | 34 | @isTest 35 | static void it_should_return_the_full_field_name() { 36 | Schema.SObjectType expectedSObjectType = Schema.Lead.SObjectType; 37 | Schema.SObjectField sobjectField = Schema.Lead.Id; 38 | String expectedFullFieldName = String.valueOf(expectedSObjectType) + '.' + String.valueOf(sobjectField); 39 | 40 | Test.startTest(); 41 | 42 | SObjectFieldDescriber sobjectFieldDescriber = new SObjectFieldDescriber(sobjectField); 43 | System.assertEquals(expectedFullFieldName, sobjectFieldDescriber.getFullFieldName()); 44 | 45 | Test.stopTest(); 46 | } 47 | 48 | @isTest 49 | static void it_should_return_the_parent_field_sobject_type() { 50 | Schema.SObjectField sobjectField = Schema.Lead.ConvertedAccountId; 51 | Schema.SObjectType expectedParentSObjectType = Schema.Account.SObjectType; 52 | 53 | Test.startTest(); 54 | 55 | SObjectFieldDescriber sobjectFieldDescriber = new SObjectFieldDescriber(sobjectField); 56 | System.assertEquals(expectedParentSObjectType, sobjectFieldDescriber.getParentSObjectType()); 57 | 58 | Test.stopTest(); 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/SObjectRecordTypes.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Record Types 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public abstract class SObjectRecordTypes extends NebulaCore implements ISObjectRecordTypes { 14 | 15 | private String sobjectName; 16 | 17 | public SObjectRecordTypes() { 18 | this.currentModule = NebulaCore.Module.RECORD_TYPES; 19 | 20 | this.sobjectName = this.getSObjectType().getDescribe().getName(); 21 | } 22 | 23 | public abstract Schema.SObjectType getSObjectType(); 24 | 25 | public Map getAllById() { 26 | return new Map(getRecordTypes()); 27 | } 28 | 29 | public Map getAllByDeveloperName() { 30 | Map allRecordTypesByDeveloperName = new Map(); 31 | for(RecordType recordType : this.getAllById().values()) { 32 | if(recordType.SObjectType != this.sobjectName) continue; 33 | 34 | allRecordTypesByDeveloperName.put(recordType.DeveloperName, recordType); 35 | } 36 | 37 | return allRecordTypesByDeveloperName; 38 | } 39 | 40 | private List getRecordTypes() { 41 | Schema.SObjectType recordTypeSObjectType = Schema.SObjectType.RecordType.getSObjectType(); 42 | ISObjectQueryBuilder query = new SObjectQueryBuilder(recordTypeSObjectType).orderBy(new QueryField(Schema.RecordType.DeveloperName)); 43 | 44 | // If we don't have the SObject cached, then we need to query 45 | if(NebulaSettings.RecordTypesSettings.LazyLoad__c) { 46 | query.filterBy(new QueryFilter().filterByField(new QueryField(Schema.RecordType.SObjectType), QueryOperator.EQUALS, this.sobjectName)); 47 | Logger.addEntry(this, 'NebulaSettings.RecordTypesSettings.LazyLoad__c=' + NebulaSettings.RecordTypesSettings.LazyLoad__c); 48 | Logger.addEntry(this, 'this.sobjectName=' + this.sobjectName); 49 | } 50 | 51 | if(!NebulaSettings.RecordTypesSettings.IncludeManagedRecordTypes__c) { 52 | query.filterBy(new QueryFilter().filterByField(new QueryField(Schema.RecordType.NamespacePrefix), QueryOperator.EQUALS, null)); 53 | } 54 | 55 | Logger.addEntry(this, 'Loading SObjectRecordTypes for=' + this.getSObjectType()); 56 | 57 | return (List)query.cacheResults().getQueryResults(); 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/AggregateResultQueryBuilder_Tests.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | @isTest 6 | private class AggregateResultQueryBuilder_Tests { 7 | 8 | 9 | @isTest 10 | static void it_should_be_usable_after_construction() { 11 | // Query builders should be usable as soon as it's constructed - it should be able to execute a query with some default values 12 | IAggregateResultQueryBuilder aggregateQueryBuilder = new AggregateResultQueryBuilder(Schema.Opportunity.SObjectType); 13 | 14 | Test.startTest(); 15 | 16 | List results = (List)aggregateQueryBuilder.getQueryResults(); 17 | 18 | Test.stopTest(); 19 | } 20 | 21 | @isTest 22 | static void it_should_cache_results() { 23 | IAggregateResultQueryBuilder aggregateResultQueryBuilder = new AggregateResultQueryBuilder(Schema.Opportunity.SObjectType); 24 | aggregateResultQueryBuilder.cacheResults(); 25 | 26 | Test.startTest(); 27 | 28 | System.assertEquals(0, Limits.getQueries()); 29 | for(Integer i = 0; i < 10; i++) { 30 | System.debug(aggregateResultQueryBuilder.getQueryResults()); 31 | } 32 | 33 | System.assertEquals(1, Limits.getQueries()); 34 | 35 | Test.stopTest(); 36 | } 37 | 38 | @isTest 39 | static void it_should_build_a_ridiculous_query_string() { 40 | String expectedString = 'SELECT Type,\nAVG(Amount) AVG__Amount, COUNT(AccountId) COUNT__AccountId, ' 41 | + 'COUNT_DISTINCT(OwnerId) COUNT_DISTINCT__OwnerId, MAX(CreatedDate) MAX__CreatedDate, MIN(CreatedDate) MIN__CreatedDate' 42 | + '\nFROM Opportunity' 43 | + '\nGROUP BY Type'; 44 | 45 | IAggregateResultQueryBuilder aggregateResultQueryBuilder = new AggregateResultQueryBuilder(Schema.Opportunity.SObjectType) 46 | .max(new QueryField(Schema.Opportunity.CreatedDate)) 47 | .avg(new QueryField(Schema.Opportunity.Amount)) 48 | .countDistinct(new QueryField(Schema.Opportunity.OwnerId)) 49 | .min(new QueryField(Schema.Opportunity.CreatedDate)) 50 | .groupBy(new QueryField(Schema.Opportunity.Type)) 51 | .count(new QueryField(Schema.Opportunity.AccountId)); 52 | String returnedQueryString = aggregateResultQueryBuilder.getQuery(); 53 | 54 | System.assertEquals(expectedString, returnedQueryString); 55 | 56 | // Verify that the query can be executed 57 | Database.query(returnedQueryString); 58 | } 59 | 60 | 61 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/logger/objects/NebulaLog__c/NebulaLog__c.object-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Accept 5 | Default 6 | 7 | 8 | CancelEdit 9 | Default 10 | 11 | 12 | Clone 13 | Default 14 | 15 | 16 | Delete 17 | Default 18 | 19 | 20 | Edit 21 | Default 22 | 23 | 24 | List 25 | Default 26 | 27 | 28 | New 29 | Default 30 | 31 | 32 | SaveEdit 33 | Default 34 | 35 | 36 | Tab 37 | Default 38 | 39 | 40 | View 41 | Default 42 | 43 | true 44 | SYSTEM 45 | Deployed 46 | Stores log records, as well as attachments, that are generated by the Nebula framework. 47 | false 48 | true 49 | false 50 | true 51 | true 52 | true 53 | true 54 | true 55 | 56 | 57 | Log-{00000} 58 | 59 | false 60 | AutoNumber 61 | 62 | Nebula Logs 63 | 64 | InitialClass__c 65 | TransactionId__c 66 | CREATEDBY_USER 67 | CREATED_DATE 68 | 69 | ReadWrite 70 | Public 71 | 72 | -------------------------------------------------------------------------------- /nebula-app-framework/tests/dml-management/classes/DMLMock_Tests.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | @isTest 6 | private class DMLMock_Tests { 7 | 8 | static IDML dmlRepo = new DMLMock.Base(); 9 | static Schema.Contact con = createContact(); 10 | 11 | @isTest 12 | static void it_should_fake_dml_insert() { 13 | Test.startTest(); 14 | dmlRepo.insertRecords(con); 15 | Test.stopTest(); 16 | 17 | System.assert(TestingUtils.insertedRecords.size() > 0); 18 | } 19 | 20 | @isTest 21 | static void it_should_fake_dml_update() { 22 | Test.startTest(); 23 | dmlRepo.updateRecords(con); 24 | Test.stopTest(); 25 | 26 | System.assert(!TestingUtils.updatedRecords.isEmpty()); 27 | } 28 | 29 | @isTest 30 | static void it_should_fake_dml_upsert() { 31 | Test.startTest(); 32 | dmlRepo.upsertRecords(con); 33 | Test.stopTest(); 34 | 35 | System.assert(!TestingUtils.upsertedRecords.isEmpty()); 36 | } 37 | 38 | @isTest 39 | static void it_should_fake_dml_delete() { 40 | Test.startTest(); 41 | dmlRepo.deleteRecords(con); 42 | Test.stopTest(); 43 | 44 | System.assert(!TestingUtils.deletedRecords.isEmpty()); 45 | } 46 | 47 | @isTest 48 | static void it_should_fake_dml_hard_delete() { 49 | Test.startTest(); 50 | dmlRepo.hardDeleteRecords(con); 51 | Test.stopTest(); 52 | 53 | System.assert(!TestingUtils.deletedRecords.isEmpty()); 54 | } 55 | 56 | @isTest 57 | static void it_should_fake_dml_undelete() { 58 | Test.startTest(); 59 | dmlRepo.undeleteRecords(con); 60 | Test.stopTest(); 61 | 62 | System.assert(!TestingUtils.undeletedRecords.isEmpty()); 63 | } 64 | 65 | @isTest 66 | static void it_should_mock_updating_read_only_fields_when_updating_data() { 67 | Schema.Lead l = new Schema.Lead(); 68 | l = (Lead)TestingUtils.setReadOnlyField(l, Schema.Lead.IsConverted, true); 69 | 70 | Test.startTest(); 71 | dmlRepo.updateRecords(l); 72 | Test.stoptest(); 73 | 74 | SObject record = TestingUtils.updatedRecords[0]; 75 | System.assert(record instanceof Schema.Lead); 76 | System.assert(record.get('IsConverted') != null); 77 | } 78 | 79 | private static Contact createContact() { 80 | con = new Contact(); 81 | con.Email = 'rightHandMan@hamilton.com'; 82 | con.FirstName = 'George'; 83 | con.LastName = 'Washington'; 84 | con.LeadSource = 'Web'; 85 | 86 | return con; 87 | } 88 | 89 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/logger/layouts/NebulaLog__c-Log Layout.layout-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Submit 4 | 5 | false 6 | false 7 | true 8 | 9 | 10 | 11 | Readonly 12 | Name 13 | 14 | 15 | Required 16 | TransactionId__c 17 | 18 | 19 | 20 | 21 | Edit 22 | InitialClass__c 23 | 24 | 25 | 26 | 27 | 28 | true 29 | true 30 | true 31 | 32 | 33 | 34 | 35 | 36 | false 37 | true 38 | true 39 | 40 | 41 | 42 | Readonly 43 | CreatedById 44 | 45 | 46 | 47 | 48 | Readonly 49 | LastModifiedById 50 | 51 | 52 | 53 | 54 | 55 | true 56 | false 57 | true 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | RelatedNoteList 66 | 67 | 68 | RelatedEntityHistoryList 69 | 70 | false 71 | false 72 | false 73 | false 74 | false 75 | 76 | 00h0Y000001YebH 77 | 4 78 | 0 79 | Default 80 | 81 | 82 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/IAggregateResultQueryBuilder.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public interface IAggregateResultQueryBuilder { 14 | 15 | IAggregateResultQueryBuilder cacheResults(); 16 | 17 | // Group By methods 18 | IAggregateResultQueryBuilder groupBy(IQueryField groupByQueryField); 19 | IAggregateResultQueryBuilder groupBy(List groupByQueryFields); 20 | IAggregateResultQueryBuilder groupBy(Schema.FieldSet fieldSet); 21 | IAggregateResultQueryBuilder groupBy(QueryDate queryDate); 22 | // TODO add support for other features, like 'having count(id) > 1', etc support 23 | // https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_groupby.htm 24 | // TODO need to research 'GROUP BY CUBE' & 'GROUPING' more https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_groupby_cube.htm 25 | 26 | // Aggregate functions 27 | IAggregateResultQueryBuilder avg(IQueryField queryField); 28 | IAggregateResultQueryBuilder avg(IQueryField queryField, String fieldAlias); 29 | IAggregateResultQueryBuilder count(IQueryField queryField); 30 | IAggregateResultQueryBuilder count(IQueryField queryField, String fieldAlias); 31 | IAggregateResultQueryBuilder countDistinct(IQueryField queryField); 32 | IAggregateResultQueryBuilder countDistinct(IQueryField queryField, String fieldAlias); 33 | IAggregateResultQueryBuilder max(IQueryField queryField); 34 | IAggregateResultQueryBuilder max(IQueryField queryField, String fieldAlias); 35 | IAggregateResultQueryBuilder min(IQueryField queryField); 36 | IAggregateResultQueryBuilder min(IQueryField queryField, String fieldAlias); 37 | IAggregateResultQueryBuilder sum(IQueryField queryField); 38 | IAggregateResultQueryBuilder sum(IQueryField queryField, String fieldAlias); 39 | 40 | // Filter methods 41 | IAggregateResultQueryBuilder filterBy(IQueryFilter queryFilter); 42 | IAggregateResultQueryBuilder filterBy(List queryFilters); 43 | 44 | // Order By methods 45 | IAggregateResultQueryBuilder orderBy(IQueryField orderByQueryField); 46 | IAggregateResultQueryBuilder orderBy(IQueryField orderByQueryField, QuerySortOrder sortOrder); 47 | IAggregateResultQueryBuilder orderBy(IQueryField orderByQueryField, QuerySortOrder sortOrder, QueryNullSortOrder nullsSortOrder); 48 | 49 | // Additional query option methods 50 | IAggregateResultQueryBuilder limitCount(Integer limitCount); 51 | 52 | // Query string methods 53 | String getQuery(); 54 | 55 | // Query execution methods 56 | AggregateResult getFirstQueryResult(); 57 | List getQueryResults(); 58 | 59 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/SObjectFieldDescriber.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Metadata 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public without sharing class SObjectFieldDescriber { 14 | // We would love for Schema.SObjectField to have a way to return the SObject Type 15 | // Sadly, it doesn't, so we have this class to fill the void. 16 | // If a proper method is ever added to Apex to get the field's SObject Type, 17 | // then we should consider removing this class. 18 | 19 | private static Map sobjectFieldHashCodeToSObjectTypeMap; 20 | 21 | private Schema.SObjectField sobjectField; 22 | private Schema.SObjectType sobjectType; 23 | 24 | public SObjectFieldDescriber(Schema.SObjectField sobjectField) { 25 | this.cacheSObjectTypeFieldHashCodes(); 26 | 27 | this.sobjectField = sobjectField; 28 | this.setSObjectType(); 29 | } 30 | 31 | public String getFieldName() { 32 | return this.sobjectField.getDescribe().getName(); 33 | } 34 | 35 | public String getFullFieldName() { 36 | return this.sobjectType + '.' + this.getFieldName(); 37 | } 38 | 39 | public Schema.SObjectType getSObjectType() { 40 | return this.sobjectType; 41 | } 42 | 43 | public Schema.SObjectType getParentSObjectType() { 44 | Schema.DescribeFieldResult fieldDescribe = this.SObjectField.getDescribe(); 45 | 46 | Schema.SObjectType parentSObjectType; 47 | if(!fieldDescribe.isNamePointing() && !fieldDescribe.getReferenceTo().isEmpty()) parentSObjectType = fieldDescribe.getReferenceTo()[0]; 48 | 49 | return parentSObjectType; 50 | } 51 | 52 | public Boolean validateSObjectType(Schema.SObjectType expectedSObjectType) { 53 | return this.SObjectType == expectedSObjectType; 54 | } 55 | 56 | private void setSObjectType() { 57 | Integer sobjectFieldHashCode = this.getHashCode(this.sobjectField); 58 | this.SObjectType = sobjectFieldHashCodeToSObjectTypeMap.get(sobjectFieldHashCode); 59 | } 60 | 61 | private void cacheSObjectTypeFieldHashCodes() { 62 | // Describe calls are "free" but still add CPU time, so let's cache them 63 | if(sobjectFieldHashCodeToSObjectTypeMap != null) return; 64 | 65 | sobjectFieldHashCodeToSObjectTypeMap = new Map(); 66 | 67 | // Build a map of hash codes for each fieldDescribe taken from Schema Global Describe 68 | Map globalDescribe = Schema.getGlobalDescribe(); 69 | for(String sobjTypeName : globalDescribe.keySet()) { 70 | SObjectType sobjType = globalDescribe.get(sobjTypeName); 71 | 72 | for(Schema.SObjectField sobjField : sobjType.getDescribe().fields.getMap().values()) { 73 | Integer sobjFieldHashCode = getHashCode(sobjField); 74 | 75 | sobjectFieldHashCodeToSObjectTypeMap.put(sobjFieldHashCode, sobjType); 76 | } 77 | } 78 | } 79 | 80 | private Integer getHashCode(Schema.SObjectField sobjField) { 81 | return ((Object)sobjField).hashCode(); 82 | } 83 | 84 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/NebulaSettings.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Configuration 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public without sharing class NebulaSettings { 14 | 15 | /** 16 | * @description TODO 17 | * returns true if it's a sandbox 18 | */ 19 | public static NebulaLoggerSettings__c LoggerSettings {get; private set;} 20 | public static NebulaRecordTypesSettings__c RecordTypesSettings {get; private set;} 21 | public static NebulaSObjectQueryBuilderSettings__c SObjectQueryBuilderSettings {get; private set;} 22 | public static NebulaTriggerHandlerSettings__c TriggerHandlerSettings {get; private set;} 23 | 24 | static { 25 | loadCustomSettings(); 26 | } 27 | 28 | public static void resetAllSettingsToDefaults() { 29 | deleteExistingCustomSettings(); 30 | createCustomSettings(); 31 | } 32 | 33 | private static void loadCustomSettings() { 34 | loadLoggerSettings(); 35 | loadRecordTypesSettings(); 36 | loadSObjectQueryBuilderSettings(); 37 | loadTriggerHandlerSettings(); 38 | } 39 | 40 | private static void deleteExistingCustomSettings() { 41 | delete [SELECT Id FROM NebulaLoggerSettings__c]; 42 | delete [SELECT Id FROM NebulaRecordTypesSettings__c]; 43 | delete [SELECT Id FROM NebulaSObjectQueryBuilderSettings__c]; 44 | delete [SELECT Id FROM NebulaTriggerHandlerSettings__c]; 45 | } 46 | 47 | private static void createCustomSettings() { 48 | upsert NebulaLoggerSettings__c.getOrgDefaults(); 49 | upsert NebulaRecordTypesSettings__c.getOrgDefaults(); 50 | upsert NebulaSObjectQueryBuilderSettings__c.getOrgDefaults(); 51 | upsert NebulaTriggerHandlerSettings__c.getOrgDefaults(); 52 | } 53 | 54 | private static void loadLoggerSettings() { 55 | loggerSettings = NebulaLoggerSettings__c.getInstance(); 56 | 57 | if(loggerSettings.Id == null) { 58 | upsert NebulaLoggerSettings__c.getOrgDefaults(); 59 | loggerSettings = NebulaLoggerSettings__c.getInstance(); 60 | } 61 | } 62 | 63 | private static void loadRecordTypesSettings() { 64 | recordTypesSettings = NebulaRecordTypesSettings__c.getInstance(); 65 | 66 | if(recordTypesSettings.Id == null) { 67 | upsert NebulaRecordTypesSettings__c.getOrgDefaults(); 68 | recordTypesSettings = NebulaRecordTypesSettings__c.getInstance(); 69 | } 70 | } 71 | 72 | private static void loadSObjectQueryBuilderSettings() { 73 | sobjectQueryBuilderSettings = NebulaSObjectQueryBuilderSettings__c.getInstance(); 74 | 75 | if(sobjectQueryBuilderSettings.Id == null) { 76 | upsert NebulaSObjectQueryBuilderSettings__c.getOrgDefaults(); 77 | sobjectQueryBuilderSettings = NebulaSObjectQueryBuilderSettings__c.getInstance(); 78 | } 79 | } 80 | 81 | private static void loadTriggerHandlerSettings() { 82 | triggerHandlerSettings = NebulaTriggerHandlerSettings__c.getInstance(); 83 | 84 | if(triggerHandlerSettings.Id == null) { 85 | upsert NebulaTriggerHandlerSettings__c.getOrgDefaults(); 86 | triggerHandlerSettings = NebulaTriggerHandlerSettings__c.getInstance(); 87 | } 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/sobject-repositories/classes/SObjectRepository.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Repository 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public abstract class SObjectRepository extends DML implements ISObjectRepository, IDML { 14 | 15 | private List sobjectFieldList; 16 | private Map sobjectFieldMap; 17 | private QueryField idQueryField; 18 | 19 | public SObjectRepository() { 20 | this.currentModule = NebulaCore.Module.REPOSITORY; 21 | 22 | this.sobjectFieldMap = this.getSObjectType().getDescribe().fields.getMap(); 23 | this.idQueryField = new QueryField(this.getField('Id')); 24 | } 25 | 26 | public abstract Schema.SObjectType getSObjectType(); 27 | 28 | // SOQL 29 | public virtual SObject getById(Id recordId) { 30 | return new SObjectQueryBuilder(this.getSObjectType()) 31 | .filterBy(new QueryFilter().filterByField(this.idQueryField, QueryOperator.EQUALS, recordId)) 32 | .filterBy(new QueryFilter().filterByField(this.idQueryField, QueryOperator.EQUALS, recordId)) 33 | .getFirstQueryResult(); 34 | } 35 | 36 | public virtual List getById(List recordIds) { 37 | return new SObjectQueryBuilder(this.getSObjectType()) 38 | .filterBy(new QueryFilter().filterByField(this.idQueryField, QueryOperator.IS_IN, recordIds)) 39 | .getQueryResults(); 40 | } 41 | 42 | public virtual List get(IQueryFilter queryFilter) { 43 | return this.get(new List{queryFilter}); 44 | } 45 | 46 | public virtual List get(List queryFilters) { 47 | return new SObjectQueryBuilder(this.getSObjectType()) 48 | .filterBy(queryFilters) 49 | .getQueryResults(); 50 | } 51 | 52 | public virtual List getByIdAndQueryFilters(Set idSet, List queryFilters) { 53 | return this.getByIdAndQueryFilters(new List(idSet), queryFilters); 54 | } 55 | 56 | public virtual List getByIdAndQueryFilters(List idList, List queryFilters) { 57 | return new SObjectQueryBuilder(this.getSObjectType()) 58 | .filterBy(new QueryFilter().filterByField(this.idQueryField, QueryOperator.IS_IN, idList)) 59 | .filterBy(queryFilters) 60 | .getQueryResults(); 61 | } 62 | 63 | // SOSL 64 | public virtual List getSearchResults(String searchTerm) { 65 | return this.getSearchResults(searchTerm, QuerySearchGroup.ALL_FIELDS, null); 66 | } 67 | 68 | public virtual List getSearchResults(String searchTerm, QuerySearchGroup searchGroup) { 69 | return this.getSearchResults(searchTerm, searchGroup, null); 70 | } 71 | 72 | public virtual List getSearchResults(String searchTerm, QuerySearchGroup searchGroup, List queryFilters) { 73 | return new SearchQueryBuilder(searchTerm, new SObjectQueryBuilder(this.getSObjectType()) 74 | .filterBy(queryFilters)) 75 | .inQuerySearchGroup(searchGroup) 76 | .getFirstSearchResult(); 77 | } 78 | 79 | private Schema.SObjectField getField(String fieldName) { 80 | return this.sobjectFieldMap.get(fieldName); 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/SearchQueryBuilder.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description A builder class that generates dynamic SOSL queries & returns a list of SObjects or list of a list of SObjects 11 | * 12 | */ 13 | public class SearchQueryBuilder extends QueryBuilder implements ISearchQueryBuilder { 14 | 15 | private String searchTerm; 16 | private QuerySearchGroup searchGroup; 17 | private List sobjectQueryBuilders; 18 | private List sobjectQueries; 19 | private Boolean withHighlight; 20 | private Boolean withSpellCorrection; 21 | 22 | public SearchQueryBuilder(String searchTerm, ISObjectQueryBuilder sobjectQueryBuilder) { 23 | this(searchTerm, new List{sobjectQueryBuilder}); 24 | } 25 | 26 | public SearchQueryBuilder(String searchTerm, List sobjectQueryBuilders) { 27 | this.searchTerm = searchTerm; 28 | this.sobjectQueryBuilders = sobjectQueryBuilders; 29 | 30 | this.searchGroup = QuerySearchGroup.ALL_FIELDS; 31 | this.sobjectQueries = new List(); 32 | 33 | this.parseSObjectQueryBuilders(); 34 | } 35 | 36 | public ISearchQueryBuilder cacheResults() { 37 | super.doCacheResults(); 38 | return this; 39 | } 40 | 41 | public ISearchQueryBuilder inQuerySearchGroup(QuerySearchGroup searchGroup) { 42 | this.searchGroup = searchGroup; 43 | return this; 44 | } 45 | 46 | public ISearchQueryBuilder withHighlight(Boolean withHighlight) { 47 | this.withHighlight = withHighlight; 48 | return this; 49 | } 50 | 51 | public ISearchQueryBuilder withSpellCorrection(Boolean withSpellCorrection) { 52 | this.withHighlight = withSpellCorrection; 53 | return this; 54 | } 55 | 56 | public String getQuery() { 57 | String query = 'FIND ' + new QueryArgumentFormatter(this.searchTerm.toLowerCase()).getValue() 58 | + '\nIN ' + this.searchGroup.name().replace('_', ' ') 59 | + '\nRETURNING ' + this.getSObjectQueriesString() 60 | + this.getWithHighlightString() 61 | + this.getWithSpellCorrectionString(); 62 | 63 | return query; 64 | } 65 | 66 | public List getFirstSearchResult() { 67 | return this.getSearchResults()[0]; 68 | } 69 | 70 | public List> getSearchResults() { 71 | return super.doGetSearchResults(this.getQuery()); 72 | } 73 | 74 | private ISearchQueryBuilder parseSObjectQueryBuilders() { 75 | for(ISObjectQueryBuilder sobjectQueryBuilder : this.sobjectQueryBuilders) { 76 | this.sobjectQueries.add(sobjectQueryBuilder.getSearchQuery()); 77 | } 78 | return this; 79 | } 80 | 81 | private String getSObjectQueriesString() { 82 | this.sobjectQueries.sort(); 83 | return String.join(this.sobjectQueries, ', '); 84 | } 85 | 86 | private String getWithHighlightString() { 87 | return (this.withHighlight == null || !this.withHighlight) ? '' : '\nWITH HIGHLIGHT'; 88 | } 89 | 90 | private String getWithSpellCorrectionString() { 91 | return this.withSpellCorrection == null ? '' : '\nWITH SPELL_CORRECTION = ' + this.withSpellCorrection; 92 | } 93 | 94 | } -------------------------------------------------------------------------------- /nebula-app-framework/tests/utilities/classes/NebulaSettings_Tests.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | @isTest 6 | private class NebulaSettings_Tests { 7 | 8 | @isTest 9 | static void it_should_return_record_types_settings() { 10 | List existingSettings = [SELECT Id FROM NebulaRecordTypesSettings__c]; 11 | System.assert(existingSettings.isEmpty()); 12 | 13 | Test.startTest(); 14 | System.assertNotEquals(null, NebulaSettings.RecordTypesSettings); 15 | Test.stopTest(); 16 | } 17 | 18 | @isTest 19 | static void it_should_return_logger_settings() { 20 | List existingSettings = [SELECT Id FROM NebulaLoggerSettings__c]; 21 | System.assert(existingSettings.isEmpty()); 22 | 23 | Test.startTest(); 24 | System.assertNotEquals(null, NebulaSettings.LoggerSettings); 25 | Test.stopTest(); 26 | } 27 | 28 | @isTest 29 | static void it_should_return_SObjectQueryBuilder_settings() { 30 | List existingSettings = [SELECT Id FROM NebulaSObjectQueryBuilderSettings__c]; 31 | System.assert(existingSettings.isEmpty()); 32 | 33 | Test.startTest(); 34 | System.assertNotEquals(null, NebulaSettings.SObjectQueryBuilderSettings); 35 | Test.stopTest(); 36 | } 37 | 38 | @isTest 39 | static void it_should_return_trigger_handler_settings() { 40 | List existingSettings = [SELECT Id FROM NebulaTriggerHandlerSettings__c]; 41 | System.assert(existingSettings.isEmpty()); 42 | 43 | Test.startTest(); 44 | System.assertNotEquals(null, NebulaSettings.TriggerHandlerSettings); 45 | Test.stopTest(); 46 | } 47 | 48 | @isTest 49 | static void it_should_reset_all_settings_to_defaults() { 50 | NebulaLoggerSettings__c nebulaLoggerSettings = NebulaLoggerSettings__c.getInstance(); 51 | upsert nebulaLoggerSettings; 52 | Id originalLoggerSettingsId = NebulaLoggerSettings__c.getInstance().Id; 53 | 54 | NebulaRecordTypesSettings__c nebulaRecordTypesSettings = NebulaRecordTypesSettings__c.getInstance(); 55 | upsert nebulaRecordTypesSettings; 56 | Id originalRecordTypesSettingsId = NebulaRecordTypesSettings__c.getInstance().Id; 57 | 58 | NebulaSObjectQueryBuilderSettings__c nebulaSObjectQueryBuilderSettings = NebulaSObjectQueryBuilderSettings__c.getInstance(); 59 | upsert nebulaSObjectQueryBuilderSettings; 60 | Id originalSObjectQueryBuilderSettingsId = NebulaSObjectQueryBuilderSettings__c.getInstance().Id; 61 | 62 | NebulaTriggerHandlerSettings__c nebulaTriggerHandlerSettings = NebulaTriggerHandlerSettings__c.getInstance(); 63 | upsert nebulaTriggerHandlerSettings; 64 | Id originalTriggerHandlerSettingsId = NebulaTriggerHandlerSettings__c.getInstance().Id; 65 | 66 | Test.startTest(); 67 | NebulaSettings.resetAllSettingsToDefaults(); 68 | Test.stopTest(); 69 | 70 | System.assertNotEquals(originalLoggerSettingsId, NebulaLoggerSettings__c.getInstance().Id); 71 | System.assertNotEquals(originalRecordTypesSettingsId, NebulaRecordTypesSettings__c.getInstance().Id); 72 | System.assertNotEquals(originalSObjectQueryBuilderSettingsId, NebulaSObjectQueryBuilderSettings__c.getInstance().Id); 73 | System.assertNotEquals(originalTriggerHandlerSettingsId, NebulaTriggerHandlerSettings__c.getInstance().Id); 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/QueryDate_Tests.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | @isTest 6 | private class QueryDate_Tests { 7 | 8 | @isTest 9 | static void it_should_return_sobject_type() { 10 | QueryDate dt = QueryDate.CALENDAR_MONTH(Schema.User.CreatedDate); 11 | System.assertEquals(Schema.User.SObjectType, dt.getSObjectType()); 12 | } 13 | 14 | @isTest 15 | static void it_should_return_calendar_month_string() { 16 | QueryDate dt = QueryDate.CALENDAR_MONTH(Schema.User.CreatedDate); 17 | System.assertEquals('CALENDAR_MONTH(CreatedDate)', dt.getValue()); 18 | } 19 | 20 | @isTest 21 | static void it_should_return_calendar_quarter_string() { 22 | QueryDate dt = QueryDate.CALENDAR_QUARTER(Schema.User.CreatedDate); 23 | System.assertEquals('CALENDAR_QUARTER(CreatedDate)', dt.getValue()); 24 | } 25 | 26 | @isTest 27 | static void it_should_return_calendar_year_string() { 28 | QueryDate dt = QueryDate.CALENDAR_YEAR(Schema.User.CreatedDate); 29 | System.assertEquals('CALENDAR_YEAR(CreatedDate)', dt.getValue()); 30 | } 31 | 32 | @isTest 33 | static void it_should_return_day_in_month_string() { 34 | QueryDate dt = QueryDate.DAY_IN_MONTH(Schema.User.CreatedDate); 35 | System.assertEquals('DAY_IN_MONTH(CreatedDate)', dt.getValue()); 36 | } 37 | 38 | @isTest 39 | static void it_should_return_day_in_week_string() { 40 | QueryDate dt = QueryDate.DAY_IN_WEEK(Schema.User.CreatedDate); 41 | System.assertEquals('DAY_IN_WEEK(CreatedDate)', dt.getValue()); 42 | } 43 | 44 | @isTest 45 | static void it_should_return_day_in_year_string() { 46 | QueryDate dt = QueryDate.DAY_IN_YEAR(Schema.User.CreatedDate); 47 | System.assertEquals('DAY_IN_YEAR(CreatedDate)', dt.getValue()); 48 | } 49 | 50 | @isTest 51 | static void it_should_return_day_only_string() { 52 | QueryDate dt = QueryDate.DAY_ONLY(Schema.User.CreatedDate); 53 | System.assertEquals('DAY_ONLY(CreatedDate)', dt.getValue()); 54 | } 55 | 56 | @isTest 57 | static void it_should_return_fiscal_month_string() { 58 | QueryDate dt = QueryDate.FISCAL_MONTH(Schema.User.CreatedDate); 59 | System.assertEquals('FISCAL_MONTH(CreatedDate)', dt.getValue()); 60 | } 61 | 62 | @isTest 63 | static void it_should_return_fiscal_quarter_string() { 64 | QueryDate dt = QueryDate.FISCAL_QUARTER(Schema.User.CreatedDate); 65 | System.assertEquals('FISCAL_QUARTER(CreatedDate)', dt.getValue()); 66 | } 67 | 68 | @isTest 69 | static void it_should_return_fiscal_year_string() { 70 | QueryDate dt = QueryDate.FISCAL_YEAR(Schema.User.CreatedDate); 71 | System.assertEquals('FISCAL_YEAR(CreatedDate)', dt.getValue()); 72 | } 73 | 74 | @isTest 75 | static void it_should_return_hour_in_day_string() { 76 | QueryDate dt = QueryDate.HOUR_IN_DAY(Schema.User.CreatedDate); 77 | System.assertEquals('HOUR_IN_DAY(CreatedDate)', dt.getValue()); 78 | } 79 | 80 | @isTest 81 | static void it_should_return_week_in_month_string() { 82 | QueryDate dt = QueryDate.WEEK_IN_MONTH(Schema.User.CreatedDate); 83 | System.assertEquals('WEEK_IN_MONTH(CreatedDate)', dt.getValue()); 84 | } 85 | 86 | @isTest 87 | static void it_should_return_week_in_year_string() { 88 | QueryDate dt = QueryDate.WEEK_IN_YEAR(Schema.User.CreatedDate); 89 | System.assertEquals('WEEK_IN_YEAR(CreatedDate)', dt.getValue()); 90 | } 91 | 92 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/dml-management/classes/DML.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Repository 9 | * 10 | * @description Provides methods with default behavior for DML actions (insert, upsert, etc) 11 | * 12 | */ 13 | public abstract class DML extends NebulaCore implements IDML { 14 | 15 | public virtual List insertRecords(SObject record) { 16 | return this.insertRecords(new List{record}); 17 | } 18 | 19 | public virtual List insertRecords(List records) { 20 | return Database.insert(records); 21 | } 22 | 23 | public virtual List updateRecords(SObject record) { 24 | return this.updateRecords(new List{record}); 25 | } 26 | 27 | public virtual List updateRecords(List records) { 28 | return Database.update(records); 29 | } 30 | 31 | public virtual List upsertRecords(SObject record) { 32 | return this.upsertRecords(this.castRecords(record)); 33 | } 34 | 35 | public virtual List upsertRecords(List records) { 36 | return Database.upsert(records); 37 | } 38 | 39 | public virtual List undeleteRecords(SObject record) { 40 | return this.undeleteRecords(new List{record}); 41 | } 42 | 43 | public virtual List undeleteRecords(List records) { 44 | return Database.undelete(records); 45 | } 46 | 47 | public virtual List deleteRecords(SObject record) { 48 | return this.deleteRecords(new List{record}); 49 | } 50 | 51 | public virtual List deleteRecords(List records) { 52 | return Database.delete(records); 53 | } 54 | 55 | public virtual List hardDeleteRecords(SObject record) { 56 | return this.hardDeleteRecords(new List{record}); 57 | } 58 | 59 | public virtual List hardDeleteRecords(List records) { 60 | List results = this.deleteRecords(records); 61 | if(!records.isEmpty()) Database.emptyRecycleBin(records); 62 | return results; 63 | } 64 | 65 | // Not all objects will have external ID fields, so these methods are protected (instead of public) 66 | // Any object that needs an upsert by external ID can expose these methods in their repos 67 | protected virtual List upsertRecords(SObject record, Schema.SObjectField externalIdField) { 68 | return this.upsertRecords(this.castRecords(record), externalIdField); 69 | } 70 | 71 | protected virtual List upsertRecords(List records, Schema.SObjectField externalIdField) { 72 | return Database.upsert(records, externalIdField); 73 | } 74 | 75 | private List castRecords(SObject record) { 76 | // Salesforce will only allow upsert calls for SObjects if a declared-type list is passed in. 77 | // This is fine for the bulk method, where we can assume the caller is passing in an explicit list, but for a single record, 78 | // the only way to successfully perform the upsert is to dynamically spin up a list of the SObject's type 79 | 80 | String listType = 'List<' + record.getSObjectType() + '>'; 81 | List castRecords = (List)Type.forName(listType).newInstance(); 82 | castRecords.add(record); 83 | 84 | return castRecords; 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/logger/classes/Logger.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Logging 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public without sharing class Logger { 14 | 15 | private static Id logId; 16 | private static Attachment logAttachment; 17 | @testVisible private static List logMessages; 18 | 19 | static { 20 | Logger.logMessages = Logger.logMessages == null ? new List() : Logger.logMessages; 21 | Logger.logAttachment = Logger.logAttachment == null ? createLogAttachment() : Logger.logAttachment; 22 | } 23 | 24 | public static void addEntry(String message) { 25 | addEntry(null, message); 26 | } 27 | 28 | public static void addEntry(INebulaCore moduleClass, String message) { 29 | Logger.Message logMessage = new Logger.Message(moduleClass, message); 30 | Logger.logMessages.add(logMessage); 31 | } 32 | 33 | public static void saveLogs() { 34 | if(!NebulaSettings.LoggerSettings.EnableLogging__c) return; 35 | 36 | saveTransactionLog(); 37 | saveSingleLogFile(); 38 | } 39 | 40 | private static void saveTransactionLog() { 41 | if(Logger.logId != null) return; 42 | 43 | NebulaLog__c newLog = new NebulaLog__c( 44 | InitialClass__c = NebulaCore.INITIAL_CLASS, 45 | TransactionId__c = NebulaCore.TRANSACTION_ID 46 | ); 47 | insert newLog; 48 | Logger.logId = newLog.Id; 49 | } 50 | 51 | private static void saveSingleLogFile() { 52 | String parsedMessageString = 53 | 'NebulaCore.TRANSACTION_ID: ' + NebulaCore.TRANSACTION_ID 54 | + '\nInitial Class: ' + NebulaCore.INITIAL_CLASS; 55 | String divider = '\n______________________________\n'; 56 | 57 | for(Logger.Message logMessage : logMessages) { 58 | parsedMessageString = 59 | parsedMessageString 60 | + divider 61 | + '\nCurrent Module: ' + logMessage.ClassModule 62 | + '\nCurrent Class: ' + logMessage.ClassName 63 | + '\nTimestamp: ' + logMessage.Timestamp.format('yyyy-MM-dd_hh:mm:ss.SS') 64 | + '\n\n' + logMessage.Message; 65 | } 66 | 67 | if(Logger.logAttachment.Id == null) { 68 | Logger.logAttachment.Name = NebulaCore.TRANSACTION_ID; 69 | Logger.logAttachment.ParentId = Logger.logId; 70 | } 71 | 72 | Logger.logAttachment.Body = Blob.valueOf(parsedMessageString); 73 | 74 | upsert Logger.logAttachment; 75 | } 76 | 77 | private static Attachment createLogAttachment() { 78 | Attachment attachment = new Attachment( 79 | ContentType = 'text/plain', 80 | IsPrivate = false, 81 | ParentId = Logger.logId 82 | ); 83 | 84 | return attachment; 85 | } 86 | 87 | private class Message { 88 | public NebulaCore.Module ClassModule {get; private set;} 89 | public String ClassName {get; private set;} 90 | public String Message {get; private set;} 91 | public Datetime Timestamp {get; private set;} 92 | 93 | public Message(INebulaCore moduleClass, String message) { 94 | this.ClassModule = moduleClass == null ? null : moduleClass.getClassModule(); 95 | this.ClassName = moduleClass == null ? null : moduleClass.getClassName(); 96 | this.Message = message; 97 | this.Timestamp = System.now(); 98 | } 99 | } 100 | 101 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryDate.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description Used to dynamically generate SOQL & SOSQL date functions. 11 | * "Date functions in SOQL queries allow you to group or filter data by date periods such as day, calendar month, or fiscal year." 12 | * Salesforce docs: developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_date_functions.htm 13 | * 14 | */ 15 | public without sharing class QueryDate extends NebulaCore { 16 | 17 | private Schema.SObjectField sobjectField; 18 | private Schema.SObjectType sobjectType; 19 | private String value; 20 | 21 | private QueryDate() { 22 | this.currentModule = NebulaCore.Module.QUERY_BUILDER; 23 | } 24 | 25 | public String getValue() { 26 | return this.value; 27 | } 28 | 29 | public Schema.SObjectType getSObjectType() { 30 | return this.sobjectType; 31 | } 32 | 33 | private QueryDate setValue(Schema.SObjectField sobjectField, String value) { 34 | this.sobjectField = sobjectField; 35 | this.sobjectType = new SObjectFieldDescriber(sobjectField).getSObjectType(); 36 | this.value = value; 37 | return this; 38 | } 39 | 40 | public static QueryDate CALENDAR_MONTH(Schema.SObjectField sobjectField) { 41 | return buildQueryDate('CALENDAR_MONTH', sobjectField); 42 | } 43 | 44 | public static QueryDate CALENDAR_QUARTER(Schema.SObjectField sobjectField) { 45 | return buildQueryDate('CALENDAR_QUARTER', sobjectField); 46 | } 47 | 48 | public static QueryDate CALENDAR_YEAR(Schema.SObjectField sobjectField) { 49 | return buildQueryDate('CALENDAR_YEAR', sobjectField); 50 | } 51 | 52 | public static QueryDate DAY_IN_MONTH(Schema.SObjectField sobjectField) { 53 | return buildQueryDate('DAY_IN_MONTH', sobjectField); 54 | } 55 | 56 | public static QueryDate DAY_IN_WEEK(Schema.SObjectField sobjectField) { 57 | return buildQueryDate('DAY_IN_WEEK', sobjectField); 58 | } 59 | 60 | public static QueryDate DAY_IN_YEAR(Schema.SObjectField sobjectField) { 61 | return buildQueryDate('DAY_IN_YEAR', sobjectField); 62 | } 63 | 64 | public static QueryDate DAY_ONLY(Schema.SObjectField sobjectField) { 65 | return buildQueryDate('DAY_ONLY', sobjectField); 66 | } 67 | 68 | public static QueryDate FISCAL_MONTH(Schema.SObjectField sobjectField) { 69 | return buildQueryDate('FISCAL_MONTH', sobjectField); 70 | } 71 | 72 | public static QueryDate FISCAL_QUARTER(Schema.SObjectField sobjectField) { 73 | return buildQueryDate('FISCAL_QUARTER', sobjectField); 74 | } 75 | 76 | public static QueryDate FISCAL_YEAR(Schema.SObjectField sobjectField) { 77 | return buildQueryDate('FISCAL_YEAR', sobjectField); 78 | } 79 | 80 | public static QueryDate HOUR_IN_DAY(Schema.SObjectField sobjectField) { 81 | return buildQueryDate('HOUR_IN_DAY', sobjectField); 82 | } 83 | 84 | public static QueryDate WEEK_IN_MONTH(Schema.SObjectField sobjectField) { 85 | return buildQueryDate('WEEK_IN_MONTH', sobjectField); 86 | } 87 | 88 | public static QueryDate WEEK_IN_YEAR(Schema.SObjectField sobjectField) { 89 | return buildQueryDate('WEEK_IN_YEAR', sobjectField); 90 | } 91 | 92 | private static QueryDate buildQueryDate(String dateOperation, Schema.SObjectField sobjectField) { 93 | String value = dateOperation + '(' + sobjectField.getDescribe().getName() + ')'; 94 | return new QueryDate().setValue(sobjectField, value); 95 | } 96 | 97 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nebula Framework for Salesforce Apex 2 | [![Deploy to Salesforce](https://img.shields.io/badge/salesforce-deploy-blue.svg)](https://githubsfdeploy.herokuapp.com) 3 | [![License: MIT](https://img.shields.io/badge/license-MIT-d742f4.svg)](https://opensource.org/licenses/MIT) 4 | [![Travis CI](https://img.shields.io/travis/jongpie/NebulaFramework/master.svg)](https://travis-ci.org/jongpie/NebulaFramework) 5 | [![Code Climate](https://img.shields.io/codeclimate/github/jongpie/NebulaFramework.svg)](https://codeclimate.com/github/jongpie/NebulaFramework) 6 | 7 | Nebula is a development framework for Salesforce's Apex language on the Force.com platform. It aims to... 8 | 1. Provide a foundation for Apex development, with the flexibility to be easily adapted to meet your implementation needs 9 | 2. Promote the design of scalable, bulkified code 10 | 3. Standardise how your code is written & organised 11 | 4. Overcome some gaps in Apex and the Force.com platform 12 | 13 | ## Features 14 | Nebula focusses on streamlining how you work with SObjects 15 | 1. **SObjectRepository.cls** - this module handles all DML actions & querying needs for an SObject, making the implementation of SObjects much easier & faster 16 | * **QueryBuilder.cls** powers Nebula's querying, allowing you to dynamically build reusable SOQL & SOSL queries 17 | * **SObjectRepositoryMock.cls** can be used in unit tests for test-driven development (TDD) & to drastically reduce the time of your unit tests. 18 | 2. **SObjectTriggerHandler.cls** - this module provides a trigger framework to handle all trigger contexts provided by Salesforce, along with additional features like recursion prevention 19 | 20 | The framework also provides several additional classes to make development easier 21 | 1. **SObjectRecordTypes.cls** - record types are an important feature of the Force.com platform. Unfortunately, Apex has limitations with handling them - record types have a field called DeveloperName that (you guessed it!) should be used by developers... but native Apex describe methods cannot access this field. Nebula tries to overcome this by providing cacheable query results of record types so you can access the DeveloperName. 22 | 2. **Logger.cls** - a flexible logging solution for Apex, leveraged by the framework itself 23 | 3. **Environment.cls** - provides information about the current Salesforce environment 24 | 4. **UUID.cls** - used to reate a randomly-generated unique ID in your code, using the Universally Unique Identifier (UUID) standard 25 | 26 | ## Usage 27 | Nebula uses interfaces, virtual & abstract classes and some Salesforce features (like custom settings) to provide a baseline for your own Apex development. You can deploy the latest version of Nebula to your org and build your implementation on top of it. If you want to customise how Nebula works, most classes & methods can be overridden with your own logic. Ideally, you should minimise any code changes to Nebula's classes so that you can easily upgrade in the future when new versions of Nebula are released. 28 | 29 | Nebula also leverages custom settings to give you control over how the framework works within your Salesforce environment. There are 4 settings 30 | 1. **Logger Settings (API Name: NebulaLoggerSettings__c)** 31 | * Enable or disable logging 32 | 2. **Record Type Settings (API Name: NebulaRecordTypesSettings__c)** 33 | * Choose how record types are cached 34 | * Select if you want to include managed record types 35 | 3. **Repository Settings (API Name: NebulaRepositorySettings__c)** 36 | * Automatically include common fields in your queries, like record ID, audit fields (CreatedById, CreatedDate, etc), Name field (or Subject field, where applicable) and more 37 | 4. **Trigger Handler Settings (API Name: NebulaTriggerHandlerSettings__c)** 38 | * Easily disable all triggers & handlers (great for data migration and other admin tasks), 39 | * Enable or disable recursion prevention 40 | 41 | ## Versioning 42 | Releases are versioned using [Semantic Versioning](http://semver.org/) in the format 'v1.0.2' (MAJOR.MINOR.PATCH): 43 | 44 | - MAJOR version when incompatible API changes are made 45 | - MINOR version new functionality is added in a backwards-compatible manner 46 | - PATCH version when backwards-compatible bug fixes are made -------------------------------------------------------------------------------- /nebula-app-framework/tests/trigger-handlers/classes/SObjectTriggerHandler_Tests.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | @isTest 6 | private class SObjectTriggerHandler_Tests { 7 | 8 | private class LeadTriggerHandlerTest extends SObjectTriggerHandler { 9 | public LeadTriggerHandlerTest() { 10 | super(true); 11 | } 12 | public override void executeBeforeInsert(List newRecordList) { 13 | List newLeadList = (List)this.recordList; 14 | 15 | for(Lead newLead : newLeadList) { 16 | this.setStatus(newLead); 17 | } 18 | } 19 | 20 | private void setStatus(Lead lead) { 21 | // Add logic here. Methods should be simple & independent from each other (except for overloaded methods) 22 | lead.Status = 'Open - Not Contacted'; 23 | } 24 | } 25 | 26 | static List leadList = new List(); 27 | 28 | static void setupData() { 29 | for(Integer i = 0; i < 5; i++) { 30 | Lead lead = new Lead( 31 | Company = 'My Test Company', 32 | LastName = 'Gillespie', 33 | Status = '' 34 | ); 35 | leadList.add(lead); 36 | } 37 | insert leadList; 38 | } 39 | 40 | @isTest 41 | static void shouldNotExecuteTriggers() { 42 | NebulaTriggerHandlerSettings__c triggerHandlerSettings = NebulaTriggerHandlerSettings__c.getInstance(); 43 | triggerHandlerSettings.ExecuteTriggers__c = false; 44 | upsert triggerHandlerSettings; 45 | 46 | LeadTriggerHandlerTest leadTriggerHandler = new LeadTriggerHandlerTest(); 47 | leadTriggerHandler.currentTriggerContext = SObjectTriggerHandler.TriggerContext.BEFORE_INSERT; 48 | leadTriggerHandler.recordList = leadList; 49 | 50 | Test.startTest(); 51 | 52 | leadTriggerHandler.execute(); 53 | 54 | Test.stopTest(); 55 | } 56 | 57 | // @isTest 58 | // static void executeAllContexts() { 59 | // setupData(); 60 | // LeadTriggerHandlerTest leadTriggerHandler = new LeadTriggerHandlerTest(); 61 | // leadTriggerHandler.recordList = leadList; 62 | 63 | // Test.startTest(); 64 | // for(SObjectTriggerHandler.TriggerContext context : SObjectTriggerHandler.TriggerContext.values()) { 65 | // leadTriggerHandler.currentTriggerContext = context; 66 | // leadTriggerHandler.execute(); 67 | // } 68 | // Test.stopTest(); 69 | // } 70 | 71 | // @isTest 72 | // static void executeBeforeInsert() { 73 | // LeadTriggerHandlerTest leadTriggerHandler = new LeadTriggerHandlerTest(); 74 | // // Set the variables for the relevant context 75 | // leadTriggerHandler.currentTriggerContext = SObjectTriggerHandler.TriggerContext.BEFORE_INSERT; 76 | // leadTriggerHandler.recordList = leadList; 77 | 78 | // Test.startTest(); 79 | 80 | // leadTriggerHandler.execute(); 81 | 82 | // Test.stopTest(); 83 | // } 84 | 85 | // @isTest 86 | // static void executeBeforeUpdate() { 87 | // LeadTriggerHandlerTest leadTriggerHandler = new LeadTriggerHandlerTest(); 88 | // // Set the variables for the relevant context 89 | // leadTriggerHandler.currentTriggerContext = SObjectTriggerHandler.TriggerContext.BEFORE_UPDATE; 90 | // leadTriggerHandler.recordList = leadList; 91 | // leadTriggerHandler.recordMap = new Map(leadList); 92 | // leadTriggerHandler.oldRecordList = leadList; 93 | // leadTriggerHandler.oldRecordMap = new Map(leadList); 94 | 95 | // Test.startTest(); 96 | 97 | // leadTriggerHandler.execute(); 98 | 99 | // Test.stopTest(); 100 | // } 101 | 102 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryArgumentFormatter.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public virtual class QueryArgumentFormatter extends NebulaCore implements IQueryArgumentFormatter { 14 | 15 | private String value; 16 | 17 | public QueryArgumentFormatter(Object valueToFormat) { 18 | this.currentModule = NebulaCore.Module.QUERY_BUILDER; 19 | this.value = this.objectToQueryString(valueToFormat); 20 | } 21 | 22 | public virtual String getValue() { 23 | return this.value; 24 | } 25 | 26 | protected virtual String objectToQueryString(Object valueToFormat) { 27 | if(valueToFormat == null) return null; 28 | else if(CollectionUtils.isList(valueToFormat)) return this.listToQueryString((List)valueToFormat); 29 | else if(CollectionUtils.isSet(valueToFormat)) return this.setToQueryString(valueToFormat); 30 | else if(CollectionUtils.isMap(valueToFormat)) return this.mapToQueryString(valueToFormat); 31 | else if(valueToFormat instanceof QueryDateLiteral) { 32 | QueryDateLiteral dateLiteral = (QueryDateLiteral)valueToFormat; 33 | return dateLiteral.getValue(); 34 | } 35 | else if(valueToFormat instanceof Boolean) return String.valueOf((Boolean)valueToFormat); 36 | else if(valueToFormat instanceof Date) return String.valueOf((Date)valueToFormat); 37 | else if(valueToFormat instanceof Datetime) { 38 | Datetime datetimeValue = (Datetime)valueToFormat; 39 | return datetimeValue.format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\'', 'Greenwich Mean Time'); 40 | } 41 | else if(valueToFormat instanceof Decimal) return String.valueOf((Decimal)valueToFormat); 42 | else if(valueToFormat instanceof Double) return String.valueOf((Double)valueToFormat); 43 | else if(valueToFormat instanceof Integer) return String.valueOf((Integer)valueToFormat); 44 | else if(valueToFormat instanceof Long) return String.valueOf((Long)valueToFormat); 45 | else if(valueToFormat instanceof SObject) { 46 | SObject record = (SObject)valueToFormat; 47 | return wrapInSingleQuotes(record.Id); 48 | } 49 | else if(valueToFormat instanceof Schema.SObjectType) { 50 | Schema.SObjectType sobjectType = (Schema.SObjectType)valueToFormat; 51 | return wrapInSingleQuotes(sobjectType.getDescribe().getName()); 52 | } 53 | else if(valueToFormat instanceof String) { 54 | // Escape single quotes to prevent SOQL/SOSL injection 55 | String stringArgument = String.escapeSingleQuotes((String)valueToFormat); 56 | return wrapInSingleQuotes(stringArgument); 57 | } 58 | else return String.valueOf(valueToFormat); 59 | } 60 | 61 | private String listToQueryString(List valueList) { 62 | List parsedValueList = new List(); 63 | for(Object value : valueList) parsedValueList.add(this.objectToQueryString(value)); 64 | return '(' + String.join(parsedValueList, ', ') + ')'; 65 | } 66 | 67 | private String setToQueryString(Object valueSet) { 68 | String unformattedString = String.valueOf(valueSet).replace('{', '').replace('}', ''); 69 | List parsedValueList = new List(); 70 | for(String collectionItem : unformattedString.split(',')) { 71 | parsedValueList.add(this.objectToQueryString(collectionItem)); 72 | } 73 | 74 | return '(' + String.join(parsedValueList, ', ') + ')'; 75 | } 76 | 77 | private String mapToQueryString(Object valueMap) { 78 | Map m = (Map)JSON.deserializeUntyped(JSON.serialize(valueMap)); 79 | 80 | return this.setToQueryString(m.keySet()); 81 | } 82 | 83 | private String wrapInSingleQuotes(String input) { 84 | input = input.trim(); 85 | 86 | if(input.left(1) != '\'') input = '\'' + input; 87 | if(input.right(1) != '\'') input = input + '\''; 88 | return input; 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/utilities/classes/CollectionUtils.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Utils 9 | * 10 | * @description A utility class to help with dealing with collections (lists, sets & maps) 11 | * 12 | */ 13 | public without sharing class CollectionUtils { 14 | 15 | /** 16 | * @description Returns the last item in a list 17 | * @param listOfItems the list to check 18 | * @return The last Object in the provided list 19 | * @example 20 | * List myList = new List{'A', B', 'C'}; 21 | * String lastItem = CollectionUtils.getLastItem(myList); 22 | * System.assertEquals('C', lastItem); 23 | */ 24 | public static Object getLastItem(List listOfItems) { 25 | Integer indexOfItem = listOfItems.size() - 1; 26 | return listOfItems[indexOfItem]; 27 | } 28 | 29 | /** 30 | * @description Removes the last item in the provided list & returns the item 31 | * @param listOfItems the list to check 32 | * @return The last Object in the provided list 33 | * @example 34 | * List myList = new List{'A', B', 'C'}; 35 | * System.assertEquals(3, myList.size()); 36 | * String lastItem = CollectionUtils.getLastItem(myList); 37 | * System.assertEquals('C', lastItem); 38 | * System.assertEquals(2, myList.size()); 39 | */ 40 | public static Object pop(List listToSplice) { 41 | return splice(listToSplice, listToSplice.size() - 1); 42 | } 43 | 44 | /** 45 | * @description Removes the item in the specified index from the provided list & returns the item 46 | * @param listOfItems The list to check 47 | * @param indexOfItem The index of the item to remove 48 | * @return The Object at the specified index 49 | * @example 50 | * List myList = new List{'A', B', 'C'}; 51 | * System.assertEquals(3, myList.size()); 52 | * String itemToRemove = CollectionUtils.splice(myList, 1); 53 | * System.assertEquals('B', itemToRemove); 54 | * System.assertEquals(2, myList.size()); 55 | */ 56 | public static Object splice(List listToSplice, Integer indexOfItem) { 57 | Object itemToRemove = listToSplice[indexOfItem]; 58 | listToSplice.remove(indexOfItem); 59 | return itemToRemove; 60 | } 61 | 62 | /** 63 | * @description Determines if the provided input is a type of collection (list, set or map) 64 | * @param input The Object to check 65 | * @return true if the item is a type of collection, otherwise returns false 66 | * @example 67 | * List myList = new List{'A', 'B', 'C'}; 68 | * System.assert(CollectionUtils.isCollection(myList)); 69 | */ 70 | public static Boolean isCollection(Object input) { 71 | return isList(input) || isSet(input) || isMap(input); 72 | } 73 | 74 | public static Boolean isList(Object input) { 75 | // If we can cast the object to a list of objects, then it's a list 76 | try { 77 | Object convertedInput = (List)input; 78 | return true; 79 | } catch(System.TypeException ex) { 80 | return false; 81 | } 82 | } 83 | 84 | public static Boolean isSet(Object input) { 85 | // We can't cast the object to a set of objects 86 | // But if we try to cast it to a list of objects & it's a set, 87 | // then a TypeException is thrown so we know it's a set 88 | try { 89 | Object convertedInput = (List)input; 90 | return false; 91 | } catch(System.TypeException ex) { 92 | return ex.getMessage().contains('Set<'); 93 | } 94 | } 95 | 96 | public static Boolean isMap(Object input) { 97 | // We can't cast the object to a map of objects 98 | // But if we try to cast it to a list of objects & it's a map, 99 | // then a TypeException is thrown so we know it's a map 100 | try { 101 | Object convertedInput = (List)input; 102 | return false; 103 | } catch(System.TypeException ex) { 104 | return ex.getMessage().contains('Map<'); 105 | } 106 | } 107 | 108 | } -------------------------------------------------------------------------------- /nebula-app-framework/tests/utilities/classes/SObjectRecordTypes_Tests.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | @isTest 6 | private class SObjectRecordTypes_Tests { 7 | 8 | private class UserRecordTypes extends SObjectRecordTypes { 9 | // Test subclass that extends SObjectRecordTypes 10 | public override Schema.SObjectType getSObjectType() { 11 | return Schema.User.SObjectType; 12 | } 13 | } 14 | 15 | @isTest 16 | static void it_should_return_a_map_of_all_record_types_by_id() { 17 | List expectedRecordTypeList = [SELECT Id, DeveloperName FROM RecordType WHERE SObjectType = 'User']; 18 | 19 | Test.startTest(); 20 | 21 | System.assertEquals(expectedRecordTypeList.size(), new SObjectRecordTypes_Tests.UserRecordTypes().getAllById().size()); 22 | for(RecordType recordType : expectedRecordTypeList) { 23 | System.assert(new SObjectRecordTypes_Tests.UserRecordTypes().getAllById().containsKey(recordType.Id)); 24 | } 25 | 26 | Test.stopTest(); 27 | } 28 | 29 | @isTest 30 | static void it_should_return_a_map_of_all_record_types_by_developer_name() { 31 | List expectedRecordTypeList = [SELECT Id, DeveloperName FROM RecordType WHERE SObjectType = 'User']; 32 | 33 | Test.startTest(); 34 | 35 | System.assertEquals(expectedRecordTypeList.size(), new SObjectRecordTypes_Tests.UserRecordTypes().getAllByDeveloperName().size()); 36 | for(RecordType recordType : expectedRecordTypeList) { 37 | System.assert(new SObjectRecordTypes_Tests.UserRecordTypes().getAllByDeveloperName().containsKey(recordType.DeveloperName)); 38 | } 39 | 40 | Test.stopTest(); 41 | } 42 | 43 | 44 | @isTest 45 | static void it_should_return_a_map_of_all_record_types_by_id_excluding_managed_record_types() { 46 | List expectedRecordTypeList = [SELECT Id, DeveloperName, NamespacePrefix FROM RecordType WHERE SObjectType = 'User' AND NamespacePrefix = null]; 47 | 48 | NebulaRecordTypesSettings__c recordTypesSettings = NebulaRecordTypesSettings__c.getInstance(); 49 | recordTypesSettings.IncludeManagedRecordTypes__c = false; 50 | upsert recordTypesSettings; 51 | 52 | Test.startTest(); 53 | 54 | System.assertEquals(expectedRecordTypeList.size(), new SObjectRecordTypes_Tests.UserRecordTypes().getAllById().size()); 55 | for(RecordType recordType : expectedRecordTypeList) { 56 | System.assert(new SObjectRecordTypes_Tests.UserRecordTypes().getAllById().containsKey(recordType.Id)); 57 | } 58 | for(RecordType recordType : new SObjectRecordTypes_Tests.UserRecordTypes().getAllById().values()) { 59 | System.assertEquals(null, recordType.NamespacePrefix); 60 | } 61 | 62 | Test.stopTest(); 63 | } 64 | 65 | @isTest 66 | static void it_should_cache_the_query_results_when_lazy_load_is_enabled() { 67 | NebulaRecordTypesSettings__c recordTypesSettings = NebulaRecordTypesSettings__c.getInstance(); 68 | recordTypesSettings.LazyLoad__c = true; 69 | upsert recordTypesSettings; 70 | 71 | Test.startTest(); 72 | 73 | System.assertEquals(0, Limits.getQueries()); 74 | for(Integer i = 0; i < 10; i++) { 75 | System.debug(new SObjectRecordTypes_Tests.UserRecordTypes().getAllById()); 76 | } 77 | 78 | System.assertEquals(1, Limits.getQueries()); 79 | 80 | Test.stopTest(); 81 | } 82 | 83 | @isTest 84 | static void it_should_cache_the_query_results_when_lazy_load_is_disabled() { 85 | NebulaRecordTypesSettings__c recordTypesSettings = NebulaRecordTypesSettings__c.getInstance(); 86 | recordTypesSettings.LazyLoad__c = false; 87 | upsert recordTypesSettings; 88 | 89 | Test.startTest(); 90 | 91 | System.assertEquals(0, Limits.getQueries()); 92 | for(Integer i = 0; i < 10; i++) { 93 | System.debug(new SObjectRecordTypes_Tests.UserRecordTypes().getAllById()); 94 | } 95 | 96 | System.assertEquals(1, Limits.getQueries()); 97 | 98 | Test.stopTest(); 99 | } 100 | 101 | } -------------------------------------------------------------------------------- /nebula-app-framework/tests/dml-management/classes/DMLMock.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Repository 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | @isTest 14 | public class DMLMock { 15 | 16 | public virtual class Base implements IDML { 17 | 18 | // TODO consider changing method name to singular, return type to single Database.SaveResult 19 | public List insertRecords(SObject record) { 20 | return this.insertRecords(new List{record}); 21 | } 22 | 23 | public List insertRecords(List recordList) { 24 | TestingUtils.generateIds(recordList); 25 | TestingUtils.insertedRecords.addAll(recordList); 26 | // TODO add logic to try to actually return real results, based on the record list provided 27 | return new List(); 28 | } 29 | 30 | public List updateRecords(SObject record) { 31 | this.updateRecords(new List{record}); 32 | // TODO add logic to try to actually return real results, based on the record list provided 33 | return new List(); 34 | } 35 | 36 | public List updateRecords(List recordList) { 37 | TestingUtils.updatedRecords.addAll(recordList); 38 | // TODO add logic to try to actually return real results, based on the record list provided 39 | return new List(); 40 | } 41 | 42 | public List upsertRecords(SObject record) { 43 | this.upsertRecords(new List{record}); 44 | // TODO add logic to try to actually return real results, based on the record list provided 45 | return new List(); 46 | } 47 | 48 | public List upsertRecords(List recordList) { 49 | TestingUtils.generateIds(recordList); 50 | TestingUtils.upsertedRecords.addAll(recordList); 51 | // TODO add logic to try to actually return real results, based on the record list provided 52 | return new List(); 53 | } 54 | 55 | public List undeleteRecords(SObject record) { 56 | this.undeleteRecords(new List{record}); 57 | // TODO add logic to try to actually return real results, based on the record list provided 58 | return new List(); 59 | } 60 | 61 | public List undeleteRecords(List recordList) { 62 | TestingUtils.undeletedRecords.addAll(recordList); 63 | // TODO add logic to try to actually return real results, based on the record list provided 64 | return new List(); 65 | } 66 | 67 | public List deleteRecords(SObject record) { 68 | this.deleteRecords(new List{record}); 69 | // TODO add logic to try to actually return real results, based on the record list provided 70 | return new List(); 71 | } 72 | 73 | public List deleteRecords(List recordList) { 74 | if(recordList != null) TestingUtils.deletedRecords.addAll(recordList); 75 | // TODO add logic to try to actually return real results, based on the record list provided 76 | return new List(); 77 | } 78 | 79 | public List hardDeleteRecords(SObject record) { 80 | this.hardDeleteRecords(new List{record}); 81 | // TODO add logic to try to actually return real results, based on the record list provided 82 | return new List(); 83 | } 84 | 85 | public List hardDeleteRecords(List recordList) { 86 | this.deleteRecords(recordList); 87 | // TODO add logic to try to actually return real results, based on the record list provided 88 | return new List(); 89 | } 90 | 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/QueryFilter_Tests.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | @isTest 6 | private class QueryFilter_Tests { 7 | 8 | @isTest 9 | static void it_should_return_the_query_filter_for_a_field() { 10 | Schema.SObjectField sobjectField = Schema.User.CompanyName; 11 | QueryOperator operator = QueryOperator.IS_IN; 12 | List providedValues = new List{'derp', 'herp'}; 13 | 14 | Test.startTest(); 15 | QueryFilter queryFilter = (QueryFilter)new QueryFilter().filterByField(new QueryField(sobjectField), operator, providedValues); 16 | Test.stopTest(); 17 | 18 | String expectedQueryFilter = 'CompanyName ' + operator.getValue() + ' (\'' + String.join(providedValues, '\', \'') + '\')'; 19 | System.assertEquals(expectedQueryFilter, queryFilter.getValue()); 20 | } 21 | 22 | @isTest 23 | static void it_should_return_the_query_filter_for_a_parent_field() { 24 | QueryField parentFieldToFilter = new QueryField(new List{ 25 | Schema.Lead.CreatedById, Schema.User.Email 26 | }); 27 | QueryOperator operator = QueryOperator.EQUALS; 28 | String providedValue = 'derp@test.com'; 29 | 30 | Test.startTest(); 31 | QueryFilter queryFilter = (QueryFilter)new QueryFilter().filterByField(parentFieldToFilter, operator, providedValue); 32 | Test.stopTest(); 33 | 34 | String expectedQueryFilter = 'CreatedBy.Email ' + operator.getValue() + ' \'' + providedValue + '\''; 35 | System.assertEquals(expectedQueryFilter, queryFilter.getValue()); 36 | } 37 | 38 | @isTest 39 | static void it_should_return_the_query_filter_for_a_grandparent_field() { 40 | QueryField grandparentFieldToFilter = new QueryField(new List{ 41 | Schema.Lead.OwnerId, Schema.User.ManagerId, Schema.User.ProfileId, Schema.Profile.Name 42 | }); 43 | QueryOperator operator = QueryOperator.EQUALS; 44 | String providedValue = 'derp'; 45 | 46 | Test.startTest(); 47 | QueryFilter queryFilter = (QueryFilter)new QueryFilter().filterByField(grandparentFieldToFilter, operator, providedValue); 48 | Test.stopTest(); 49 | 50 | String expectedQueryFilter = 'Owner.Manager.Profile.Name ' + operator.getValue() + ' \'' + providedValue + '\''; 51 | System.assertEquals(expectedQueryFilter, queryFilter.getValue()); 52 | } 53 | 54 | @isTest 55 | static void it_should_return_the_query_filter_for_a_query_date() { 56 | QueryDate qd = QueryDate.CALENDAR_MONTH(Schema.Lead.CreatedDate); 57 | QueryOperator operator = QueryOperator.EQUALS; 58 | Integer providedValue = 3; 59 | 60 | Test.startTest(); 61 | QueryFilter queryFilter = (QueryFilter)new QueryFilter().filterByQueryDate(qd, operator, providedValue); 62 | Test.stopTest(); 63 | 64 | String expectedQueryFilter = 'CALENDAR_MONTH(CreatedDate) ' + operator.getValue() + ' ' + providedValue; 65 | System.assertEquals(expectedQueryFilter, queryFilter.getValue()); 66 | } 67 | 68 | @isTest 69 | static void it_should_return_the_query_filter_for_a_subquery() { 70 | QueryOperator operator = QueryOperator.IS_IN; 71 | Schema.SObjectField lookupFieldOnRelatedSObject = Schema.Lead.ConvertedAccountId; 72 | 73 | Test.startTest(); 74 | QueryFilter queryFilter = (QueryFilter)new QueryFilter().filterBySubquery(operator, lookupFieldOnRelatedSObject); 75 | Test.stopTest(); 76 | 77 | String expectedQueryFilter = 'Id ' + operator.getValue() + ' (SELECT ' + lookupFieldOnRelatedSObject.getDescribe().getName() + ' FROM Lead)'; 78 | System.assertEquals(expectedQueryFilter, queryFilter.getValue()); 79 | } 80 | 81 | @isTest 82 | static void it_should_return_the_query_filter_for_a_subquery_with_a_specified_field() { 83 | QueryOperator operator = QueryOperator.IS_IN; 84 | Schema.SObjectField lookupField = Schema.Lead.OwnerId; 85 | Schema.SObjectField lookupFieldOnRelatedSObject = Schema.User.Id; 86 | 87 | Test.startTest(); 88 | QueryFilter queryFilter = (QueryFilter)new QueryFilter().filterBySubquery(lookupField, operator, lookupFieldOnRelatedSObject); 89 | Test.stopTest(); 90 | 91 | String expectedQueryFilter = 'OwnerId ' + operator.getValue() + ' (SELECT ' + lookupFieldOnRelatedSObject.getDescribe().getName() + ' FROM User)'; 92 | System.assertEquals(expectedQueryFilter, queryFilter.getValue()); 93 | } 94 | 95 | } -------------------------------------------------------------------------------- /nebula-app-framework/tests/logger/classes/Logger_Tests.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | @isTest 6 | private class Logger_Tests { 7 | 8 | private class FakeNebulaClass extends NebulaCore {} 9 | 10 | @isTest 11 | static void it_should_add_entry_for_a_message_string() { 12 | String message = 'my test message'; 13 | System.assert(Logger.logMessages.isEmpty()); 14 | 15 | Test.startTest(); 16 | Logger.addEntry(message); 17 | Test.stopTest(); 18 | 19 | System.assertEquals(null, Logger.logMessages[0].ClassModule); 20 | System.assertEquals(null, Logger.logMessages[0].ClassName); 21 | System.assertEquals(message, Logger.logMessages[0].Message); 22 | } 23 | 24 | @isTest 25 | static void it_should_add_entry_for_a_module_class_and_a_message_string() { 26 | FakeNebulaClass moduleClass = new FakeNebulaClass(); 27 | String message = 'my test message'; 28 | System.assert(Logger.logMessages.isEmpty()); 29 | 30 | Test.startTest(); 31 | Logger.addEntry(moduleClass, message); 32 | Test.stopTest(); 33 | 34 | System.assertEquals(moduleClass.getClassModule(), Logger.logMessages[0].ClassModule); 35 | System.assertEquals(moduleClass.getClassName(), Logger.logMessages[0].ClassName); 36 | System.assertEquals(message, Logger.logMessages[0].Message); 37 | } 38 | 39 | @isTest 40 | static void it_should_add_entry_for_a_module_class_and_an_exception_and_a_message_string() { 41 | FakeNebulaClass moduleClass = new FakeNebulaClass(); 42 | String message = 'my test message'; 43 | System.assert(Logger.logMessages.isEmpty()); 44 | 45 | Test.startTest(); 46 | Logger.addEntry(moduleClass, message); 47 | Test.stopTest(); 48 | 49 | System.assertEquals(moduleClass.getClassModule(), Logger.logMessages[0].ClassModule); 50 | System.assertEquals(moduleClass.getClassName(), Logger.logMessages[0].ClassName); 51 | System.assertEquals(message, Logger.logMessages[0].Message); 52 | } 53 | 54 | @isTest 55 | static void it_should_save_logs_when_logging_is_enabled() { 56 | FakeNebulaClass moduleClass = new FakeNebulaClass(); 57 | String message = 'my test message'; 58 | System.assert(Logger.logMessages.isEmpty()); 59 | List existingLogs = [SELECT Id FROM NebulaLog__c]; 60 | System.assert(existingLogs.isEmpty()); 61 | 62 | // Enable logging 63 | NebulaLoggerSettings__c nebulaLoggerSettings = NebulaLoggerSettings__c.getInstance(); 64 | nebulaLoggerSettings.EnableLogging__c = true; 65 | upsert nebulaLoggerSettings; 66 | 67 | Logger.addEntry(moduleClass, message); 68 | 69 | Test.startTest(); 70 | String transactionId = NebulaCore.TRANSACTION_ID; 71 | Logger.saveLogs(); 72 | Test.stopTest(); 73 | 74 | List logs = [SELECT Id, TransactionId__c FROM NebulaLog__c]; 75 | System.assertEquals(1, logs.size()); 76 | System.assertEquals(transactionId, logs[0].TransactionId__c); 77 | 78 | List logAttachments = [SELECT Id, Name, ParentId, ContentType, IsPrivate, Body FROM Attachment WHERE ParentId = :logs[0].Id]; 79 | System.assertEquals(1, logAttachments.size()); 80 | System.assertEquals(transactionId, logAttachments[0].Name); 81 | System.assertEquals('text/plain', logAttachments[0].ContentType); 82 | System.assertEquals(false, logAttachments[0].IsPrivate); 83 | String attachmentBody = logAttachments[0].Body.toString(); 84 | System.assert(attachmentBody.contains(message), attachmentBody); 85 | } 86 | 87 | @isTest 88 | static void it_should_not_save_logs_when_logging_is_disabled() { 89 | FakeNebulaClass moduleClass = new FakeNebulaClass(); 90 | String message = 'my test message'; 91 | System.assert(Logger.logMessages.isEmpty()); 92 | List existingLogs = [SELECT Id FROM NebulaLog__c]; 93 | System.assert(existingLogs.isEmpty()); 94 | 95 | // Disable logging 96 | NebulaLoggerSettings__c nebulaLoggerSettings = NebulaLoggerSettings__c.getInstance(); 97 | nebulaLoggerSettings.EnableLogging__c = false; 98 | upsert nebulaLoggerSettings; 99 | 100 | Logger.addEntry(moduleClass, message); 101 | 102 | Test.startTest(); 103 | String transactionId = NebulaCore.TRANSACTION_ID; 104 | Logger.saveLogs(); 105 | Test.stopTest(); 106 | 107 | List logs = [SELECT Id, TransactionId__c FROM NebulaLog__c]; 108 | System.assertEquals(0, logs.size()); 109 | } 110 | 111 | } -------------------------------------------------------------------------------- /nebula-app-framework/tests/sobject-repositories/classes/SObjectRepositoryMocks.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Repository 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | @isTest 14 | public class SObjectRepositoryMocks { 15 | 16 | public virtual class Base extends DMLMock.Base implements ISObjectRepository { 17 | 18 | protected List records; 19 | protected List recordIdList; 20 | protected List queryFilters; 21 | 22 | public Base() { 23 | this.records = new List(); 24 | } 25 | 26 | public Base(List records) { 27 | this.records = records; 28 | } 29 | 30 | public Schema.SObjectType getSObjectType() { 31 | return Schema.User.SObjectType; 32 | } 33 | 34 | public Base with(List records) { 35 | this.records = records; 36 | return this; 37 | } 38 | 39 | public virtual ISObjectRepository filterBy(IQueryFilter queryFilter) { 40 | return this.filterBy(new List{queryFilter}); 41 | } 42 | 43 | public virtual ISObjectRepository filterBy(List queryFilters) { 44 | this.queryFilters = queryFilters; 45 | return this; 46 | } 47 | 48 | // SOQL 49 | public virtual SObject getById(Id recordId) { 50 | return this.getById(new List{recordId})[0]; 51 | } 52 | 53 | public virtual List getById(List recordIdList) { 54 | this.recordIdList = recordIdList; 55 | 56 | if(this.records == null) this.createMockObjects(); 57 | 58 | return this.records; 59 | } 60 | 61 | public virtual List get(List queryFilters) { 62 | this.queryFilters = queryFilters; 63 | return this.records; 64 | } 65 | 66 | public List get(IQueryFilter queryFilter) { 67 | return this.get(new List{queryFilter}); 68 | } 69 | 70 | public List get(IQueryFilter queryFilterOne, IQueryFilter queryFilterTwo) { 71 | return this.get(new List{queryFilterOne, queryFilterTwo}); 72 | } 73 | 74 | public List get(IQueryFilter queryFilterOne, IQueryFilter queryFilterTwo, IQueryFilter queryFilterThree) { 75 | return this.get(new List{queryFilterOne, queryFilterTwo, queryFilterThree}); 76 | } 77 | 78 | public virtual List getQueryFilters() { 79 | return queryFilters; 80 | } 81 | 82 | public virtual List getByIdAndQueryFilters(Set idList, List queryFilters) { 83 | return this.getByIdAndQueryFilters(new List(idList), queryFilters); 84 | } 85 | 86 | public virtual List getByIdAndQueryFilters(List idList, List queryFilters) { 87 | List records = this.getById(idList); 88 | // TODO need to revisit - QueryFilter no longer has the methods needed below, so need a new solution 89 | // for(SObject record : records) { 90 | // for(IQueryFilter queryFilter : queryFilters) { 91 | // if(queryFilter.getSObjectField() == null) continue; 92 | // 93 | // record.put(queryFilter.getSObjectField(), queryFilter.getProvidedValue()); 94 | // } 95 | // } 96 | 97 | return records; 98 | } 99 | 100 | // SOSL 101 | public virtual List getSearchResults(String searchTerm) { 102 | return this.returnListOfSObjects(); 103 | } 104 | 105 | public virtual List getSearchResults(String searchTerm, QuerySearchGroup searchGroup) { 106 | return this.returnListOfSObjects(); 107 | } 108 | 109 | public virtual List getSearchResults(String searchTerm, QuerySearchGroup searchGroup, List queryFilters) { 110 | return this.returnListOfSObjects(); 111 | } 112 | 113 | private void createMockObjects() { 114 | // We would expect that for the Ids passed in, there will be a corresponding number of records returned of the exact same 115 | // SObjectType as their Ids. 116 | this.records = new List(); 117 | for(Id recordId : this.recordIdList) { 118 | SObjectType objType = recordId.getSObjectType(); 119 | SObject record = (SObject)Type.forName(String.valueOf(objType)).newInstance(); 120 | record.put('Id', recordId); 121 | 122 | this.records.add(record); 123 | } 124 | } 125 | 126 | private List returnListOfSObjects() { 127 | if(this.records == null) return new List(); 128 | 129 | return this.records; 130 | } 131 | } 132 | 133 | } -------------------------------------------------------------------------------- /tools/codeclimate/apex/style.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | The Style Ruleset contains rules regarding preferred usage of names and identifiers. 9 | 10 | 11 | 16 | 17 | A variable naming conventions rule - customize this to your liking. Currently, it 18 | checks for final variables that should be fully capitalized and non-final variables 19 | that should not include underscores. 20 | 21 | 1 22 | 23 | 29 | 30 | 31 | 32 | 37 | 38 | Method names should always begin with a lower case character, and should not contain underscores. 39 | 40 | 1 41 | 42 | 47 | 48 | 49 | 50 | 55 | 56 | Class names should always begin with an upper case character. 57 | 58 | 1 59 | 60 | 62 | 63 | 64 | 65 | 70 | 71 | Non-constructor methods should not have the same name as the enclosing class. 72 | 73 | 3 74 | 75 | 82 | 83 | 84 | 85 | 90 | 91 | As triggers do not allow methods like regular classes they are less flexible and suited to apply good encapsulation style. 92 | Therefore delegate the triggers work to a regular class (often called Trigger handler class). 93 | 94 | See more here: https://developer.salesforce.com/page/Trigger_Frameworks_and_Apex_Trigger_Best_Practices 95 | 96 | 3 97 | 98 | 112 | 113 | 114 | 115 | 120 | 121 | Global classes should be avoided (especially in managed packages) as they can never be deleted or changed in signature. Always check twice if something needs to be global. 122 | Many interfaces (e.g. Batch) required global modifiers in the past but don't require this anymore. Don't lock yourself in. 123 | 124 | 3 125 | 126 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryDateLiteral.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description TODO 11 | * 12 | */ 13 | public without sharing class QueryDateLiteral extends NebulaCore { 14 | 15 | private String value; 16 | 17 | private QueryDateLiteral() { 18 | this.currentModule = NebulaCore.Module.QUERY_BUILDER; 19 | } 20 | 21 | public String getValue() { 22 | return this.value; 23 | } 24 | 25 | private QueryDateLiteral(String value) { 26 | this.value = value; 27 | } 28 | 29 | // Actual constant literals 30 | public static final QueryDateLiteral YESTERDAY = new QueryDateLiteral('YESTERDAY'); 31 | public static final QueryDateLiteral TODAY = new QueryDateLiteral('TODAY'); 32 | public static final QueryDateLiteral TOMORROW = new QueryDateLiteral('TOMORROW'); 33 | public static final QueryDateLiteral LAST_WEEK = new QueryDateLiteral('LAST_WEEK'); 34 | public static final QueryDateLiteral THIS_WEEK = new QueryDateLiteral('THIS_WEEK'); 35 | public static final QueryDateLiteral NEXT_WEEK = new QueryDateLiteral('NEXT_WEEK'); 36 | public static final QueryDateLiteral LAST_MONTH = new QueryDateLiteral('LAST_MONTH'); 37 | public static final QueryDateLiteral THIS_MONTH = new QueryDateLiteral('THIS_MONTH'); 38 | public static final QueryDateLiteral NEXT_MONTH = new QueryDateLiteral('NEXT_MONTH'); 39 | public static final QueryDateLiteral LAST_90_DAYS = new QueryDateLiteral('LAST_90_DAYS'); 40 | public static final QueryDateLiteral NEXT_90_DAYS = new QueryDateLiteral('NEXT_90_DAYS'); 41 | public static final QueryDateLiteral LAST_QUARTER = new QueryDateLiteral('LAST_QUARTER'); 42 | public static final QueryDateLiteral THIS_QUARTER = new QueryDateLiteral('THIS_QUARTER'); 43 | public static final QueryDateLiteral NEXT_QUARTER = new QueryDateLiteral('NEXT_QUARTER'); 44 | public static final QueryDateLiteral LAST_FISCAL_QUARTER = new QueryDateLiteral('LAST_FISCAL_QUARTER'); 45 | public static final QueryDateLiteral THIS_FISCAL_QUARTER = new QueryDateLiteral('THIS_FISCAL_QUARTER'); 46 | public static final QueryDateLiteral NEXT_FISCAL_QUARTER = new QueryDateLiteral('NEXT_FISCAL_QUARTER'); 47 | public static final QueryDateLiteral LAST_YEAR = new QueryDateLiteral('LAST_YEAR'); 48 | public static final QueryDateLiteral THIS_YEAR = new QueryDateLiteral('THIS_YEAR'); 49 | public static final QueryDateLiteral NEXT_YEAR = new QueryDateLiteral('NEXT_YEAR'); 50 | public static final QueryDateLiteral LAST_FISCAL_YEAR = new QueryDateLiteral('LAST_FISCAL_YEAR'); 51 | public static final QueryDateLiteral THIS_FISCAL_YEAR = new QueryDateLiteral('THIS_FISCAL_YEAR'); 52 | public static final QueryDateLiteral NEXT_FISCAL_YEAR = new QueryDateLiteral('NEXT_FISCAL_YEAR'); 53 | 54 | private static final String LAST_N = 'LAST_N_{0}:{1}'; 55 | private static final String NEXT_N = 'NEXT_N_{0}:{1}'; 56 | private static final String N_DAYS_AGO = 'N_DAYS_AGO:{0}'; 57 | 58 | private static final String DAYS = 'DAYS'; 59 | private static final String WEEKS = 'WEEKS'; 60 | private static final String MONTHS = 'MONTHS'; 61 | private static final String QUARTERS = 'QUARTERS'; 62 | private static final String YEARS = 'YEARS'; 63 | private static final String FISCAL_QUARTERS = 'FISCAL_QUARTERS'; 64 | private static final String FISCAL_YEARS = 'FISCAL_YEARS'; 65 | 66 | // Buildable literals 67 | public static QueryDateLiteral N_DAYS_AGO(Integer num) { 68 | String parsedValue = String.format(N_DAYS_AGO, new List{String.valueOf(num)}); 69 | return new QueryDateLiteral(parsedValue); 70 | } 71 | 72 | public static QueryDateLiteral LAST_N_DAYS(Integer num) { 73 | return buildQueryDateLiteral(LAST_N, DAYS, num); 74 | } 75 | 76 | public static QueryDateLiteral LAST_N_WEEKS(Integer num) { 77 | return buildQueryDateLiteral(LAST_N, WEEKS, num); 78 | } 79 | 80 | public static QueryDateLiteral LAST_N_MONTHS(Integer num) { 81 | return buildQueryDateLiteral(LAST_N, MONTHS, num); 82 | } 83 | 84 | public static QueryDateLiteral LAST_N_QUARTERS(Integer num) { 85 | return buildQueryDateLiteral(LAST_N, QUARTERS, num); 86 | } 87 | 88 | public static QueryDateLiteral LAST_N_YEARS(Integer num) { 89 | return buildQueryDateLiteral(LAST_N, YEARS, num); 90 | } 91 | 92 | public static QueryDateLiteral LAST_N_FISCAL_YEARS(Integer num) { 93 | return buildQueryDateLiteral(LAST_N, FISCAL_YEARS, num); 94 | } 95 | 96 | public static QueryDateLiteral NEXT_N_DAYS(Integer num) { 97 | return buildQueryDateLiteral(NEXT_N, DAYS, num); 98 | } 99 | 100 | public static QueryDateLiteral NEXT_N_WEEKS(Integer num) { 101 | return buildQueryDateLiteral(NEXT_N, WEEKS, num); 102 | } 103 | 104 | public static QueryDateLiteral NEXT_N_MONTHS(Integer num) { 105 | return buildQueryDateLiteral(NEXT_N, MONTHS, num); 106 | } 107 | 108 | public static QueryDateLiteral NEXT_N_QUARTERS(Integer num) { 109 | return buildQueryDateLiteral(NEXT_N, QUARTERS, num); 110 | } 111 | 112 | public static QueryDateLiteral NEXT_N_FISCAL_QUARTERS(Integer num) { 113 | return buildQueryDateLiteral(NEXT_N, FISCAL_QUARTERS, num); 114 | } 115 | 116 | public static QueryDateLiteral NEXT_N_YEARS(Integer num) { 117 | return buildQueryDateLiteral(NEXT_N, YEARS, num); 118 | } 119 | 120 | public static QueryDateLiteral NEXT_N_FISCAL_YEARS(Integer num) { 121 | return buildQueryDateLiteral(NEXT_N, FISCAL_YEARS, num); 122 | } 123 | 124 | private static QueryDateLiteral buildQueryDateLiteral(String base, String period, Integer num) { 125 | String parsedValue = String.format(base, new List{period, String.valueOf(num)}); 126 | return new QueryDateLiteral(parsedValue); 127 | } 128 | 129 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryBuilder.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description Abstract class that provides some shared properties & methods for SObjectQueryBuilder & AggregateResultQueryBuilder 11 | * 12 | */ 13 | public abstract class QueryBuilder extends NebulaCore { 14 | 15 | private static Map> cachedQueryResultsByHashCode = new Map>(); 16 | private static Map>> cachedSearchResultsByHashCode = new Map>>(); 17 | 18 | protected List whereClauseList; 19 | protected List orderByList; 20 | protected Integer limitCount; 21 | 22 | protected SObjectType sobjectType; 23 | protected Map sobjectTypeFieldMap; 24 | 25 | private Boolean cacheResults; 26 | 27 | public QueryBuilder() { 28 | this.currentModule = NebulaCore.Module.QUERY_BUILDER; 29 | 30 | this.whereClauseList = new List(); 31 | this.orderByList = new List(); 32 | this.cacheResults = false; 33 | } 34 | 35 | protected void doCacheResults() { 36 | this.cacheResults = true; 37 | } 38 | 39 | protected void doFilterBy(IQueryFilter queryFilter) { 40 | this.doFilterBy(new List{queryFilter}); 41 | } 42 | 43 | protected void doFilterBy(List queryFilters) { 44 | if(queryFilters == null) return; 45 | 46 | for(IQueryFilter queryFilter : queryFilters) this.whereClauseList.add(queryFilter.getValue()); 47 | } 48 | 49 | protected void doOrderBy(IQueryField orderByQueryField) { 50 | this.doOrderBy(orderByQueryField, null, null); 51 | } 52 | 53 | protected void doOrderBy(IQueryField orderByQueryField, QuerySortOrder sortOrder) { 54 | this.doOrderBy(orderByQueryField, sortOrder, null); 55 | } 56 | 57 | protected void doOrderBy(IQueryField orderByQueryField, QuerySortOrder sortOrder, QueryNullSortOrder nullsSortOrder) { 58 | String sortOrderString = ''; 59 | if(sortOrder == QuerySortOrder.ASCENDING) sortOrderString = ' ASC'; 60 | else if(sortOrder == QuerySortOrder.DESCENDING) sortOrderString = ' DESC'; 61 | 62 | if(nullsSortOrder != null) sortOrderString += ' NULLS ' + nullsSortOrder; 63 | 64 | this.orderByList.add(orderByQueryField.getValue() + sortOrderString); 65 | } 66 | 67 | protected void doLimitCount(Integer limitCount) { 68 | this.limitCount = limitCount; 69 | } 70 | 71 | protected String doGetWhereClauseString() { 72 | if(this.whereClauseList.isEmpty()) return ''; 73 | 74 | // Dedupe 75 | this.whereClauseList = new List(new Set(this.whereClauseList)); 76 | 77 | this.whereClauseList.sort(); 78 | return '\nWHERE ' + String.join(this.whereClauseList, '\nAND '); 79 | } 80 | 81 | protected String doGetOrderByString() { 82 | if(this.orderByList.isEmpty()) return ''; 83 | 84 | // Dedupe 85 | this.orderByList = new List(new Set(this.orderByList)); 86 | 87 | this.orderByList.sort(); 88 | return '\nORDER BY ' + String.join(new List(orderByList), ', '); 89 | } 90 | 91 | protected String doGetLimitCountString() { 92 | return this.limitCount != null ? '\nLIMIT '+ this.limitCount : ''; 93 | } 94 | 95 | protected List doGetQueryResults(String query) { 96 | if(this.cacheResults) return this.getCachedQuery(query); 97 | else return this.executeQuery(query); 98 | } 99 | 100 | protected List> doGetSearchResults(String query) { 101 | if(this.cacheResults) return this.getCachedSearch(query); 102 | else return this.executeSearch(query); 103 | } 104 | 105 | private void filterByWithSeparator(List queryFilters, String separator) { 106 | if(queryFilters == null) return; 107 | 108 | List queryFilterValues = new List(); 109 | for(IQueryFilter queryFilter : queryFilters) queryFilterValues.add(queryFilter.getValue()); 110 | 111 | String orStatement = '(' + String.join(queryFilterValues, ' ' + separator + ' ') + ')'; 112 | this.whereClauseList.add(orStatement); 113 | } 114 | 115 | private List getCachedQuery(String query) { 116 | Integer hashCode = query.hashCode(); 117 | 118 | Boolean isCached = cachedQueryResultsByHashCode.containsKey(hashCode); 119 | if(!isCached) cachedQueryResultsByHashCode.put(hashCode, this.executeQuery(query)); 120 | 121 | // Always return a deep clone so the original cached version is never modified 122 | return cachedQueryResultsByHashCode.get(hashCode).deepClone(true, true, true); 123 | } 124 | 125 | private List executeQuery(String query) { 126 | List results = Database.query(query); 127 | this.logResults(query, results); 128 | return results; 129 | } 130 | 131 | private List> getCachedSearch(String query) { 132 | Integer hashCode = query.hashCode(); 133 | 134 | Boolean isCached = cachedSearchResultsByHashCode.containsKey(hashCode); 135 | if(!isCached) cachedSearchResultsByHashCode.put(hashCode, this.executeSearch(query)); 136 | 137 | // Always return a deep clone so the original cached version is never modified 138 | List> cachedResults = cachedSearchResultsByHashCode.get(hashCode); 139 | List> deepClonedResults = new List>(); 140 | for(List cachedListOfResults : cachedResults) deepClonedResults.add(cachedListOfResults.deepClone(true, true, true)); 141 | return deepClonedResults; 142 | } 143 | 144 | private List> executeSearch(String query) { 145 | List> results = Search.query(query); 146 | this.logResults(query, results); 147 | return results; 148 | } 149 | 150 | private void logResults(String query, List results) { 151 | String logEntry = 'Query:\n' + query + '\n\nResults:\n' + JSON.serializePretty(results); 152 | Logger.addEntry(this, logEntry); 153 | } 154 | 155 | } -------------------------------------------------------------------------------- /nebula-app-framework/main/query-and-search/classes/QueryFilter.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | 6 | /** 7 | * 8 | * @group Query Builder 9 | * 10 | * @description Handles generating any query conditions (the WHERE statement in a query) 11 | * Each part of a WHERE statement is a separate instance of query filter. 12 | * 13 | */ 14 | public class QueryFilter extends NebulaCore implements IQueryFilter, Comparable { 15 | 16 | private String value; 17 | 18 | public QueryFilter() { 19 | this.currentModule = NebulaCore.Module.QUERY_BUILDER; 20 | } 21 | 22 | /** 23 | * @description Creates a filter for a field on a parent sobject 24 | * @param queryField An instance of QueryField, containg the field or chain of fields that should be filtered 25 | * @param operator The instance of QueryOperator to use in the filter the list to check 26 | * @param providedValue The value to compare to in the filter 27 | * @return The instance of IQueryFilter, to allow chaining methods 28 | * @example 29 | * List parentFieldChain = new List{Schema.Lead.CreatedById, Schema.User.Email}; 30 | * QueryFilter filter = new QueryFilter().setValue(parentFieldChain, QueryOperator.NOT_EQUAL_TO, null); 31 | * System.assertEquals('CreatedBy.Email != null', filter.getValue()); 32 | */ 33 | public IQueryFilter filterByField(QueryField queryField, QueryOperator operator, Object providedValue) { 34 | this.value = queryField.getValue() 35 | + ' ' + operator.getValue() 36 | + ' ' + new QueryArgumentFormatter(providedValue).getValue(); 37 | 38 | return this; 39 | } 40 | 41 | /** 42 | * @description Creates a filter for a date function 43 | * @param queryDateToFilter An instance of QueryDate, created by supplying a date or datetime field to filter on 44 | * @param operator The instance of QueryOperator to use in the filter the list to check 45 | * @param providedValue The value to compare to in the filter 46 | * @return The instance of IQueryFilter, to allow chaining methods 47 | * @example 48 | * QueryDate qd = QueryDate.CALENDAR_MONTH(Schema.Lead.CreatedDate); 49 | * QueryFilter filter = new QueryFilter().setValue(qd, QueryOperator.EQUALS, 2); 50 | * System.assertEquals('CALENDAR_MONTH(CreatedDate) = 2', filter.getValue()); 51 | */ 52 | public IQueryFilter filterByQueryDate(QueryDate queryDateToFilter, QueryOperator operator, Integer providedValue) { 53 | this.value = queryDateToFilter.getValue() 54 | + ' ' + operator.getValue() 55 | + ' ' + providedValue; 56 | 57 | return this; 58 | } 59 | 60 | /** 61 | * @description Creates a filter for a subquery on the sobject's ID 62 | * @param inOrNotIn An instance of QueryOperator - it must be QueryOperator.IS_IN or QueryOperator.IS_NOT_IN 63 | * @param lookupFieldOnRelatedSObject The lookup field on a related object that contains the ID of the current sobject 64 | * @return The instance of IQueryFilter, to allow chaining methods 65 | * @example 66 | * QueryFilter filter = new QueryFilter().setValue(QueryOperator.IS_IN, Schema.Lead.ConvertedAccountId); 67 | * System.assertEquals('Id IN (SELECT ConvertedAccountId FROM Lead)', filter.getValue()); 68 | */ 69 | // TODO figure out a better solution for inOrNotIn 70 | public IQueryFilter filterBySubquery(QueryOperator inOrNotIn, Schema.SObjectField lookupFieldOnRelatedSObject) { 71 | return this.setValueForSubquery('Id', inOrNotIn, lookupFieldOnRelatedSObject); 72 | } 73 | 74 | /** 75 | * @description Creates a filter for a subquery on an ID field for the current sobject 76 | * @param lookupField The lookup field on the current sobject that contains an ID 77 | * @param inOrNotIn An instance of QueryOperator - it must be QueryOperator.IS_IN or QueryOperator.IS_NOT_IN 78 | * @param lookupFieldOnRelatedSObject The lookup field on a related object that contains the value of the lookupField paraemter 79 | * @return The instance of IQueryFilter, to allow chaining methods 80 | * @example 81 | * QueryFilter filter = new QueryFilter().setValue(Schema.Lead.OwnerId, QueryOperator.IS_IN, Schema.User.Id); 82 | * System.assertEquals('OwnerId IN (SELECT Id FROM User)', filter.getValue()); 83 | */ 84 | public IQueryFilter filterBySubquery(Schema.SObjectField lookupField, QueryOperator inOrNotIn, Schema.SObjectField lookupFieldOnRelatedSObject) { 85 | return this.setValueForSubquery(lookupField.getDescribe().getName(), inOrNotIn, lookupFieldOnRelatedSObject); 86 | } 87 | 88 | /** 89 | * @description Adds several filters together as a set of 'AND' filters 90 | * @param queryFilters The filters to group together 91 | * @return The instance of IQueryFilter, to allow chaining methods 92 | */ 93 | public IQueryFilter andFilterBy(List queryFilters) { 94 | return this.filterByWithSeparator(queryFilters, 'AND'); 95 | } 96 | 97 | /** 98 | * @description Adds several filters together as a set of 'OR' filters 99 | * @param queryFilters The filters to group together 100 | * @return The instance of IQueryFilter, to allow chaining methods 101 | */ 102 | public IQueryFilter orFilterBy(List queryFilters) { 103 | return this.filterByWithSeparator(queryFilters, 'OR'); 104 | } 105 | 106 | /** 107 | * @description Returns the calculated value, based on the method & parameters provided 108 | * @return The string of the query filter, ready to be used in dynamic SOQL/SOSL 109 | */ 110 | public String getValue() { 111 | return this.value; 112 | } 113 | 114 | public Integer compareTo(Object compareTo) { 115 | QueryFilter compareToQueryFilter = (QueryFilter)compareTo; 116 | if(this.getValue() == compareToQueryFilter.getValue()) return 0; 117 | if(this.getValue() > compareToQueryFilter.getValue()) return 1; 118 | return -1; 119 | } 120 | 121 | private IQueryFilter setValueForSubquery(String idFieldName, QueryOperator inOrNotIn, Schema.SObjectField lookupFieldOnRelatedSObject) { 122 | String relatedSObjectTypeName = new SObjectFieldDescriber(lookupFieldOnRelatedSObject).getSObjectType().getDescribe().getName(); 123 | String lookupFieldOnRelatedSObjectName = lookupFieldOnRelatedSObject.getDescribe().getName(); 124 | 125 | this.value = idFieldName + ' ' + inOrNotIn.getValue() + ' (SELECT ' + lookupFieldOnRelatedSObjectName + ' FROM ' + relatedSObjectTypeName + ')'; 126 | 127 | return this; 128 | } 129 | 130 | private IQueryFilter filterByWithSeparator(List queryFilters, String separator) { 131 | List queryFilterValues = new List(); 132 | for(IQueryFilter queryFilter : queryFilters) queryFilterValues.add(queryFilter.getValue()); 133 | 134 | this.value = '(' + String.join(queryFilterValues, ' ' + separator + ' ') + ')'; 135 | return this; 136 | } 137 | 138 | } -------------------------------------------------------------------------------- /nebula-app-framework/tests/query-and-search/classes/QueryArgumentFormatter_Tests.cls: -------------------------------------------------------------------------------- 1 | /************************************************************************************************* 2 | * This file is part of the Nebula Framework project, released under the MIT License. * 3 | * See LICENSE file or go to https://github.com/jongpie/NebulaFramework for full license details. * 4 | *************************************************************************************************/ 5 | @isTest 6 | private class QueryArgumentFormatter_Tests { 7 | 8 | @isTest 9 | static void it_should_return_query_string_for_null() { 10 | Object providedValue = null; 11 | String expectedString = null; 12 | 13 | Test.startTest(); 14 | String returnedValue = new QueryArgumentFormatter(providedValue).getValue(); 15 | Test.stopTest(); 16 | 17 | System.assertEquals(expectedString, returnedValue); 18 | } 19 | 20 | @isTest 21 | static void it_should_return_query_string_for_list() { 22 | List providedValueList = new List{1, 2, 3}; 23 | String expectedString = '(1, 2, 3)'; 24 | 25 | Test.startTest(); 26 | String returnedValue = new QueryArgumentFormatter(providedValueList).getValue(); 27 | Test.stopTest(); 28 | 29 | System.assertEquals(expectedString, returnedValue); 30 | } 31 | 32 | @isTest 33 | static void it_should_return_query_string_for_set() { 34 | Set providedValueSet = new Set{'A', 'B', 'C'}; 35 | String expectedString = '(\'A\', \'B\', \'C\')'; 36 | 37 | Test.startTest(); 38 | String returnedValue = new QueryArgumentFormatter(providedValueSet).getValue(); 39 | Test.stopTest(); 40 | 41 | System.assertEquals(expectedString, returnedValue); 42 | } 43 | 44 | @isTest 45 | static void it_should_return_query_string_for_map() { 46 | Map providedValueMap = new Map([SELECT Id FROM User LIMIT 10]); 47 | List sortedIdList = new List(providedValueMap.keySet()); 48 | sortedIdList.sort(); 49 | String expectedString = '(\'' + String.join(sortedIdList, '\', \'') + '\')'; 50 | 51 | Test.startTest(); 52 | String returnedValue = new QueryArgumentFormatter(providedValueMap).getValue(); 53 | Test.stopTest(); 54 | 55 | System.assertEquals(expectedString, returnedValue); 56 | } 57 | 58 | @isTest 59 | static void it_should_return_query_string_for_query_date_literal() { 60 | QueryDateLiteral providedValue = QueryDateLiteral.YESTERDAY; 61 | String expectedString = providedValue.getValue(); 62 | 63 | Test.startTest(); 64 | String returnedValue = new QueryArgumentFormatter(providedValue).getValue(); 65 | Test.stopTest(); 66 | 67 | System.assertEquals(expectedString, returnedValue); 68 | } 69 | 70 | @isTest 71 | static void it_should_return_query_string_for_boolean() { 72 | Boolean providedValue = true; 73 | String expectedString = String.valueOf(providedValue); 74 | 75 | Test.startTest(); 76 | String returnedValue = new QueryArgumentFormatter(providedValue).getValue(); 77 | Test.stopTest(); 78 | 79 | System.assertEquals(expectedString, returnedValue); 80 | } 81 | 82 | @isTest 83 | static void it_should_return_query_string_for_date() { 84 | Date providedValue = System.today(); 85 | String expectedString = String.valueOf(providedValue); 86 | 87 | Test.startTest(); 88 | String returnedValue = new QueryArgumentFormatter(providedValue).getValue(); 89 | Test.stopTest(); 90 | 91 | System.assertEquals(expectedString, returnedValue); 92 | } 93 | 94 | @isTest 95 | static void it_should_return_query_string_for_datetime() { 96 | Datetime providedValue = System.now(); 97 | String expectedString = providedValue.format('yyyy-MM-dd\'T\'HH:mm:ss\'Z\'', 'Greenwich Mean Time'); 98 | 99 | Test.startTest(); 100 | String returnedValue = new QueryArgumentFormatter(providedValue).getValue(); 101 | Test.stopTest(); 102 | 103 | System.assertEquals(expectedString, returnedValue); 104 | } 105 | 106 | @isTest 107 | static void it_should_return_query_string_for_decimal() { 108 | Decimal providedValue = 1.1; 109 | String expectedString = String.valueOf(providedValue); 110 | 111 | Test.startTest(); 112 | String returnedValue = new QueryArgumentFormatter(providedValue).getValue(); 113 | Test.stopTest(); 114 | 115 | System.assertEquals(expectedString, returnedValue); 116 | } 117 | 118 | @isTest 119 | static void it_should_return_query_string_for_double() { 120 | Double providedValue = 1.1; 121 | String expectedString = String.valueOf(providedValue); 122 | 123 | Test.startTest(); 124 | String returnedValue = new QueryArgumentFormatter(providedValue).getValue(); 125 | Test.stopTest(); 126 | 127 | System.assertEquals(expectedString, returnedValue); 128 | } 129 | 130 | @isTest 131 | static void it_should_return_query_string_for_integer() { 132 | Integer providedValue = 10; 133 | String expectedString = String.valueOf(providedValue); 134 | 135 | Test.startTest(); 136 | String returnedValue = new QueryArgumentFormatter(providedValue).getValue(); 137 | Test.stopTest(); 138 | 139 | System.assertEquals(expectedString, returnedValue); 140 | } 141 | 142 | @isTest 143 | static void it_should_return_query_string_for_long() { 144 | Long providedValue = 1234567890; 145 | String expectedString = String.valueOf(providedValue); 146 | 147 | Test.startTest(); 148 | String returnedValue = new QueryArgumentFormatter(providedValue).getValue(); 149 | Test.stopTest(); 150 | 151 | System.assertEquals(expectedString, returnedValue); 152 | } 153 | 154 | @isTest 155 | static void it_should_return_query_string_for_sobject() { 156 | Lead providedValue = new Lead( 157 | Company = 'My Test Company', 158 | LastName = 'Gillespie' 159 | ); 160 | insert providedValue; 161 | String expectedString = '\'' + providedValue.Id + '\''; 162 | 163 | Test.startTest(); 164 | String returnedValue = new QueryArgumentFormatter(providedValue).getValue(); 165 | Test.stopTest(); 166 | 167 | System.assertEquals(expectedString, returnedValue); 168 | } 169 | 170 | @isTest 171 | static void it_should_return_query_string_for_sobject_type() { 172 | Schema.SObjectType providedValue = Schema.Lead.SObjectType; 173 | String expectedString = '\'' + providedValue.getDescribe().getName() + '\''; 174 | 175 | Test.startTest(); 176 | String returnedValue = new QueryArgumentFormatter(providedValue).getValue(); 177 | Test.stopTest(); 178 | 179 | System.assertEquals(expectedString, returnedValue); 180 | } 181 | 182 | @isTest 183 | static void it_should_return_query_string_for_string() { 184 | String providedValue = 'test'; 185 | String expectedString = '\'' + providedValue + '\''; 186 | 187 | Test.startTest(); 188 | String returnedValue = new QueryArgumentFormatter(providedValue).getValue(); 189 | Test.stopTest(); 190 | 191 | System.assertEquals(expectedString, returnedValue); 192 | } 193 | 194 | @isTest 195 | static void it_should_return_query_string_for_string_containing_single_quotes() { 196 | String providedValue = 'Jon\'s test'; 197 | String expectedString = '\'' + String.escapeSingleQuotes(providedValue) + '\''; 198 | 199 | Test.startTest(); 200 | String returnedValue = new QueryArgumentFormatter(providedValue).getValue(); 201 | Test.stopTest(); 202 | 203 | System.assertEquals(expectedString, returnedValue); 204 | } 205 | 206 | @isTest 207 | static void it_should_return_query_string_for_unsupported_type() { 208 | UUID providedValue = new UUID(); 209 | String expectedString = String.valueOf(providedValue); 210 | 211 | Test.startTest(); 212 | String returnedValue = new QueryArgumentFormatter(providedValue).getValue(); 213 | Test.stopTest(); 214 | 215 | System.assertEquals(expectedString, returnedValue); 216 | } 217 | 218 | } --------------------------------------------------------------------------------