github.com/argoproj/argo-events@v1.9.1/sensors/triggers/params_test.go (about) 1 /* 2 Copyright 2018 BlackRock, Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package triggers 18 19 import ( 20 "encoding/json" 21 "testing" 22 "time" 23 24 "github.com/ghodss/yaml" 25 "github.com/stretchr/testify/assert" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 28 29 "github.com/argoproj/argo-events/common" 30 apicommon "github.com/argoproj/argo-events/pkg/apis/common" 31 "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1" 32 ) 33 34 var sensorObj = &v1alpha1.Sensor{ 35 ObjectMeta: metav1.ObjectMeta{ 36 Name: "fake-sensor", 37 Namespace: "fake", 38 }, 39 Spec: v1alpha1.SensorSpec{ 40 Triggers: []v1alpha1.Trigger{ 41 { 42 Template: &v1alpha1.TriggerTemplate{ 43 Name: "fake-trigger", 44 K8s: &v1alpha1.StandardK8STrigger{}, 45 }, 46 }, 47 }, 48 }, 49 } 50 51 func newUnstructured(apiVersion, kind, namespace, name string) *unstructured.Unstructured { 52 return &unstructured.Unstructured{ 53 Object: map[string]interface{}{ 54 "apiVersion": apiVersion, 55 "kind": kind, 56 "metadata": map[string]interface{}{ 57 "namespace": namespace, 58 "name": name, 59 "labels": map[string]interface{}{ 60 "name": name, 61 }, 62 }, 63 }, 64 } 65 } 66 67 type Details struct { 68 Street string `json:"street"` 69 City string `json:"city"` 70 Pin string `json:"pin"` 71 } 72 73 type Payload struct { 74 FirstName string `json:"firstName"` 75 LastName string `json:"lastName"` 76 Age int `json:"age"` 77 IsActive bool `json:"isActive"` 78 TypelessAge string `json:"typelessAge"` 79 TypelessIsActive string `json:"typelessIsActive"` 80 Details Details `json:"details"` 81 } 82 83 func TestConstructPayload(t *testing.T) { 84 testEvents := map[string]*v1alpha1.Event{ 85 "fake-dependency": { 86 Context: &v1alpha1.EventContext{ 87 ID: "1", 88 Type: "webhook", 89 Source: "webhook-gateway", 90 DataContentType: common.MediaTypeJSON, 91 Subject: "example-1", 92 }, 93 Data: []byte("{\"firstName\": \"fake\"}"), 94 }, 95 "another-fake-dependency": { 96 Context: &v1alpha1.EventContext{ 97 ID: "2", 98 Type: "calendar", 99 Source: "calendar-gateway", 100 DataContentType: common.MediaTypeJSON, 101 Subject: "example-1", 102 }, 103 Data: []byte("{\"lastName\": \"foo\"}"), 104 }, 105 "use-event-data-type": { 106 Context: &v1alpha1.EventContext{ 107 ID: "3", 108 Type: "calendar", 109 Source: "calendar-gateway", 110 DataContentType: common.MediaTypeJSON, 111 Subject: "example-1", 112 }, 113 Data: []byte("{\"age\": 100, \"isActive\": false, \"countries\": [\"ca\", \"us\", \"mx\"]}"), 114 }, 115 } 116 117 defaultFirstName := "faker" 118 defaultLastName := "bar" 119 120 parameters := []v1alpha1.TriggerParameter{ 121 { 122 Src: &v1alpha1.TriggerParameterSource{ 123 DependencyName: "fake-dependency", 124 DataKey: "firstName", 125 Value: &defaultFirstName, 126 }, 127 Dest: "firstName", 128 }, 129 { 130 Src: &v1alpha1.TriggerParameterSource{ 131 DependencyName: "another-fake-dependency", 132 DataKey: "lastName", 133 Value: &defaultLastName, 134 }, 135 Dest: "lastName", 136 }, 137 { 138 Src: &v1alpha1.TriggerParameterSource{ 139 DependencyName: "use-event-data-type", 140 DataKey: "age", 141 UseRawData: true, 142 }, 143 Dest: "age", 144 }, 145 { 146 Src: &v1alpha1.TriggerParameterSource{ 147 DependencyName: "use-event-data-type", 148 DataKey: "isActive", 149 UseRawData: true, 150 }, 151 Dest: "isActive", 152 }, 153 { 154 Src: &v1alpha1.TriggerParameterSource{ 155 DependencyName: "use-event-data-type", 156 DataKey: "age", 157 }, 158 Dest: "typelessAge", 159 }, 160 { 161 Src: &v1alpha1.TriggerParameterSource{ 162 DependencyName: "use-event-data-type", 163 DataKey: "isActive", 164 }, 165 Dest: "typelessIsActive", 166 }, 167 } 168 169 payloadBytes, err := ConstructPayload(testEvents, parameters) 170 assert.Nil(t, err) 171 assert.NotNil(t, payloadBytes) 172 173 var p *Payload 174 err = json.Unmarshal(payloadBytes, &p) 175 assert.Nil(t, err) 176 assert.Equal(t, "fake", p.FirstName) 177 assert.Equal(t, "foo", p.LastName) 178 assert.Equal(t, 100, p.Age) 179 assert.Equal(t, false, p.IsActive) 180 assert.Equal(t, "100", p.TypelessAge) 181 assert.Equal(t, "false", p.TypelessIsActive) 182 183 parameters[0].Src.DataKey = "unknown" 184 parameters[1].Src.DataKey = "unknown" 185 186 payloadBytes, err = ConstructPayload(testEvents, parameters) 187 assert.Nil(t, err) 188 assert.NotNil(t, payloadBytes) 189 190 err = json.Unmarshal(payloadBytes, &p) 191 assert.Nil(t, err) 192 assert.Equal(t, "faker", p.FirstName) 193 assert.Equal(t, "bar", p.LastName) 194 } 195 196 func TestResolveParamValue(t *testing.T) { 197 event := &v1alpha1.Event{ 198 Context: &v1alpha1.EventContext{ 199 DataContentType: common.MediaTypeJSON, 200 Subject: "example-1", 201 Source: "webhook-gateway", 202 Type: "webhook", 203 ID: "1", 204 Time: metav1.Time{Time: time.Now().UTC()}, 205 }, 206 Data: []byte("{\"name\": {\"first\": \"fake\", \"last\": \"user\"}, \"reviews\": 8, \"rating\": 4.5, \"isActive\" : true, \"isVerified\" : false, \"countries\": [\"ca\", \"us\", \"mx\"]}"), 207 } 208 eventBody, err := json.Marshal(event) 209 assert.Nil(t, err) 210 211 events := map[string]*v1alpha1.Event{ 212 "fake-dependency": event, 213 } 214 215 defaultValue := "hello" 216 217 tests := []struct { 218 name string 219 source *v1alpha1.TriggerParameterSource 220 result string 221 }{ 222 { 223 name: "get first name", 224 source: &v1alpha1.TriggerParameterSource{ 225 DependencyName: "fake-dependency", 226 DataKey: "name.first", 227 }, 228 result: "fake", 229 }, 230 { 231 name: "get the event subject", 232 source: &v1alpha1.TriggerParameterSource{ 233 DependencyName: "fake-dependency", 234 ContextKey: "subject", 235 }, 236 result: "example-1", 237 }, 238 { 239 name: "get the entire payload", 240 source: &v1alpha1.TriggerParameterSource{ 241 DependencyName: "fake-dependency", 242 }, 243 result: string(eventBody), 244 }, 245 { 246 name: "get the default value", 247 source: &v1alpha1.TriggerParameterSource{ 248 DependencyName: "fake-dependency", 249 Value: &defaultValue, 250 }, 251 result: defaultValue, 252 }, 253 { 254 name: "data key has preference over context key", 255 source: &v1alpha1.TriggerParameterSource{ 256 DependencyName: "fake-dependency", 257 ContextKey: "subject", 258 DataKey: "name.first", 259 }, 260 result: "fake", 261 }, 262 { 263 name: "get first name with template", 264 source: &v1alpha1.TriggerParameterSource{ 265 DependencyName: "fake-dependency", 266 DataTemplate: "{{ .Input.name.first }}", 267 }, 268 result: "fake", 269 }, 270 { 271 name: "get capitalized first name with template", 272 source: &v1alpha1.TriggerParameterSource{ 273 DependencyName: "fake-dependency", 274 DataTemplate: "{{ upper .Input.name.first }}", 275 }, 276 result: "FAKE", 277 }, 278 { 279 name: "get subject with template", 280 source: &v1alpha1.TriggerParameterSource{ 281 DependencyName: "fake-dependency", 282 ContextTemplate: "{{ .Input.subject }}", 283 }, 284 result: "example-1", 285 }, 286 { 287 name: "get formatted subject with template", 288 source: &v1alpha1.TriggerParameterSource{ 289 DependencyName: "fake-dependency", 290 ContextTemplate: `{{ .Input.subject | replace "-" "_" }}`, 291 }, 292 result: "example_1", 293 }, 294 { 295 name: "data template has preference over context template", 296 source: &v1alpha1.TriggerParameterSource{ 297 DependencyName: "fake-dependency", 298 ContextTemplate: "{{ .Input.subject }}", 299 DataTemplate: "{{ .Input.name.first }}", 300 }, 301 result: "fake", 302 }, 303 { 304 name: "data template fails over to data key", 305 source: &v1alpha1.TriggerParameterSource{ 306 DependencyName: "fake-dependency", 307 DataTemplate: "{{ .Input.name.non_exist }}", 308 DataKey: "name.first", 309 }, 310 result: "fake", 311 }, 312 { 313 name: "invalid template fails over to data key", 314 source: &v1alpha1.TriggerParameterSource{ 315 DependencyName: "fake-dependency", 316 DataTemplate: "{{ no }}", 317 DataKey: "name.first", 318 }, 319 result: "fake", 320 }, 321 { 322 name: "UseRawData set to true - string", 323 source: &v1alpha1.TriggerParameterSource{ 324 DependencyName: "fake-dependency", 325 DataKey: "name.first", 326 UseRawData: true, 327 }, 328 result: "fake", 329 }, 330 { 331 name: "UseRawData set to true - json", 332 source: &v1alpha1.TriggerParameterSource{ 333 DependencyName: "fake-dependency", 334 DataKey: "name", 335 UseRawData: true, 336 }, 337 result: "{\"first\": \"fake\", \"last\": \"user\"}", 338 }, 339 { 340 name: "UseRawData set to true - list", 341 source: &v1alpha1.TriggerParameterSource{ 342 DependencyName: "fake-dependency", 343 DataKey: "countries", 344 UseRawData: true, 345 }, 346 result: "[\"ca\", \"us\", \"mx\"]", 347 }, 348 } 349 350 for _, test := range tests { 351 t.Run(test.name, func(t *testing.T) { 352 result, _, err := ResolveParamValue(test.source, events) 353 assert.Nil(t, err) 354 assert.Equal(t, test.result, *result) 355 }) 356 } 357 } 358 359 func TestRenderDataAsJSON(t *testing.T) { 360 event := &v1alpha1.Event{ 361 Context: &v1alpha1.EventContext{ 362 DataContentType: common.MediaTypeJSON, 363 Subject: "example-1", 364 Source: "webhook-gateway", 365 Type: "webhook", 366 ID: "1", 367 Time: metav1.Time{Time: time.Now().UTC()}, 368 }, 369 Data: []byte("{\"name\": {\"first\": \"fake\", \"last\": \"user\"} }"), 370 } 371 body, err := renderEventDataAsJSON(event) 372 assert.Nil(t, err) 373 assert.Equal(t, string(body), "{\"name\": {\"first\": \"fake\", \"last\": \"user\"} }") 374 375 testYaml := struct { 376 Name string `json:"name"` 377 Age int `json:"age"` 378 }{ 379 Name: "test", 380 Age: 20, 381 } 382 383 yamlBody, err := yaml.Marshal(&testYaml) 384 assert.Nil(t, err) 385 event.Data = yamlBody 386 event.Context.DataContentType = common.MediaTypeYAML 387 assert.Nil(t, err) 388 body, err = renderEventDataAsJSON(event) 389 assert.Nil(t, err) 390 assert.Equal(t, string(body), "{\"age\":20,\"name\":\"test\"}") 391 } 392 393 func TestApplyParams(t *testing.T) { 394 event := &v1alpha1.Event{ 395 Context: &v1alpha1.EventContext{ 396 DataContentType: common.MediaTypeJSON, 397 Subject: "example-1", 398 Source: "webhook-gateway", 399 Type: "webhook", 400 ID: "1", 401 Time: metav1.Time{Time: time.Now().UTC()}, 402 }, 403 Data: []byte("{\"name\": {\"first\": \"fake\", \"last\": \"user\"}, \"age\": 100, \"countries\": [\"ca\", \"us\", \"mx\"] }"), 404 } 405 406 events := map[string]*v1alpha1.Event{ 407 "fake-dependency": event, 408 } 409 410 tests := []struct { 411 name string 412 params []v1alpha1.TriggerParameter 413 jsonObj []byte 414 result []byte 415 }{ 416 { 417 name: "normal apply parameters operation", 418 params: []v1alpha1.TriggerParameter{ 419 { 420 Src: &v1alpha1.TriggerParameterSource{ 421 DependencyName: "fake-dependency", 422 DataKey: "name.first", 423 }, 424 Dest: "name", 425 Operation: v1alpha1.TriggerParameterOpNone, 426 }, 427 }, 428 jsonObj: []byte("{\"name\": \"faker\"}"), 429 result: []byte("{\"name\": \"fake\"}"), 430 }, 431 { 432 name: "apply parameters with prepend operation", 433 params: []v1alpha1.TriggerParameter{ 434 { 435 Src: &v1alpha1.TriggerParameterSource{ 436 DependencyName: "fake-dependency", 437 DataKey: "name.first", 438 }, 439 Dest: "name", 440 Operation: v1alpha1.TriggerParameterOpPrepend, 441 }, 442 }, 443 jsonObj: []byte("{\"name\": \"faker\"}"), 444 result: []byte("{\"name\": \"fakefaker\"}"), 445 }, 446 { 447 name: "apply parameters with append operation", 448 params: []v1alpha1.TriggerParameter{ 449 { 450 Src: &v1alpha1.TriggerParameterSource{ 451 DependencyName: "fake-dependency", 452 DataKey: "name.first", 453 }, 454 Dest: "name", 455 Operation: v1alpha1.TriggerParameterOpAppend, 456 }, 457 }, 458 jsonObj: []byte("{\"name\": \"faker\"}"), 459 result: []byte("{\"name\": \"fakerfake\"}"), 460 }, 461 { 462 name: "apply parameters with overwrite operation", 463 params: []v1alpha1.TriggerParameter{ 464 { 465 Src: &v1alpha1.TriggerParameterSource{ 466 DependencyName: "fake-dependency", 467 DataKey: "name.first", 468 }, 469 Dest: "name", 470 Operation: v1alpha1.TriggerParameterOpOverwrite, 471 }, 472 }, 473 jsonObj: []byte("{\"name\": \"faker\"}"), 474 result: []byte("{\"name\": \"fake\"}"), 475 }, 476 { 477 name: "apply block parameters with overwrite operation - useRawDataValue false", 478 params: []v1alpha1.TriggerParameter{ 479 { 480 Src: &v1alpha1.TriggerParameterSource{ 481 DependencyName: "fake-dependency", 482 DataKey: "name", 483 }, 484 Dest: "name", 485 Operation: v1alpha1.TriggerParameterOpOverwrite, 486 }, 487 }, 488 jsonObj: []byte("{\"name\": \"faker\"}"), 489 result: []byte("{\"name\": \"{\\\"first\\\": \\\"fake\\\", \\\"last\\\": \\\"user\\\"}\"}"), 490 }, 491 { 492 name: "apply block parameters with overwrite operation - useRawDataValue true", 493 params: []v1alpha1.TriggerParameter{ 494 { 495 Src: &v1alpha1.TriggerParameterSource{ 496 DependencyName: "fake-dependency", 497 DataKey: "name", 498 UseRawData: true, 499 }, 500 Dest: "name", 501 Operation: v1alpha1.TriggerParameterOpOverwrite, 502 }, 503 }, 504 jsonObj: []byte("{\"name\": \"faker\"}"), 505 result: []byte("{\"name\": {\"first\": \"fake\", \"last\": \"user\"}}"), 506 }, 507 { 508 name: "Use raw data types", 509 params: []v1alpha1.TriggerParameter{ 510 { 511 Src: &v1alpha1.TriggerParameterSource{ 512 DependencyName: "fake-dependency", 513 DataKey: "age", 514 UseRawData: true, 515 }, 516 Dest: "age", 517 Operation: v1alpha1.TriggerParameterOpOverwrite, 518 }, 519 { 520 Src: &v1alpha1.TriggerParameterSource{ 521 DependencyName: "fake-dependency", 522 DataKey: "age", 523 UseRawData: true, 524 }, 525 Dest: "ageWithYears", 526 Operation: v1alpha1.TriggerParameterOpAppend, 527 }, 528 { 529 Src: &v1alpha1.TriggerParameterSource{ 530 DependencyName: "fake-dependency", 531 DataKey: "countries", 532 UseRawData: true, 533 }, 534 Dest: "countries", 535 Operation: v1alpha1.TriggerParameterOpAppend, 536 }, 537 }, 538 jsonObj: []byte("{\"age\": \"this-gets-over-written\", \"ageWithYears\": \"Years: \"}"), 539 result: []byte("{\"age\": 100, \"ageWithYears\": \"Years: 100\",\"countries\":[\"ca\", \"us\", \"mx\"]}"), 540 }, 541 } 542 543 for _, test := range tests { 544 t.Run(test.name, func(t *testing.T) { 545 result, err := ApplyParams(test.jsonObj, test.params, events) 546 assert.Nil(t, err) 547 assert.Equal(t, string(test.result), string(result)) 548 }) 549 } 550 } 551 552 func TestApplyResourceParameters(t *testing.T) { 553 obj := sensorObj.DeepCopy() 554 deployment := newUnstructured("apps/v1", "Deployment", "fake-deployment", "fake") 555 556 event := &v1alpha1.Event{ 557 Context: &v1alpha1.EventContext{ 558 DataContentType: common.MediaTypeJSON, 559 Subject: "example-1", 560 Source: "webhook-gateway", 561 Type: "webhook", 562 ID: "1", 563 Time: metav1.Time{Time: time.Now().UTC()}, 564 }, 565 Data: []byte("{\"name\": {\"first\": \"test-deployment\"} }"), 566 } 567 568 testEvents := map[string]*v1alpha1.Event{ 569 "fake-dependency": event, 570 } 571 572 artifact := apicommon.NewResource(deployment) 573 obj.Spec.Triggers[0].Template.K8s.Source = &v1alpha1.ArtifactLocation{ 574 Resource: &artifact, 575 } 576 obj.Spec.Triggers[0].Template.K8s.Parameters = []v1alpha1.TriggerParameter{ 577 { 578 Src: &v1alpha1.TriggerParameterSource{ 579 DependencyName: "fake-dependency", 580 DataKey: "name.first", 581 }, 582 Operation: v1alpha1.TriggerParameterOpNone, 583 Dest: "metadata.name", 584 }, 585 } 586 587 err := ApplyResourceParameters(testEvents, obj.Spec.Triggers[0].Template.K8s.Parameters, deployment) 588 assert.Nil(t, err) 589 assert.Equal(t, deployment.GetName(), "test-deployment") 590 } 591 592 func TestApplyTemplateParameters(t *testing.T) { 593 obj := sensorObj.DeepCopy() 594 event := &v1alpha1.Event{ 595 Context: &v1alpha1.EventContext{ 596 DataContentType: common.MediaTypeJSON, 597 Subject: "example-1", 598 Source: "webhook-gateway", 599 Type: "webhook", 600 ID: "1", 601 Time: metav1.Time{Time: time.Now().UTC()}, 602 }, 603 Data: []byte("{\"group\": \"fake\" }"), 604 } 605 testEvents := map[string]*v1alpha1.Event{ 606 "fake-dependency": event, 607 } 608 609 obj.Spec.Triggers[0].Parameters = []v1alpha1.TriggerParameter{ 610 { 611 Src: &v1alpha1.TriggerParameterSource{ 612 DependencyName: "fake-dependency", 613 DataKey: "group", 614 }, 615 Operation: v1alpha1.TriggerParameterOpOverwrite, 616 Dest: "k8s.group", 617 }, 618 } 619 err := ApplyTemplateParameters(testEvents, &obj.Spec.Triggers[0]) 620 assert.Nil(t, err) 621 }