pact_broker 2.99.0 → 2.102.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/Gemfile +1 -0
  4. data/README.md +14 -4
  5. data/db/migrations/20220622_default_allow_dangerous_contract_modification_to_false_for_new_installations.rb +22 -0
  6. data/db/migrations/20220625_delete_pacticipants_with_no_name.rb +62 -0
  7. data/db/migrations/migration_helper.rb +6 -0
  8. data/docs/CONFIGURATION.md +3 -1
  9. data/docs/api/PACTICIPANTS.md +290 -0
  10. data/docs/api/WEBHOOKS.md +40 -40
  11. data/lib/pact_broker/api/contracts/pacticipant_create_schema.rb +28 -0
  12. data/lib/pact_broker/api/decorators/embedded_version_decorator.rb +0 -1
  13. data/lib/pact_broker/api/decorators/reason_decorator.rb +1 -15
  14. data/lib/pact_broker/api/decorators/triggered_webhook_decorator.rb +1 -2
  15. data/lib/pact_broker/api/resources/all_webhooks.rb +1 -4
  16. data/lib/pact_broker/api/resources/base_resource.rb +58 -5
  17. data/lib/pact_broker/api/resources/branch_version.rb +10 -1
  18. data/lib/pact_broker/api/resources/clean.rb +11 -9
  19. data/lib/pact_broker/api/resources/currently_deployed_versions_for_environment.rb +0 -4
  20. data/lib/pact_broker/api/resources/currently_supported_versions_for_environment.rb +0 -4
  21. data/lib/pact_broker/api/resources/deployed_version.rb +20 -16
  22. data/lib/pact_broker/api/resources/deployed_versions_for_version_and_environment.rb +1 -1
  23. data/lib/pact_broker/api/resources/environment.rb +5 -5
  24. data/lib/pact_broker/api/resources/environments.rb +1 -5
  25. data/lib/pact_broker/api/resources/label.rb +4 -0
  26. data/lib/pact_broker/api/resources/matrix_for_consumer_and_provider.rb +10 -0
  27. data/lib/pact_broker/api/resources/pact.rb +14 -5
  28. data/lib/pact_broker/api/resources/pact_webhooks.rb +1 -4
  29. data/lib/pact_broker/api/resources/pacticipant.rb +11 -5
  30. data/lib/pact_broker/api/resources/pacticipant_resource_methods.rb +0 -1
  31. data/lib/pact_broker/api/resources/pacticipant_webhooks.rb +1 -4
  32. data/lib/pact_broker/api/resources/pacticipants.rb +7 -6
  33. data/lib/pact_broker/api/resources/provider_pacts_for_verification.rb +19 -11
  34. data/lib/pact_broker/api/resources/publish_contracts.rb +12 -16
  35. data/lib/pact_broker/api/resources/released_version.rb +20 -8
  36. data/lib/pact_broker/api/resources/released_versions_for_version_and_environment.rb +11 -7
  37. data/lib/pact_broker/api/resources/tag.rb +7 -3
  38. data/lib/pact_broker/api/resources/verifications.rb +7 -9
  39. data/lib/pact_broker/api/resources/version.rb +8 -8
  40. data/lib/pact_broker/api/resources/webhook.rb +5 -4
  41. data/lib/pact_broker/api/resources/webhook_execution.rb +4 -6
  42. data/lib/pact_broker/config/runtime_configuration.rb +1 -1
  43. data/lib/pact_broker/contracts/contract_to_publish.rb +4 -0
  44. data/lib/pact_broker/contracts/contracts_to_publish.rb +4 -0
  45. data/lib/pact_broker/contracts/service.rb +23 -5
  46. data/lib/pact_broker/doc/views/index/pacticipant-branch-version.markdown +13 -2
  47. data/lib/pact_broker/doc/views/provider-pacts-for-verification.markdown +1 -1
  48. data/lib/pact_broker/domain/pacticipant.rb +1 -0
  49. data/lib/pact_broker/domain/tag.rb +8 -32
  50. data/lib/pact_broker/domain/verification.rb +3 -2
  51. data/lib/pact_broker/domain/version.rb +26 -10
  52. data/lib/pact_broker/locale/en.yml +8 -4
  53. data/lib/pact_broker/matrix/deployment_status_summary.rb +28 -19
  54. data/lib/pact_broker/matrix/parse_query.rb +5 -0
  55. data/lib/pact_broker/matrix/quick_row.rb +5 -2
  56. data/lib/pact_broker/matrix/repository.rb +3 -3
  57. data/lib/pact_broker/matrix/resolved_selector.rb +47 -10
  58. data/lib/pact_broker/matrix/service.rb +17 -7
  59. data/lib/pact_broker/matrix/unresolved_selector.rb +14 -2
  60. data/lib/pact_broker/messages.rb +0 -15
  61. data/lib/pact_broker/pacticipants/find_potential_duplicate_pacticipant_names.rb +3 -3
  62. data/lib/pact_broker/pacticipants/repository.rb +5 -4
  63. data/lib/pact_broker/pacticipants/service.rb +11 -1
  64. data/lib/pact_broker/pacts/generate_sha.rb +1 -0
  65. data/lib/pact_broker/pacts/pact_version.rb +1 -0
  66. data/lib/pact_broker/pacts/verifiable_pact_messages.rb +4 -2
  67. data/lib/pact_broker/repositories/helpers.rb +13 -0
  68. data/lib/pact_broker/string_refinements.rb +8 -0
  69. data/lib/pact_broker/test/http_test_data_builder.rb +15 -0
  70. data/lib/pact_broker/test/test_data_builder.rb +20 -0
  71. data/lib/pact_broker/ui/controllers/index.rb +3 -1
  72. data/lib/pact_broker/ui/views/index/show.haml +3 -4
  73. data/lib/pact_broker/ui/views/matrix/show.haml +5 -2
  74. data/lib/pact_broker/verifications/pact_version_provider_tag_successful_verification.rb +15 -0
  75. data/lib/pact_broker/version.rb +1 -1
  76. data/lib/pact_broker/versions/branch_service.rb +7 -0
  77. data/lib/pact_broker/versions/branch_version_repository.rb +17 -0
  78. data/lib/pact_broker/webhooks/pact_and_verification_parameters.rb +1 -1
  79. data/lib/rack/pact_broker/cascade.rb +87 -0
  80. data/lib/webmachine/describe_routes.rb +43 -9
  81. metadata +8 -5
  82. data/lib/db.rb +0 -79
  83. data/lib/pact_broker/api/resources/default_base_resource.rb +0 -0
