5
5
package io .airbyte .server .handlers ;
6
6
7
7
import static org .junit .jupiter .api .Assertions .assertEquals ;
8
+ import static org .junit .jupiter .api .Assertions .assertThrows ;
8
9
import static org .junit .jupiter .api .Assertions .assertTrue ;
9
10
import static org .mockito .ArgumentMatchers .eq ;
10
11
import static org .mockito .Mockito .mock ;
24
25
import io .airbyte .api .model .generated .OperatorNormalization .OptionEnum ;
25
26
import io .airbyte .api .model .generated .OperatorType ;
26
27
import io .airbyte .api .model .generated .OperatorWebhook ;
28
+ import io .airbyte .api .model .generated .OperatorWebhook .WebhookTypeEnum ;
29
+ import io .airbyte .api .model .generated .OperatorWebhookDbtCloud ;
27
30
import io .airbyte .commons .enums .Enums ;
28
31
import io .airbyte .config .OperatorNormalization .Option ;
29
32
import io .airbyte .config .StandardSync ;
@@ -45,10 +48,12 @@ class OperationsHandlerTest {
45
48
46
49
private static final String WEBHOOK_OPERATION_NAME = "fake-operation-name" ;
47
50
private static final UUID WEBHOOK_CONFIG_ID = UUID .randomUUID ();
48
- private static final String WEBHOOK_EXECUTION_URL = "fake-execution-url" ;
49
- private static final String WEBHOOK_EXECUTION_BODY = "fake-execution-body" ;
50
51
private static final UUID WEBHOOK_OPERATION_ID = UUID .randomUUID ();
51
- public static final String NEW_EXECUTION_URL = "new-execution-url" ;
52
+ private static final Integer DBT_CLOUD_WEBHOOK_ACCOUNT_ID = 123 ;
53
+ private static final Integer DBT_CLOUD_WEBHOOK_JOB_ID = 456 ;
54
+ private static final Integer NEW_DBT_CLOUD_WEBHOOK_ACCOUNT_ID = 789 ;
55
+ public static final String EXECUTION_BODY = "{\" cause\" : \" airbyte\" }" ;
56
+ public static final String EXECUTION_URL_TEMPLATE = "https://cloud.getdbt.com/api/v2/accounts/%d/jobs/%d/run/" ;
52
57
private ConfigRepository configRepository ;
53
58
private Supplier <UUID > uuidGenerator ;
54
59
private OperationsHandler operationsHandler ;
@@ -104,8 +109,10 @@ void testCreateWebhookOperation() throws JsonValidationException, ConfigNotFound
104
109
when (uuidGenerator .get ()).thenReturn (WEBHOOK_OPERATION_ID );
105
110
final OperatorWebhook webhookConfig = new OperatorWebhook ()
106
111
.webhookConfigId (WEBHOOK_CONFIG_ID )
107
- .executionUrl (WEBHOOK_EXECUTION_URL )
108
- .executionBody (WEBHOOK_EXECUTION_BODY );
112
+ .webhookType (WebhookTypeEnum .DBTCLOUD )
113
+ .dbtCloud (new OperatorWebhookDbtCloud ()
114
+ .accountId (DBT_CLOUD_WEBHOOK_ACCOUNT_ID )
115
+ .jobId (DBT_CLOUD_WEBHOOK_JOB_ID ));
109
116
final OperationCreate operationCreate = new OperationCreate ()
110
117
.workspaceId (standardSyncOperation .getWorkspaceId ())
111
118
.name (WEBHOOK_OPERATION_NAME )
@@ -119,8 +126,9 @@ void testCreateWebhookOperation() throws JsonValidationException, ConfigNotFound
119
126
.withOperatorType (StandardSyncOperation .OperatorType .WEBHOOK )
120
127
.withOperatorWebhook (new io .airbyte .config .OperatorWebhook ()
121
128
.withWebhookConfigId (WEBHOOK_CONFIG_ID )
122
- .withExecutionUrl (WEBHOOK_EXECUTION_URL )
123
- .withExecutionBody (WEBHOOK_EXECUTION_BODY ))
129
+ .withExecutionUrl (String .format (EXECUTION_URL_TEMPLATE , DBT_CLOUD_WEBHOOK_ACCOUNT_ID ,
130
+ DBT_CLOUD_WEBHOOK_JOB_ID ))
131
+ .withExecutionBody (EXECUTION_BODY ))
124
132
.withTombstone (false );
125
133
126
134
when (configRepository .getStandardSyncOperation (WEBHOOK_OPERATION_ID )).thenReturn (expectedPersistedOperation );
@@ -131,7 +139,12 @@ void testCreateWebhookOperation() throws JsonValidationException, ConfigNotFound
131
139
assertEquals (WEBHOOK_OPERATION_ID , actualOperationRead .getOperationId ());
132
140
assertEquals (WEBHOOK_OPERATION_NAME , actualOperationRead .getName ());
133
141
assertEquals (OperatorType .WEBHOOK , actualOperationRead .getOperatorConfiguration ().getOperatorType ());
134
- assertEquals (webhookConfig , actualOperationRead .getOperatorConfiguration ().getWebhook ());
142
+
143
+ // NOTE: we expect the server to dual-write on read until the frontend moves to the new format.
144
+ final OperatorWebhook expectedWebhookConfigRead =
145
+ webhookConfig .executionUrl (String .format (EXECUTION_URL_TEMPLATE , DBT_CLOUD_WEBHOOK_ACCOUNT_ID ,
146
+ DBT_CLOUD_WEBHOOK_JOB_ID )).executionBody (EXECUTION_BODY );
147
+ assertEquals (expectedWebhookConfigRead , actualOperationRead .getOperatorConfiguration ().getWebhook ());
135
148
136
149
verify (configRepository ).writeStandardSyncOperation (eq (expectedPersistedOperation ));
137
150
}
@@ -189,36 +202,58 @@ void testUpdateWebhookOperation() throws JsonValidationException, ConfigNotFound
189
202
when (uuidGenerator .get ()).thenReturn (WEBHOOK_OPERATION_ID );
190
203
final OperatorWebhook webhookConfig = new OperatorWebhook ()
191
204
.webhookConfigId (WEBHOOK_CONFIG_ID )
192
- .executionUrl (NEW_EXECUTION_URL )
193
- .executionBody (WEBHOOK_EXECUTION_BODY );
205
+ .webhookType (WebhookTypeEnum .DBTCLOUD )
206
+ .dbtCloud (new OperatorWebhookDbtCloud ()
207
+ .accountId (NEW_DBT_CLOUD_WEBHOOK_ACCOUNT_ID )
208
+ .jobId (DBT_CLOUD_WEBHOOK_JOB_ID ));
194
209
final OperationUpdate operationUpdate = new OperationUpdate ()
195
210
.name (WEBHOOK_OPERATION_NAME )
196
211
.operationId (WEBHOOK_OPERATION_ID )
197
212
.operatorConfiguration (new OperatorConfiguration ()
198
213
.operatorType (OperatorType .WEBHOOK ).webhook (webhookConfig ));
199
214
215
+ final var persistedWebhook = new io .airbyte .config .OperatorWebhook ()
216
+ .withWebhookConfigId (WEBHOOK_CONFIG_ID )
217
+ .withExecutionUrl (String .format (EXECUTION_URL_TEMPLATE , DBT_CLOUD_WEBHOOK_ACCOUNT_ID ,
218
+ DBT_CLOUD_WEBHOOK_JOB_ID ))
219
+ .withExecutionBody (EXECUTION_BODY );
220
+
221
+ final var updatedWebhook = new io .airbyte .config .OperatorWebhook ()
222
+ .withWebhookConfigId (WEBHOOK_CONFIG_ID )
223
+ .withExecutionUrl (String .format (EXECUTION_URL_TEMPLATE , NEW_DBT_CLOUD_WEBHOOK_ACCOUNT_ID ,
224
+ DBT_CLOUD_WEBHOOK_JOB_ID ))
225
+ .withExecutionBody (EXECUTION_BODY );
226
+
200
227
final StandardSyncOperation persistedOperation = new StandardSyncOperation ()
201
228
.withWorkspaceId (standardSyncOperation .getWorkspaceId ())
202
229
.withOperationId (WEBHOOK_OPERATION_ID )
203
230
.withName (WEBHOOK_OPERATION_NAME )
204
231
.withOperatorType (StandardSyncOperation .OperatorType .WEBHOOK )
205
- .withOperatorWebhook (new io .airbyte .config .OperatorWebhook ()
206
- .withWebhookConfigId (WEBHOOK_CONFIG_ID )
207
- .withExecutionUrl (WEBHOOK_EXECUTION_URL )
208
- .withExecutionBody (WEBHOOK_EXECUTION_BODY ));
232
+ .withOperatorWebhook (persistedWebhook );
209
233
210
- when (configRepository .getStandardSyncOperation (WEBHOOK_OPERATION_ID )).thenReturn (persistedOperation );
234
+ final StandardSyncOperation updatedOperation = new StandardSyncOperation ()
235
+ .withWorkspaceId (standardSyncOperation .getWorkspaceId ())
236
+ .withOperationId (WEBHOOK_OPERATION_ID )
237
+ .withName (WEBHOOK_OPERATION_NAME )
238
+ .withOperatorType (StandardSyncOperation .OperatorType .WEBHOOK )
239
+ .withOperatorWebhook (updatedWebhook );
240
+
241
+ when (configRepository .getStandardSyncOperation (WEBHOOK_OPERATION_ID )).thenReturn (persistedOperation ).thenReturn (updatedOperation );
211
242
212
243
final OperationRead actualOperationRead = operationsHandler .updateOperation (operationUpdate );
213
244
214
245
assertEquals (WEBHOOK_OPERATION_ID , actualOperationRead .getOperationId ());
215
246
assertEquals (WEBHOOK_OPERATION_NAME , actualOperationRead .getName ());
216
247
assertEquals (OperatorType .WEBHOOK , actualOperationRead .getOperatorConfiguration ().getOperatorType ());
217
- assertEquals (webhookConfig , actualOperationRead .getOperatorConfiguration ().getWebhook ());
248
+ final OperatorWebhook expectedWebhookConfigRead =
249
+ webhookConfig .executionUrl (String .format (EXECUTION_URL_TEMPLATE , NEW_DBT_CLOUD_WEBHOOK_ACCOUNT_ID ,
250
+ DBT_CLOUD_WEBHOOK_JOB_ID )).executionBody (EXECUTION_BODY );
251
+ assertEquals (expectedWebhookConfigRead , actualOperationRead .getOperatorConfiguration ().getWebhook ());
218
252
219
253
verify (configRepository )
220
254
.writeStandardSyncOperation (persistedOperation .withOperatorWebhook (persistedOperation .getOperatorWebhook ().withExecutionUrl (
221
- NEW_EXECUTION_URL )));
255
+ String .format (EXECUTION_URL_TEMPLATE , NEW_DBT_CLOUD_WEBHOOK_ACCOUNT_ID ,
256
+ DBT_CLOUD_WEBHOOK_JOB_ID ))));
222
257
}
223
258
224
259
@ Test
@@ -313,4 +348,39 @@ void testEnumConversion() {
313
348
io .airbyte .config .OperatorNormalization .Option .class ));
314
349
}
315
350
351
+ @ Test
352
+ void testDbtCloudRegex () {
353
+ // Validate that a non-url is rejected.
354
+ assertThrows (IllegalArgumentException .class , () -> checkDbtCloudUrl ("not-a-url" ));
355
+ // Validate that the URL is anchored to the beginning.
356
+ assertThrows (IllegalArgumentException .class ,
357
+ () -> checkDbtCloudUrl ("some-nonsense-" + String .format (EXECUTION_URL_TEMPLATE , DBT_CLOUD_WEBHOOK_ACCOUNT_ID ,
358
+ DBT_CLOUD_WEBHOOK_JOB_ID )));
359
+ // Validate that the URL is anchored to the end.
360
+ assertThrows (IllegalArgumentException .class ,
361
+ () -> checkDbtCloudUrl (String .format (EXECUTION_URL_TEMPLATE , DBT_CLOUD_WEBHOOK_ACCOUNT_ID ,
362
+ DBT_CLOUD_WEBHOOK_JOB_ID ) + "-some-nonsense" ));
363
+ // Validate that the account id must be an integer.
364
+ assertThrows (IllegalArgumentException .class , () -> checkDbtCloudUrl ("https://cloud.getdbt.com/api/v2/accounts/abc/jobs/123/run/" ));
365
+ // Validate that the job id must be an integer.
366
+ assertThrows (IllegalArgumentException .class , () -> checkDbtCloudUrl ("https://cloud.getdbt.com/api/v2/accounts/123/jobs/abc/run/" ));
367
+ }
368
+
369
+ private void checkDbtCloudUrl (final String urlToCheck ) throws JsonValidationException , ConfigNotFoundException , IOException {
370
+ final StandardSyncOperation persistedOperation = new StandardSyncOperation ()
371
+ .withWorkspaceId (standardSyncOperation .getWorkspaceId ())
372
+ .withOperationId (WEBHOOK_OPERATION_ID )
373
+ .withName (WEBHOOK_OPERATION_NAME )
374
+ .withOperatorType (StandardSyncOperation .OperatorType .WEBHOOK )
375
+ .withOperatorWebhook (new io .airbyte .config .OperatorWebhook ()
376
+ .withWebhookConfigId (WEBHOOK_CONFIG_ID )
377
+ .withExecutionUrl (urlToCheck )
378
+ .withExecutionBody (EXECUTION_BODY ))
379
+ .withTombstone (false );
380
+ when (configRepository .getStandardSyncOperation (WEBHOOK_OPERATION_ID )).thenReturn (persistedOperation );
381
+
382
+ final OperationIdRequestBody operationIdRequestBody = new OperationIdRequestBody ().operationId (WEBHOOK_OPERATION_ID );
383
+ operationsHandler .getOperation (operationIdRequestBody );
384
+ }
385
+
316
386
}
0 commit comments