選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

224 行
9.9 KiB

  1. /**
  2. * @description : Invocable for doing large batch DML from Flow
  3. * @author : Matt Comer, Kyle Kim, Ward Wood, Adam White
  4. * @group :
  5. * @last modified on :
  6. * @last modified by :
  7. * Modifications Log
  8. * Ver Date Author Modification
  9. * 1.0 08-06-2020 Matt Comer, Kyle Kim, Ward Wood Initial Version
  10. * 1.1 08-20-2020 Adam White Added email inputs / send on complete
  11. **/
  12. global with sharing class MassTransactorAction {
  13. public static Integer MAX_BATCH_SIZE = 2000;
  14. // If no Batch size is specified from the flow - batch size defaults to DEFAULT_BATCH_SIZE
  15. public static Integer DEFAULT_BATCH_SIZE = 200;
  16. //Main Method to acting as an orchestration layer.
  17. @InvocableMethod (label='DML Records Asynchronously')
  18. public static List<InvocableApexAsyncBatchResponse> InvocableApexCreateAsyncController(List<InvocableApexAsyncBatchRequest> RequestList){
  19. // NOTE that this action is currently not bulkified due to the nature of what it does.
  20. // Creating and submitting multiple independent batch jobs would not properly scale.
  21. InvocableApexAsyncBatchRequest request = RequestList[0];
  22. ValidationObject vo = ValidateRequestObject(request);
  23. InvocableApexAsyncBatchResponse response = new InvocableApexAsyncBatchResponse();
  24. List<InvocableApexAsyncBatchResponse> result = new List<InvocableApexAsyncBatchResponse>();
  25. result.add(response);
  26. if (vo.Validity) {
  27. Integer batchSize = request.BatchSizeIsNull() ? DEFAULT_BATCH_SIZE : request.BatchSize;
  28. MassTransactorBatchHelper bh;
  29. if (request.EmailToAddress != null) {
  30. SimpleEmailOnFinishAction fa = new SimpleEmailOnFinishAction(request.EmailToAddress,
  31. request.EmailSubject,
  32. request.EmailBody);
  33. bh = new MassTransactorBatchHelper(request.RequestList,
  34. request.OperationType,
  35. fa);
  36. } else {
  37. bh = new MassTransactorBatchHelper(request.RequestList,
  38. request.OperationType);
  39. }
  40. response.jobId = Database.executeBatch(bh, batchSize);
  41. response.success = true;
  42. } else {
  43. response.success = false;
  44. response.errorMessage = vo.ResponseMessage;
  45. }
  46. return result;
  47. }
  48. //Method & Main controller for Error handling. utlizes two different methods.
  49. public static ValidationObject ValidateRequestObject(InvocableApexAsyncBatchRequest RequestPayload){
  50. ValidationObject MasterValidationObject = new ValidationObject();
  51. ValidatePayloadSize(RequestPayload, MasterValidationObject);
  52. if (MasterValidationObject.Validity == false){
  53. return MasterValidationObject;
  54. }
  55. else{
  56. //run another check on items if math is good then we can return it and let the controller do it's magic
  57. ValidatePayloadItems(RequestPayload, MasterValidationObject);
  58. return MasterValidationObject;
  59. }
  60. }
  61. //Method scans the payload and ensure that the items in the batch are okay
  62. //We need to ensure Deletes are parsed as [{ids}]
  63. //Everything else [{any fields in there. map it correctly when assigning it though..}]
  64. public static void ValidatePayloadItems (InvocableApexAsyncBatchRequest RequestPayload, ValidationObject MasterValidationObject){
  65. //Variables to establish Constants ()
  66. Schema.sObjectType ObjectType = RequestPayload.RequestList.get(0).getSObjectType();
  67. //Parse out all IDs into a set(de-dup if someone did something silly).
  68. Set <Id> IDSet = new Set <ID>();
  69. for(sObject cursor : RequestPayload.RequestList){
  70. if(cursor.Id != null || cursor.Id != ''){
  71. IDSet.add(cursor.Id);
  72. }
  73. }
  74. String queryString = 'Select ID from ' + String.valueOf(ObjectType) + ' where ID in :IDSet';
  75. List <sObject> ValidationList = Database.query(queryString);
  76. //based on the operation type
  77. switch on RequestPayload.OperationType.toUpperCase(){
  78. //Make sure IDs do not exist.
  79. when 'CREATE'{
  80. if(ValidationList.size() > 0) {
  81. MasterValidationObject.Validity = false;
  82. MasterValidationObject.ResponseMessage = 'There are IDs that exis. Cannot create records with existing Ids.';
  83. MasterValidationObject.ResponseCode = 403;
  84. }
  85. }
  86. //update and delete, make sure all Ids Exist.
  87. when else {
  88. if(ValidationList.size() != RequestPayload.RequestList.size()){
  89. MasterValidationObject.Validity = false;
  90. MasterValidationObject.ResponseMessage = 'There are records in this collection with IDs that do not exist. Cannot execute delete or updates on Ids that do not exist';
  91. MasterValidationObject.ResponseCode = 405;
  92. }
  93. }
  94. }
  95. if(MasterValidationObject.Validity != false){
  96. MasterValidationObject.Validity = true;
  97. MasterValidationObject.ResponseMessage = 'Valid Payload';
  98. MasterValidationObject.ResponseCode = 200;
  99. }
  100. }
  101. // Method scans the payload, and ensures that the batch size is okay
  102. public static void ValidatePayloadSize (InvocableApexAsyncBatchRequest RequestPayload, ValidationObject MasterValidationObject){
  103. if(RequestPayload.RequestList.size() == 0 || RequestPayload.RequestList == null) {
  104. MasterValidationObject.Validity = false;
  105. MasterValidationObject.ResponseMessage = 'There are no records to process.';
  106. MasterValidationObject.ResponseCode = 66;
  107. }
  108. else {
  109. //If there is no batchsize set it to the max
  110. Integer BatchSize = RequestPayload.BatchSize = RequestPayload.BatchSizeIsNull() ? DEFAULT_BATCH_SIZE : RequestPayload.BatchSize;
  111. //check if batch size reaches limit || Check if too many batches || Check if no batches...
  112. if(BatchSize > MAX_BATCH_SIZE || BatchSize <= 0){
  113. MasterValidationObject.Validity = false;
  114. MasterValidationObject.ResponseCode = 401;
  115. MasterValidationObject.ResponseMessage = 'Batch size needs to be between 0 and 2000';
  116. }
  117. else {
  118. MasterValidationObject.Validity = true;
  119. MasterValidationObject.ResponseCode = 200;
  120. MasterValidationObject.ResponseMessage = 'Success';
  121. }
  122. }
  123. }
  124. //Request and Response Objects.
  125. public class InvocableApexAsyncBatchRequest{
  126. @InvocableVariable(label='Operation Type' description='CUD operation Create Delete Update' required=true)
  127. public String OperationType;
  128. @InvocableVariable(label='Records for Input' description='Flexible List to pass objects' required=true)
  129. public List<sObject> RequestList;
  130. @InvocableVariable(label='Batch Size' description='Size of Batch for each job' required=false)
  131. public Integer BatchSize;
  132. @InvocableVariable(label='Finish Notification Email Address' description='A single email address to send the finish email to')
  133. public String EmailToAddress;
  134. @InvocableVariable(label='Body of the email' description='What the email will contain upon completion')
  135. public String EmailBody;
  136. @InvocableVariable(label='Subject of the email' description='Subject of the email upon completion')
  137. public String EmailSubject;
  138. //Boolean to check if this is null or empty.
  139. public Boolean BatchSizeIsNull(){
  140. if(this.BatchSize == null || this.BatchSize == 0){
  141. return true;
  142. }
  143. else{
  144. return false;
  145. }
  146. }
  147. }
  148. //Response object - not sure if we need this
  149. public class InvocableApexAsyncBatchResponse {
  150. @InvocableVariable(label='Submission Results' description='True if the batch was succesfully submitted, false if not')
  151. public Boolean success;
  152. @InvocableVariable(label='JobId' description='The jobId of the batch processing the DML')
  153. public String jobId;
  154. @InvocableVariable(label='Error message' description='If success is false, the validation error which occured')
  155. public String errorMessage;
  156. }
  157. public class ValidationObject {
  158. public boolean Validity;
  159. public String ResponseMessage;
  160. public Integer ResponseCode;
  161. }
  162. public class SimpleEmailOnFinishAction implements MassTransactorBatchHelper.OnFinishAction {
  163. private String emailTo;
  164. private String subject;
  165. private String body;
  166. public SimpleEmailOnFinishAction(String emailTo, String subject, String body) {
  167. this.emailTo = emailTo;
  168. this.subject = subject;
  169. this.body = body;
  170. }
  171. public void onFinish(Id finishedJobId) {
  172. If(this.emailTo != null && this.subject != null && this.body != null) {
  173. Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
  174. String[] toAddresses = new List<String>();
  175. toAddresses.add(this.emailTo);
  176. mail.setToAddresses(toAddresses);
  177. mail.setSubject(this.subject);
  178. mail.setPlainTextBody(this.body);
  179. Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
  180. }
  181. }
  182. }
  183. }