data/docs/api/WEBHOOKS.md CHANGED
@@ -33,7 +33,7 @@ Body:
33
33
  "enabled": true,
34
34
  "request": {
35
35
  "method": "POST",
36
- "url": "https://example.org/example",
36
+ "url": "https://example.org/webhook",
37
37
  "headers": {
38
38
  "Content-Type": "application/json"
39
39
  },
@@ -50,29 +50,29 @@ Body:
50
50
  "_links": {
51
51
  "self": {
52
52
  "title": "an example webhook",
53
- "href": "http://example.org/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac"
53
+ "href": "https://pact-broker/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac"
54
54
  },
55
55
  "pb:execute": {
56
56
  "title": "Test the execution of the webhook with the latest matching pact or verification by sending a POST request to this URL",
57
- "href": "http://example.org/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac/execute"
57
+ "href": "https://pact-broker/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac/execute"
58
58
  },
59
59
  "pb:consumer": {
60
60
  "title": "Consumer",
61
61
  "name": "Foo",
62
- "href": "http://example.org/pacticipants/Foo"
62
+ "href": "https://pact-broker/pacticipants/Foo"
63
63
  },
64
64
  "pb:provider": {
65
65
  "title": "Provider",
66
66
  "name": "Bar",
67
- "href": "http://example.org/pacticipants/Bar"
67
+ "href": "https://pact-broker/pacticipants/Bar"
68
68
  },
69
69
  "pb:pact-webhooks": {
70
70
  "title": "All webhooks for consumer Foo and provider Bar",
71
- "href": "http://example.org/webhooks/provider/Bar/consumer/Foo"
71
+ "href": "https://pact-broker/webhooks/provider/Bar/consumer/Foo"
72
72
  },
73
73
  "pb:webhooks": {
74
74
  "title": "All webhooks",
75
- "href": "http://example.org/webhooks"
75
+ "href": "https://pact-broker/webhooks"
76
76
  }
77
77
  }
78
78
  }
@@ -142,15 +142,15 @@ Body:
142
142
  "_links": {
143
143
  "self": {
144
144
  "title": "an example webhook",
145
- "href": "http://example.org/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac"
145
+ "href": "https://pact-broker/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac"
146
146
  },
147
147
  "pb:execute": {
148
148
  "title": "Test the execution of the webhook with the latest matching pact or verification by sending a POST request to this URL",
149
- "href": "http://example.org/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac/execute"
149
+ "href": "https://pact-broker/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac/execute"
150
150
  },
151
151
  "pb:webhooks": {
152
152
  "title": "All webhooks",
153
- "href": "http://example.org/webhooks"
153
+ "href": "https://pact-broker/webhooks"
154
154
  }
155
155
  }
156
156
  }
