4
4
// ----------------------------------------------------------------------------------------------------//
5
5
6
6
public inherited sharing class CustomMetadataSaver {
7
+ private static final Boolean DEFAULT_SEND_EMAIL_ON_ERROR = false ;
8
+ private static final Boolean DEFAULT_SEND_EMAIL_ON_SUCCESS = false ;
7
9
private static final List <String > DEPLOYMENT_JOB_IDS = new List <String >();
8
10
private static final Set <String > IGNORED_FIELD_NAMES = getIgnoredFieldNames ();
9
11
10
12
public class FlowInput {
11
- @InvocableVariable (required = true label = ' The collection of custom metadata type records to deploy ' )
13
+ @InvocableVariable (required = true label = ' Custom Metadata Type Records to Deploy ' )
12
14
public List <SObject > customMetadataRecords ;
15
+
16
+ @InvocableVariable (
17
+ required = true
18
+ label = ' Send Email Alert if the Deployment Fails'
19
+ description = ' This option is only used by the default callback Apex class.'
20
+ )
21
+ public Boolean sendEmailOnError ;
22
+
23
+ @InvocableVariable (
24
+ required = true
25
+ label = ' Send Email Alert if the Deployment Succeeds'
26
+ description = ' This option is only used by the default callback Apex class.'
27
+ )
28
+ public Boolean sendEmailOnSuccess ;
29
+
30
+ @InvocableVariable (
31
+ required = false
32
+ label = ' (Optional) Custom Callback Apex Class'
33
+ description = ' The name of your Apex class to execute after the deployment completes. When provided, this is used instead of the default callback.'
34
+ )
35
+ public String customCallbackName ;
13
36
}
14
37
15
38
private CustomMetadataSaver () {
@@ -18,27 +41,49 @@ public inherited sharing class CustomMetadataSaver {
18
41
19
42
@InvocableMethod (
20
43
category = ' Custom Metadata'
21
- label = ' Deploy Changes to Custom Metadata Records'
44
+ label = ' Deploy Custom Metadata Type Records'
22
45
description = ' Deploys changes to the list of custom metadata records'
23
46
)
24
- public static void deploy (List <FlowInput > inputs ) {
47
+ public static List < String > deploy (List <FlowInput > inputs ) {
25
48
System .debug (' inputs==' + inputs );
26
49
27
- List <SObject > consolidatedList = new List <SObject >();
50
+ Boolean sendEmailOnError = DEFAULT_SEND_EMAIL_ON_ERROR ;
51
+ Boolean sendEmailOnSuccess = DEFAULT_SEND_EMAIL_ON_SUCCESS ;
52
+ String customCallbackName ;
53
+
54
+ // Combine all CMDT records into 1 list so that there is only 1 deployment
55
+ // TODO allow this to be controlled via FlowInput - there are uses cases for also having multiple deployments
56
+ List <SObject > consolidatedCustomMetadataRecords = new List <SObject >();
28
57
for (FlowInput input : inputs ) {
29
- consolidatedList .addAll (input .customMetadataRecords );
58
+ consolidatedCustomMetadataRecords .addAll (input .customMetadataRecords );
59
+ // If any of the inputs confirm that an email should be sent, then send it - otherwise, no email will be sent
60
+ if (input .sendEmailOnError == true ) {
61
+ sendEmailOnError = input .sendEmailOnError ;
62
+ }
63
+ // If any of the inputs confirm that an email should be sent, then send it - otherwise, no email will be sent
64
+ if (input .sendEmailOnSuccess == true ) {
65
+ sendEmailOnSuccess = input .sendEmailOnSuccess ;
66
+ }
67
+ // Assumption: only a single custom callback will be specified
68
+ // If we find a custom callback class name, update the variable
69
+ if (String .isNotBlank (input .customCallbackName )) {
70
+ customCallbackName = input .customCallbackName ;
71
+ }
30
72
}
31
73
32
- System .debug (' consolidatedList==' + consolidatedList );
74
+ System .debug (' consolidatedCustomMetadataRecords==' + consolidatedCustomMetadataRecords );
75
+
76
+ Metadata .DeployCallback callback = getFlowDeployCallback (customCallbackName , sendEmailOnError , sendEmailOnSuccess );
77
+ System .debug (' callback==' + callback );
33
78
34
- deploy (consolidatedList , null ) ;
79
+ return new List < String >{ deploy (consolidatedCustomMetadataRecords , callback ) } ;
35
80
}
36
81
37
- public static void deploy (List <SObject > customMetadataRecords ) {
38
- deploy (customMetadataRecords , null );
82
+ public static String deploy (List <SObject > customMetadataRecords ) {
83
+ return deploy (customMetadataRecords , new DefaultDeployCallback () );
39
84
}
40
85
41
- public static void deploy (List <SObject > customMetadataRecords , Metadata.DeployCallback callback ) {
86
+ public static String deploy (List <SObject > customMetadataRecords , Metadata.DeployCallback callback ) {
42
87
Metadata .DeployContainer deployment = new Metadata .DeployContainer ();
43
88
44
89
for (SObject customMetadataRecord : customMetadataRecords ) {
@@ -49,6 +94,8 @@ public inherited sharing class CustomMetadataSaver {
49
94
String jobId = Test .isRunningTest () ? ' Fake Job ID' : Metadata .Operations .enqueueDeployment (deployment , callback );
50
95
DEPLOYMENT_JOB_IDS .add (jobId );
51
96
System .debug (LoggingLevel .INFO , ' Deployment Job ID: ' + jobId );
97
+
98
+ return jobId ;
52
99
}
53
100
54
101
public static List <String > getDeploymentJobIds () {
@@ -93,4 +140,81 @@ public inherited sharing class CustomMetadataSaver {
93
140
94
141
return customMetadata ;
95
142
}
143
+
144
+ private static Metadata.DeployCallback getFlowDeployCallback (String customCallbackName , Boolean sendEmailOnError , Boolean sendEmailOnSuccess ) {
145
+ Metadata .DeployCallback callback ;
146
+ if (String .isNotBlank (customCallbackName ) && Type .forName (customCallbackName ) != null ) {
147
+ // Dynamically create a new instance of the specified class
148
+ // Assumption: specified class uses a parameterless constructor
149
+ System .debug (' customCallbackName==' + customCallbackName );
150
+ callback = (Metadata .DeployCallback ) Type .forName (customCallbackName ).newInstance ();
151
+ } else {
152
+ // If no custom callback class is specified, use the default inner class
153
+ callback = new DefaultDeployCallback (sendEmailOnError , sendEmailOnSuccess );
154
+ }
155
+ return callback ;
156
+ }
157
+
158
+ private static void sendEmail (String subject , String textBody ) {
159
+ Messaging .SingleEmailMessage singleEmail = new Messaging .SingleEmailMessage ();
160
+ singleEmail .setToAddresses (new List <String >{ UserInfo .getUserEmail () });
161
+ singleEmail .setSubject (subject );
162
+ singleEmail .setPlainTextBody (textBody );
163
+
164
+ Messaging .sendEmail (new List <Messaging .SingleEmailMessage >{ singleEmail });
165
+ }
166
+
167
+ // Inner class used as the default callback if no other callback is specified
168
+ @testVisible
169
+ private class DefaultDeployCallback implements Metadata .DeployCallback {
170
+ @testVisible
171
+ private Boolean success ;
172
+
173
+ private Boolean sendEmailOnError ;
174
+ private Boolean sendEmailOnSuccess ;
175
+
176
+ @testVisible
177
+ private DefaultDeployCallback () {
178
+ this (DEFAULT_SEND_EMAIL_ON_ERROR , DEFAULT_SEND_EMAIL_ON_SUCCESS );
179
+ }
180
+ @testVisible
181
+ private DefaultDeployCallback (Boolean sendEmailOnError , Boolean sendEmailOnSuccess ) {
182
+ this .sendEmailOnError = sendEmailOnError ;
183
+ this .sendEmailOnSuccess = sendEmailOnSuccess ;
184
+ }
185
+
186
+ public void handleResult (Metadata.DeployResult result , Metadata.DeployCallbackContext context ) {
187
+ System .debug (' deploy result.success==' + result .success );
188
+ System .debug (' deploy result==' + result );
189
+ System .debug (' deploy callback context==' + context );
190
+
191
+ System .debug (' this.sendEmailOnError==' + this .sendEmailOnError );
192
+ System .debug (' this.sendEmailOnSuccess==' + this .sendEmailOnSuccess );
193
+
194
+ this .success = result .success ;
195
+
196
+ // Build the email pieces
197
+ String subject = ' Custom metadata type deployment completed' ;
198
+ String textBody = ' Deployment ID {0} completed\n Status: {1}\n {2} total items deployed\n {3} items failed\n Details: {4}' ;
199
+ List <Object > textReplacements = new List <Object >{
200
+ result .id ,
201
+ result .status ,
202
+ result .numberComponentsTotal ,
203
+ result .numberComponentErrors ,
204
+ Json .serializePretty (result .details )
205
+ };
206
+ textBody = String .format (textBody , textReplacements );
207
+
208
+ // Send the email
209
+ if (result .success == true && this .sendEmailOnSuccess == true ) {
210
+ System .debug (' Deployment Succeeded!' );
211
+
212
+ sendEmail (subject , textBody );
213
+ } else if (result .success == false && this .sendEmailOnError == true ) {
214
+ System .debug (' Deployment Failed!' );
215
+
216
+ sendEmail (subject , textBody );
217
+ }
218
+ }
219
+ }
96
220
}
0 commit comments