@@ -191,13 +191,13 @@ Body:
191
191
  {
192
192
  "title": "A webhook for the pact between Foo and Bar",
193
193
  "name": "an example webhook",
194
- "href": "http://example.org/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac"
194
+ "href": "https://pact-broker/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac"
195
195
  }
196
196
  ],
197
197
  "curies": [
198
198
  {
199
199
  "name": "pb",
200
- "href": "http://example.org/doc/webhooks-{rel}",
200
+ "href": "https://pact-broker/doc/webhooks-{rel}",
201
201
  "templated": true
202
202
  }
203
203
  ]
@@ -240,7 +240,7 @@ Body:
240
240
  #### Response
241
241
 
242
242
  Status: `201`<br/>
243
- Headers: `{"Content-Type":"application/hal+json;charset=utf-8","Location":"http://example.org/webhooks/dCGCl-Ba3PqEFJ_iE9mJkQ"}`<br/>
243
+ Headers: `{"Content-Type":"application/hal+json;charset=utf-8","Location":"https://pact-broker/webhooks/dCGCl-Ba3PqEFJ_iE9mJkQ"}`<br/>
244
244
  Body:
245
245
 
246
246
  ```
@@ -269,15 +269,15 @@ Body:
269
269
  "_links": {
270
270
  "self": {
271
271
  "title": "an example webhook",
272
- "href": "http://example.org/webhooks/dCGCl-Ba3PqEFJ_iE9mJkQ"
272
+ "href": "https://pact-broker/webhooks/dCGCl-Ba3PqEFJ_iE9mJkQ"
273
273
  },
274
274
  "pb:execute": {
275
275
  "title": "Test the execution of the webhook with the latest matching pact or verification by sending a POST request to this URL",
276
- "href": "http://example.org/webhooks/dCGCl-Ba3PqEFJ_iE9mJkQ/execute"
276
+ "href": "https://pact-broker/webhooks/dCGCl-Ba3PqEFJ_iE9mJkQ/execute"
277
277
  },
278
278
  "pb:webhooks": {
279
279
  "title": "All webhooks",
280
- "href": "http://example.org/webhooks"
280
+ "href": "https://pact-broker/webhooks"
281
281
  }
282
282
  }
283
283
  }
@@ -320,7 +320,7 @@ Body:
320
320
  "curies": [
321
321
  {
322
322
  "name": "pb",
323
- "href": "http://example.org/doc/webhooks-{rel}",
323
+ "href": "https://pact-broker/doc/webhooks-{rel}",
324
324
  "templated": true
325
325
  }
326
326
  ]
@@ -365,7 +365,7 @@ Body:
365
365
  "curies": [
366
366
  {
367
367
  "name": "pb",
368
- "href": "http://example.org/doc/webhooks-{rel}",
368
+ "href": "https://pact-broker/doc/webhooks-{rel}",
369
369
  "templated": true
370
370
  }
371
371
  ]
@@ -408,13 +408,13 @@ Body:
408
408
  {
409
409
  "title": "A webhook for the pact between Foo and Bar",
410
410
  "name": "an example webhook",
411
- "href": "http://example.org/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac"
411
+ "href": "https://pact-broker/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac"
412
412
  }
413
413
  ],
414
414
  "curies": [
415
415
  {
416
416
  "name": "pb",
417
- "href": "http://example.org/doc/webhooks-{rel}",
417
+ "href": "https://pact-broker/doc/webhooks-{rel}",
418
418
  "templated": true
419
419
  }
420
420
  ]
@@ -457,13 +457,13 @@ Body:
457
457
  {
458
458
  "title": "A webhook for the pact between Foo and Bar",
459
459
  "name": "an example webhook",
460
- "href": "http://example.org/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac"
460
+ "href": "https://pact-broker/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac"
461
461
  }
462
462
  ],
463
463
  "curies": [
464
464
  {
465
465
  "name": "pb",
466
- "href": "http://example.org/doc/webhooks-{rel}",
466
+ "href": "https://pact-broker/doc/webhooks-{rel}",
467
467
  "templated": true
468
468
  }
469
469
  ]
@@ -510,12 +510,12 @@ Body:
510
510
  "triggeredAt": "2021-09-01T00:07:21+00:00",
511
511
  "_links": {
512
512
  "pb:logs": {
513
- "href": "http://example.org/triggered-webhooks/6cd5cc48-db3c-4a4c-a36d-e9bedeb9d91e/logs",
513
+ "href": "https://pact-broker/triggered-webhooks/6cd5cc48-db3c-4a4c-a36d-e9bedeb9d91e/logs",
514
514
  "title": "Webhook execution logs",
515
515
  "name": "POST example.org"
516
516
  },
517
517
  "pb:webhook": {
518
- "href": "http://example.org/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac",
518
+ "href": "https://pact-broker/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac",
519
519
  "title": "Webhook",
520
520
  "name": "POST example.org"
521
521
  }
@@ -533,20 +533,20 @@ Body:
533
533
  ],
534
534
  "pb:pact-webhooks": {
535
535
  "title": "Webhooks for the pact between Foo and Bar",
536
- "href": "http://example.org/pacts/provider/Bar/consumer/Foo/webhooks"
536
+ "href": "https://pact-broker/pacts/provider/Bar/consumer/Foo/webhooks"
537
537
  },
538
538
  "pb:pact-version": {
539
- "href": "http://example.org/pacts/provider/Bar/consumer/Foo/version/2",
539
+ "href": "https://pact-broker/pacts/provider/Bar/consumer/Foo/version/3e1f00a04",
540
540
  "title": "Pact",
541
- "name": "Pact between Foo (2) and Bar"
541
+ "name": "Pact between Foo (3e1f00a04) and Bar"
542
542
  },
543
543
  "pb:consumer": {
544
- "href": "http://example.org/pacticipants/Foo",
544
+ "href": "https://pact-broker/pacticipants/Foo",
545
545
  "title": "Consumer",
546
546
  "name": "Foo"
547
547
  },
548
548
  "pb:provider": {
549
- "href": "http://example.org/pacticipants/Bar",
549
+ "href": "https://pact-broker/pacticipants/Bar",
550
550
  "title": "Provider",
551
551
  "name": "Bar"
552
552
  }
@@ -579,13 +579,13 @@ Body:
579
579
  "request": {
580
580
  "headers": {
581
581
  "accept": "*/*",
582
- "user-agent": "Pact Broker v2.87.0",
582
+ "user-agent": "Pact Broker",
583
583
  "content-type": "application/json"
584
584
  },
585
585
  "body": {
586
- "pactUrl": "http://example.org/pacts/provider/Bar/consumer/Foo/pact-version/3e193ecb37ad04b43ce974a38352c704b2e0ed6b/metadata/Y3ZuPTImdz10cnVl"
586
+ "pactUrl": "https://pact-broker/pacts/provider/Bar/consumer/Foo/pact-version/3e193ecb37ad04b43ce974a38352c704b2e0ed6b/metadata/3e193ecb37ad04b43ce974a38352c704b2e0ed6b"
587
587
  },
588
- "url": "/example"
588
+ "url": "/webhook"
589
589
  },
590
590
  "response": {
591
591
  "status": 200,
@@ -593,7 +593,7 @@ Body:
593
593
  },
594
594
  "body": ""
595
595
  },
596
- "logs": "[2021-09-01T10:07:21Z] DEBUG: Webhook context {\"base_url\":\"http://example.org\",\"event_name\":\"test\"}\n[2021-09-01T10:07:21Z] INFO: HTTP/1.1 POST https://example.org/example\n[2021-09-01T10:07:21Z] INFO: accept: */*\n[2021-09-01T10:07:21Z] INFO: user-agent: Pact Broker v2.87.0\n[2021-09-01T10:07:21Z] INFO: content-type: application/json\n[2021-09-01T10:07:21Z] INFO: {\"pactUrl\":\"http://example.org/pacts/provider/Bar/consumer/Foo/pact-version/3e193ecb37ad04b43ce974a38352c704b2e0ed6b/metadata/Y3ZuPTImdz10cnVl\"}\n[2021-09-01T10:07:21Z] INFO: HTTP/1.0 200 \n[2021-09-01T10:07:21Z] INFO: \n",
596
+ "logs": "[2021-09-01T10:07:21Z] DEBUG: Webhook context {\"base_url\":\"https://pact-broker\",\"event_name\":\"test\"}\n[2021-09-01T10:07:21Z] INFO: HTTP/1.1 POST https://example.org/webhook\n[2021-09-01T10:07:21Z] INFO: accept: */*\n[2021-09-01T10:07:21Z] INFO: user-agent: Pact Broker\n[2021-09-01T10:07:21Z] INFO: content-type: application/json\n[2021-09-01T10:07:21Z] INFO: {\"pactUrl\":\"https://pact-broker/pacts/provider/Bar/consumer/Foo/pact-version/3e193ecb37ad04b43ce974a38352c704b2e0ed6b/metadata/3e193ecb37ad04b43ce974a38352c704b2e0ed6b\"}\n[2021-09-01T10:07:21Z] INFO: HTTP/1.0 200 \n[2021-09-01T10:07:21Z] INFO: \n",
597
597
  "success": true,
598
598
  "_links": {
599
599
  }
@@ -649,11 +649,11 @@ Body:
649
649
  "request": {
650
650
  "headers": {
651
651
  "accept": "application/json",
652
- "user-agent": "Pact Broker v2.87.0",
652
+ "user-agent": "Pact Broker",
653
653
  "authorization": "**********"
654
654
  },
655
655
  "body": {
656
- "pactUrl": "http://example.org/pacts/provider/Bar/consumer/Foo/pact-version/3e193ecb37ad04b43ce974a38352c704b2e0ed6b/metadata/Y3ZuPTImdz10cnVl"
656
+ "pactUrl": "https://pact-broker/pacts/provider/Bar/consumer/Foo/pact-version/3e193ecb37ad04b43ce974a38352c704b2e0ed6b/metadata/3e193ecb37ad04b43ce974a38352c704b2e0ed6b"
657
657
  },
658
658
  "url": "/example"
659
659
  },
@@ -663,7 +663,7 @@ Body:
663
663
  },
664
664
  "body": ""
665
665
  },
666
- "logs": "[2021-09-01T10:07:21Z] DEBUG: Webhook context {\"base_url\":\"http://example.org\",\"event_name\":\"test\"}\n[2021-09-01T10:07:21Z] INFO: HTTP/1.1 POST https://example.org/example\n[2021-09-01T10:07:21Z] INFO: accept: application/json\n[2021-09-01T10:07:21Z] INFO: user-agent: Pact Broker v2.87.0\n[2021-09-01T10:07:21Z] INFO: authorization: **********\n[2021-09-01T10:07:21Z] INFO: {\"pactUrl\":\"http://example.org/pacts/provider/Bar/consumer/Foo/pact-version/3e193ecb37ad04b43ce974a38352c704b2e0ed6b/metadata/Y3ZuPTImdz10cnVl\"}\n[2021-09-01T10:07:21Z] INFO: HTTP/1.0 200 \n[2021-09-01T10:07:21Z] INFO: \n",
666
+ "logs": "[2021-09-01T10:07:21Z] DEBUG: Webhook context {\"base_url\":\"https://pact-broker\",\"event_name\":\"test\"}\n[2021-09-01T10:07:21Z] INFO: HTTP/1.1 POST https://example.org/example\n[2021-09-01T10:07:21Z] INFO: accept: application/json\n[2021-09-01T10:07:21Z] INFO: user-agent: Pact Broker\n[2021-09-01T10:07:21Z] INFO: authorization: **********\n[2021-09-01T10:07:21Z] INFO: {\"pactUrl\":\"https://pact-broker/pacts/provider/Bar/consumer/Foo/pact-version/3e193ecb37ad04b43ce974a38352c704b2e0ed6b/metadata/3e193ecb37ad04b43ce974a38352c704b2e0ed6b\"}\n[2021-09-01T10:07:21Z] INFO: HTTP/1.0 200 \n[2021-09-01T10:07:21Z] INFO: \n",
667
667
  "success": true,
668
668
  "_links": {
669
669
  }
@@ -704,12 +704,12 @@ Body:
704
704
  "triggeredAt": "2021-09-01T00:07:21+00:00",
705
705
  "_links": {
706
706
  "pb:logs": {
707
- "href": "http://example.org/triggered-webhooks/6cd5cc48-db3c-4a4c-a36d-e9bedeb9d91e/logs",
707
+ "href": "https://pact-broker/triggered-webhooks/6cd5cc48-db3c-4a4c-a36d-e9bedeb9d91e/logs",
708
708
  "title": "Webhook execution logs",
709
709
  "name": "POST example.org"
710
710
  },
711
711
  "pb:webhook": {
712
- "href": "http://example.org/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac",
712
+ "href": "https://pact-broker/webhooks/d2181b32-8b03-4daf-8cc0-d9168b2f6fac",
713
713
  "title": "Webhook",
714
714
  "name": "POST example.org"
715
715
  }
@@ -719,8 +719,8 @@ Body:
719
719
  },
720
720
  "_links": {
721
721
  "self": {
722
- "title": "Webhooks triggered by the publication of the pact between Foo (2) and Bar",
723
- "href": "http://example.org/pacts/provider/Bar/consumer/Foo/version/2/triggered-webhooks"
722
+ "title": "Webhooks triggered by the publication of the pact between Foo (3e1f00a04) and Bar",
723
+ "href": "http://example.org/pacts/provider/Bar/consumer/Foo/version/3e1f00a04/triggered-webhooks"
724
724
  }
725
725
  }
726
726
  }
@@ -0,0 +1,28 @@
1
+ require "pact_broker/api/contracts/pacticipant_schema"
2
+
3
+ module PactBroker
4
+ module Api
5
+ module Contracts
6
+ class PacticipantCreateSchema
7
+ extend DryValidationWorkarounds
8
+ extend PactBroker::Messages
9
+ using PactBroker::HashRefinements
10
+
11
+ SCHEMA = Dry::Validation.Schema do
12
+ configure do
13
+ predicates(DryValidationPredicates)
14
+ config.messages_file = File.expand_path("../../../locale/en.yml", __FILE__)
15
+ end
16
+ required(:name).filled(:str?, :single_line?)
17
+ end
18
+
19
+ def self.call(params_with_string_keys)
20
+ params = params_with_string_keys&.symbolize_keys
21
+ update_errors = PacticipantSchema::SCHEMA.call(params).messages(full: true)
22
+ create_errors = SCHEMA.call(params).messages(full: true)
23
+ select_first_message(flatten_indexed_messages(update_errors.merge(create_errors)))
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -8,7 +8,6 @@ module PactBroker
8
8
 
9
9
  property :number
10
10
  if PactBroker.feature_enabled?(:branches)
11
- property :branch
12
11
  property :build_url
13
12
  end
14
13
 
@@ -30,7 +30,7 @@ module PactBroker
30
30
  when PactBroker::Matrix::PactNotVerifiedByRequiredProviderVersion
31
31
  "There is no verified pact between #{reason.consumer_selector.description} and #{reason.provider_selector.description}"
32
32
  when PactBroker::Matrix::SpecifiedVersionDoesNotExist
33
- version_does_not_exist_description(reason.selector)
33
+ reason.selector.version_does_not_exist_description
34
34
  when PactBroker::Matrix::VerificationFailed
35
35
  "The verification for the pact between #{reason.consumer_selector.description} and #{reason.provider_selector.description} failed"
36
36
  when PactBroker::Matrix::NoDependenciesMissing
@@ -53,20 +53,6 @@ module PactBroker
53
53
  end
54
54
  end
55
55
 
56
- def version_does_not_exist_description selector
57
- if selector.version_does_not_exist?
58
- if selector.tag
59
- "No version with tag #{selector.tag} exists for #{selector.pacticipant_name}"
60
- elsif selector.pacticipant_version_number
61
- "No pacts or verifications have been published for version #{selector.pacticipant_version_number} of #{selector.pacticipant_name}"
62
- else
63
- "No pacts or verifications have been published for #{selector.pacticipant_name}"
64
- end
65
- else
66
- ""
67
- end
68
- end
69
-
70
56
  # TODO move this somewhere else
71
57
  def interaction_description(interaction)
72
58
  if interaction["providerState"] && interaction["providerState"] != ""
@@ -10,8 +10,7 @@ module PactBroker
10
10
  property :number_of_attempts_remaining, as: :attemptsRemaining
11
11
  property :trigger_type, as: :triggerType
12
12
  property :event_name, as: :eventName
13
-
14
- property :created_at, as: :triggeredAt
13
+ property :created_at, as: :triggeredAt, getter: lambda { |_| FormatDateTime.call(created_at) }, writeable: false
15
14
 
16
15
  link :'pb:logs' do | context |
17
16
  {
@@ -30,10 +30,7 @@ module PactBroker
30
30
  end
31
31
 
32
32
  def malformed_request?
33
- if request.post?
34
- return invalid_json? || validation_errors?(webhook)
35
- end
36
- false
33
+ super || (request.post? && validation_errors?(webhook))
37
34
  end
38
35
 
39
36
  def to_json
@@ -15,6 +15,7 @@ module PactBroker
15
15
  module Api
16
16
  module Resources
17
17
  class InvalidJsonError < PactBroker::Error ; end
18
+ class NonUTF8CharacterFound < PactBroker::Error ; end
18
19
 
19
20
  class BaseResource < Webmachine::Resource
20
21
  include PactBroker::Services
@@ -39,6 +40,10 @@ module PactBroker
39
40
  super + ["PATCH"]
40
41
  end
41
42
 
43
+ def malformed_request?
44
+ content_type_is_json_but_invalid_json_provided?
45
+ end
46
+
42
47
  def finish_request
43
48
  application_context.after_resource&.call(self)
44
49
  PactBroker.configuration.after_resource.call(self)
@@ -125,17 +130,32 @@ module PactBroker
125
130
  else
126
131
  @params_with_string_keys ||= JSON.parse(request_body, { symbolize_names: false }.merge(PACT_PARSING_OPTIONS)) #Not load! Otherwise it will try to load Ruby classes.
127
132
  end
128
- rescue JSON::JSONError => e
129
- raise InvalidJsonError.new("Error parsing JSON - #{e.message}")
133
+ rescue StandardError => e
134
+ fragment = fragment_before_invalid_utf_8_char
135
+
136
+ if fragment
137
+ raise NonUTF8CharacterFound.new(message("errors.non_utf_8_char_in_request_body", char_number: fragment.length + 1, fragment: fragment))
138
+ else
139
+ raise InvalidJsonError.new(e.message)
140
+ end
130
141
  end
131
142
  # rubocop: enable Metrics/CyclomaticComplexity
132
143
 
144
+ def fragment_before_invalid_utf_8_char
145
+ request_body.each_char.with_index do | char, index |
146
+ if !char.valid_encoding?
147
+ return index < 100 ? request_body[0...index] : request_body[index-100...index]
148
+ end
149
+ end
150
+ nil
151
+ end
152
+
133
153
  def params_with_string_keys
134
154
  params(symbolize_names: false)
135
155
  end
136
156
 
137
157
  def pact_params
138
- @pact_params ||= PactBroker::Pacts::PactParams.from_request request, identifier_from_path
158
+ @pact_params ||= PactBroker::Pacts::PactParams.from_request(request, identifier_from_path)
139
159
  end
140
160
 
141
161
  def set_json_error_message message
@@ -192,9 +212,15 @@ module PactBroker
192
212
  begin
193
213
  params
194
214
  false
215
+ rescue NonUTF8CharacterFound => e
216
+ logger.info(e.message) # Don't use the default SemanticLogger error logging method because it will try and print out the cause which will contain non UTF-8 chars in the message
217
+ set_json_error_message(e.message)
218
+ response.headers["Content-Type"] = "application/hal+json;charset=utf-8"
219
+ true
195
220
  rescue StandardError => e
196
- logger.info "Error parsing JSON #{e} - #{request_body}"
197
- set_json_error_message "Error parsing JSON - #{e.message}"
221
+ message = "#{e.cause ? e.cause.class.name : e.class.name} - #{e.message}"
222
+ logger.info(message)
223
+ set_json_error_message(message)
198
224
  response.headers["Content-Type"] = "application/hal+json;charset=utf-8"
199
225
  true
200
226
  end
@@ -279,6 +305,33 @@ module PactBroker
279
305
  def malformed_request_for_json_with_schema?(schema_to_use = schema, params_to_validate = params)
280
306
  invalid_json? || validation_errors_for_schema?(schema_to_use, params_to_validate)
281
307
  end
308
+
309
+ # Ensure we have valid JSON if a JSON body is required OR if a body has been provided
310
+ def content_type_is_json_but_invalid_json_provided?
311
+ content_type_json? && ((request_body_required? || any_request_body?) && invalid_json?)
312
+ end
313
+
314
+ def content_type_json?
315
+ request.content_type&.include?("json")
316
+ end
317
+
318
+ def request_body_required?
319
+ false
320
+ end
321
+
322
+ # TODO rename to put_to_create_supported, otherwise it sounds like it's a policy issue
323
+ # Not a Webmachine method. This is used by security policy code to identify whether
324
+ # a PUT to a non existing resource can create a new object.
325
+ def put_can_create?
326
+ false
327
+ end
328
+
329
+ # TODO rename to patch_to_create_supported, otherwise it sounds like it's a policy issue
330
+ # Not a Webmachine method. This is used by security policy code to identify whether
331
+ # a PATCH to a non existing resource can create a new object.
332
+ def patch_can_create?
333
+ false
334
+ end
282
335
  end
283
336
  end
284
337
  end
@@ -14,7 +14,11 @@ module PactBroker
14
14
  end
15
15
 
16
16
  def allowed_methods
17
- ["GET", "PUT", "OPTIONS"]
17
+ ["GET", "PUT", "DELETE", "OPTIONS"]
18
+ end
19
+
20
+ def put_can_create?
21
+ true
18
22
  end
19
23
 
20
24
  def resource_exists?
@@ -25,6 +29,11 @@ module PactBroker
25
29
  decorator_class(:branch_version_decorator).new(branch_version).to_json(decorator_options)
26
30
  end
27
31
 
32
+ def delete_resource
33
+ branch_service.delete_branch_version(branch_version)
34
+ true
35
+ end
36
+
28
37
  def from_json
29
38
  already_existed = !!branch_version
30
39
  @branch_version = branch_service.find_or_create_branch_version(identifier_from_path)
@@ -2,14 +2,12 @@ require "pact_broker/api/resources/base_resource"
2
2
  require "pact_broker/db/clean"
3
3
  require "pact_broker/matrix/unresolved_selector"
4
4
 
5
+ # Not exposed yet as we'd need to support administrator auth first
6
+
5
7
  module PactBroker
6
8
  module Api
7
9
  module Resources
8
10
  class Clean < BaseResource
9
- def content_types_accepted
10
- [["application/json"]]
11
- end
12
-
13
11
  def content_types_provided
14
12
  [["application/hal+json"]]
15
13
  end
@@ -19,12 +17,16 @@ module PactBroker
19
17
  end
20
18
 
21
19
  def process_post
22
- keep_selectors = (params[:keep] || []).collect do | hash |
23
- PactBroker::Matrix::UnresolvedSelector.new(hash)
24
- end
20
+ if content_type_json?
21
+ keep_selectors = (params[:keep] || []).collect do | hash |
22
+ PactBroker::Matrix::UnresolvedSelector.new(hash)
23
+ end
25
24
 
26
- result = PactBroker::DB::Clean.call(Sequel::Model.db, { keep: keep_selectors })
27
- response.body = result.to_json
25
+ result = PactBroker::DB::Clean.call(Sequel::Model.db, { keep: keep_selectors })
26
+ response.body = result.to_json
27
+ else
28
+ 415
29
+ end
28
30
  end
29
31
 
30
32
  def policy_name
@@ -8,10 +8,6 @@ module PactBroker
8
8
  class CurrentlyDeployedVersionsForEnvironment < BaseResource
9
9
  using PactBroker::StringRefinements
10
10
 
11
- def content_types_accepted
12
- [["application/json", :from_json]]
13
- end
14
-
15
11
  def content_types_provided
16
12
  [["application/hal+json", :to_json]]
17
13
  end
@@ -8,10 +8,6 @@ module PactBroker
8
8
  class CurrentlySupportedVersionsForEnvironment < BaseResource
9
9
  using PactBroker::StringRefinements
10
10
 
11
- def content_types_accepted
12
- [["application/json", :from_json]]
13
- end
14
-
15
11
  def content_types_provided
16
12
  [["application/hal+json", :to_json]]
17
13
  end
@@ -8,11 +8,6 @@ module PactBroker
8
8
  class DeployedVersion < BaseResource
9
9
  include PactBroker::Messages
10
10
 
11
- def initialize
12
- super
13
- @currently_deployed_param = params(default: {})[:currentlyDeployed]
14
- end
15
-
16
11
  def content_types_provided
17
12
  [
18
13
  ["application/hal+json", :to_json]
@@ -29,16 +24,12 @@ module PactBroker
29
24
  ["GET", "PATCH", "OPTIONS"]
30
25
  end
31
26
 
32
- def resource_exists?
33
- !!deployed_version
27
+ def patch_can_create?
28
+ false
34
29
  end
35
30
 
36
- def malformed_request?
37
- if request.patch?
38
- return invalid_json?
39
- else
40
- false
41
- end
31
+ def resource_exists?
32
+ !!deployed_version
42
33
  end
43
34
 
44
35
  def to_json
@@ -58,16 +49,29 @@ module PactBroker
58
49
  end
59
50
 
60
51
  def policy_name
61
- :'versions::version'
52
+ :'versions::deployed_version'
53
+ end
54
+
55
+ def policy_record_context
56
+ {
57
+ pacticipant: deployed_version&.pacticipant
58
+ }
62
59
  end
63
60
 
64
61
  def policy_record
65
- deployed_version&.version
62
+ deployed_version&.environment
66
63
  end
67
64
 
68
65
  private
69
66
 
70
- attr_reader :currently_deployed_param
67
+ # can't use ||= with a potentially nil value
68
+ def currently_deployed_param
69
+ if defined?(@currently_deployed_param)
70
+ @currently_deployed_param
71
+ else
72
+ @currently_deployed_param = params(default: {})[:currentlyDeployed]
73
+ end
74
+ end
71
75
 
72
76
  def process_currently_deployed_param
73
77
  if currently_deployed_param == false
@@ -40,7 +40,7 @@ module PactBroker
40
40
  end
41
41
 
42
42
  def policy_name
43
- :'versions::deployed_version'
43
+ :'versions::deployed_versions'
44
44
  end
45
45
 
46
46
  def policy_record
@@ -17,16 +17,16 @@ module PactBroker
17
17
  ["GET", "PUT", "DELETE", "OPTIONS"]
18
18
  end
19
19
 
20
+ def put_can_create?
21
+ false
22
+ end
23
+
20
24
  def resource_exists?
21
25
  !!environment
22
26
  end
23
27
 
24
28
  def malformed_request?
25
- if request.put? && environment
26
- invalid_json? || validation_errors_for_schema?(schema, params.merge(uuid: uuid))
27
- else
28
- false
29
- end
29
+ super || (request.put? && environment && validation_errors_for_schema?(schema, params.merge(uuid: uuid)))
30
30
  end
31
31
 
32
32
  def from_json