pact_broker 2.58.0 → 2.59.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release_gem.yml +45 -0
  3. data/.travis.yml +5 -3
  4. data/CHANGELOG.md +37 -0
  5. data/DEVELOPER_SETUP.md +46 -16
  6. data/Dockerfile +23 -2
  7. data/Gemfile +29 -2
  8. data/config.ru +32 -12
  9. data/config/database.yml +7 -0
  10. data/docker-compose-dev-postgres.yml +35 -0
  11. data/docker-compose-test.yml +100 -0
  12. data/lib/pact_broker/api/decorators/decorator_context.rb +1 -1
  13. data/lib/pact_broker/api/decorators/version_decorator.rb +2 -0
  14. data/lib/pact_broker/api/pact_broker_urls.rb +4 -4
  15. data/lib/pact_broker/api/paths.rb +15 -0
  16. data/lib/pact_broker/api/renderers/html_pact_renderer.rb +26 -8
  17. data/lib/pact_broker/api/resources/all_webhooks.rb +12 -3
  18. data/lib/pact_broker/api/resources/badge.rb +4 -0
  19. data/lib/pact_broker/api/resources/base_resource.rb +2 -174
  20. data/lib/pact_broker/api/resources/can_i_deploy.rb +8 -0
  21. data/lib/pact_broker/api/resources/dashboard.rb +4 -0
  22. data/lib/pact_broker/api/resources/default_base_resource.rb +211 -0
  23. data/lib/pact_broker/api/resources/error_handler.rb +3 -5
  24. data/lib/pact_broker/api/resources/group.rb +7 -11
  25. data/lib/pact_broker/api/resources/index.rb +4 -0
  26. data/lib/pact_broker/api/resources/integration.rb +12 -0
  27. data/lib/pact_broker/api/resources/integrations.rb +9 -1
  28. data/lib/pact_broker/api/resources/label.rb +16 -6
  29. data/lib/pact_broker/api/resources/latest_pact.rb +8 -0
  30. data/lib/pact_broker/api/resources/latest_pacts.rb +9 -4
  31. data/lib/pact_broker/api/resources/latest_verifications_for_consumer_version.rb +16 -2
  32. data/lib/pact_broker/api/resources/matrix.rb +2 -2
  33. data/lib/pact_broker/api/resources/matrix_for_consumer_and_provider.rb +1 -1
  34. data/lib/pact_broker/api/resources/pact.rb +8 -0
  35. data/lib/pact_broker/api/resources/pact_content_diff.rb +1 -2
  36. data/lib/pact_broker/api/resources/pact_triggered_webhooks.rb +1 -7
  37. data/lib/pact_broker/api/resources/pact_versions.rb +1 -3
  38. data/lib/pact_broker/api/resources/pact_webhooks.rb +2 -5
  39. data/lib/pact_broker/api/resources/pact_webhooks_status.rb +1 -17
  40. data/lib/pact_broker/api/resources/pacticipant.rb +11 -14
  41. data/lib/pact_broker/api/resources/pacticipants.rb +15 -1
  42. data/lib/pact_broker/api/resources/pacticipants_for_label.rb +1 -1
  43. data/lib/pact_broker/api/resources/previous_distinct_pact_version.rb +8 -1
  44. data/lib/pact_broker/api/resources/provider_pacts.rb +9 -1
  45. data/lib/pact_broker/api/resources/provider_pacts_for_verification.rb +1 -1
  46. data/lib/pact_broker/api/resources/tag.rb +5 -1
  47. data/lib/pact_broker/api/resources/tagged_pact_versions.rb +1 -2
  48. data/lib/pact_broker/api/resources/triggered_webhook_logs.rb +4 -0
  49. data/lib/pact_broker/api/resources/verification.rb +7 -3
  50. data/lib/pact_broker/api/resources/verification_triggered_webhooks.rb +5 -1
  51. data/lib/pact_broker/api/resources/verifications.rb +7 -3
  52. data/lib/pact_broker/api/resources/version.rb +4 -1
  53. data/lib/pact_broker/api/resources/versions.rb +1 -3
  54. data/lib/pact_broker/api/resources/webhook.rb +6 -2
  55. data/lib/pact_broker/api/resources/webhook_execution.rb +4 -0
  56. data/lib/pact_broker/api/resources/webhooks.rb +3 -18
  57. data/lib/pact_broker/app.rb +1 -0
  58. data/lib/pact_broker/badges/service.rb +3 -2
  59. data/lib/pact_broker/configuration.rb +21 -2
  60. data/lib/pact_broker/db/clean.rb +0 -6
  61. data/lib/pact_broker/doc/views/layouts/main.haml +1 -1
  62. data/lib/pact_broker/domain/pacticipant.rb +7 -2
  63. data/lib/pact_broker/domain/version.rb +3 -0
  64. data/lib/pact_broker/groups/service.rb +1 -1
  65. data/lib/pact_broker/index/service.rb +9 -43
  66. data/lib/pact_broker/matrix/deployment_status_summary.rb +1 -1
  67. data/lib/pact_broker/pacticipants/repository.rb +2 -2
  68. data/lib/pact_broker/pacts/content.rb +2 -1
  69. data/lib/pact_broker/pacts/latest_pact_publication_id_for_consumer_version.rb +2 -5
  70. data/lib/pact_broker/pacts/pact_publication.rb +11 -9
  71. data/lib/pact_broker/pacts/pact_version.rb +3 -4
  72. data/lib/pact_broker/pacts/repository.rb +53 -39
  73. data/lib/pact_broker/pacts/verifiable_pact.rb +8 -0
  74. data/lib/pact_broker/policies.rb +53 -0
  75. data/lib/pact_broker/repositories/helpers.rb +3 -20
  76. data/lib/pact_broker/test/test_data_builder.rb +4 -4
  77. data/lib/pact_broker/ui/controllers/clusters.rb +1 -1
  78. data/lib/pact_broker/ui/controllers/groups.rb +2 -2
  79. data/lib/pact_broker/ui/controllers/index.rb +1 -3
  80. data/lib/pact_broker/ui/controllers/matrix.rb +2 -2
  81. data/lib/pact_broker/ui/views/groups/show.html.erb +3 -3
  82. data/lib/pact_broker/ui/views/index/show-with-tags.haml +10 -10
  83. data/lib/pact_broker/ui/views/index/show.haml +6 -6
  84. data/lib/pact_broker/ui/views/layouts/main.haml +1 -1
  85. data/lib/pact_broker/ui/views/matrix/show.haml +4 -5
  86. data/lib/pact_broker/verifications/latest_verification_id_for_pact_version_and_provider_version.rb +3 -5
  87. data/lib/pact_broker/version.rb +1 -1
  88. data/lib/pact_broker/versions/repository.rb +2 -4
  89. data/lib/rack/pact_broker/request_target.rb +6 -1
  90. data/lib/sequel/plugins/insert_ignore.rb +69 -0
  91. data/lib/sequel/plugins/upsert.rb +103 -0
  92. data/pact_broker.gemspec +22 -41
  93. data/public/javascripts/pact.js +6 -2
  94. data/script/docker/db-execute-sql-file.sh +2 -0
  95. data/script/issues/consumer-version-selectors-docs/issue-text.txt +11 -0
  96. data/script/issues/consumer-version-selectors-docs/issues.txt +6 -0
  97. data/script/issues/consumer-version-selectors-docs/raise-issue-in-client-repos.sh +10 -0
  98. data/script/prod/redact-data.sql +1 -0
  99. data/script/release-via-github-action.sh +7 -0
  100. data/script/seed.rb +5 -7
  101. data/script/trigger-release.sh +30 -0
  102. data/spec/features/get_versions_spec.rb +1 -6
  103. data/spec/lib/pact_broker/api/decorators/version_decorator_spec.rb +4 -0
  104. data/spec/lib/pact_broker/api/pact_broker_urls_spec.rb +2 -2
  105. data/spec/lib/pact_broker/api/renderers/html_pact_renderer_spec.rb +24 -6
  106. data/spec/lib/pact_broker/api/resources/all_webhooks_spec.rb +1 -1
  107. data/spec/lib/pact_broker/api/resources/default_base_resource_spec.rb +158 -0
  108. data/spec/lib/pact_broker/api/resources/error_handler_spec.rb +18 -1
  109. data/spec/lib/pact_broker/api/resources/tag_spec.rb +2 -2
  110. data/spec/lib/pact_broker/api/resources/webhook_spec.rb +1 -1
  111. data/spec/lib/pact_broker/api/resources/webhooks_spec.rb +1 -1
  112. data/spec/lib/pact_broker/badges/service_spec.rb +6 -6
  113. data/spec/lib/pact_broker/db/clean_old_spec.rb +125 -0
  114. data/spec/lib/pact_broker/db/clean_spec.rb +45 -11
  115. data/spec/lib/pact_broker/index/service_spec.rb +2 -3
  116. data/spec/lib/pact_broker/pacts/content_spec.rb +8 -0
  117. data/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_spec.rb +36 -0
  118. data/spec/lib/pact_broker/versions/repository_spec.rb +8 -0
  119. data/spec/lib/rack/pact_broker/request_target_spec.rb +7 -0
  120. data/spec/lib/sequel/plugins/insert_ignore_spec.rb +82 -0
  121. data/spec/lib/sequel/plugins/upsert_spec.rb +125 -0
  122. data/spec/spec_helper.rb +1 -0
  123. data/tasks/audit.rake +6 -2
  124. data/tasks/development.rake +2 -2
  125. metadata +46 -284
  126. data/spec/lib/pact_broker/api/resources/base_resource_spec.rb +0 -78
@@ -22,6 +22,14 @@ module PactBroker
22
22
  def wip?
23
23
  wip
24
24
  end
25
+
26
+ def <=> other
27
+ if self.consumer_name != other.consumer_name
28
+ return self.consumer_name <=> other.consumer_name
29
+ else
30
+ return self.consumer_version.order <=> other.consumer_version.order
31
+ end
32
+ end
25
33
  end
26
34
  end
27
35
  end
@@ -0,0 +1,53 @@
1
+ require 'pact_broker/configuration'
2
+
3
+ module PactBroker
4
+ class DefaultPolicy
5
+ def initialize(current_user, resource)
6
+ @current_user = current_user
7
+ @resource = resource
8
+ end
9
+
10
+ def update?
11
+ true
12
+ end
13
+
14
+ def delete?
15
+ true
16
+ end
17
+
18
+ def create?
19
+ true
20
+ end
21
+
22
+ def view?
23
+ true
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :current_user, :resource
29
+
30
+ class Scope
31
+ def initialize(user, scope)
32
+ @user = user
33
+ @scope = scope
34
+ end
35
+
36
+ def resolve
37
+ scope
38
+ end
39
+
40
+ private
41
+
42
+ attr_reader :user, :scope
43
+ end
44
+ end
45
+
46
+ def self.policy!(*args)
47
+ PactBroker.configuration.policy_builder.call(*args)
48
+ end
49
+
50
+ def self.policy_scope!(*args)
51
+ PactBroker.configuration.policy_scope_builder.call(*args)
52
+ end
53
+ end
@@ -44,30 +44,13 @@ module PactBroker
44
44
  end
45
45
 
46
46
  def select_for_subquery column
47
- if mysql? #stoopid mysql doesn't allow subqueries
48
- select(column).collect{ | it | it[column] }
47
+ if mysql? #stoopid mysql doesn't allow you to modify datasets with subqueries
48
+ column_name = column.respond_to?(:alias) ? column.alias : column
49
+ select(column).collect{ | it | it[column_name] }
49
50
  else
50
51
  select(column)
51
52
  end
52
53
  end
53
-
54
- def upsert row, unique_key_names, columns_to_update = nil
55
- if postgres?
56
- insert_conflict(update: row, target: unique_key_names).insert(row)
57
- elsif mysql?
58
- update_cols = columns_to_update || (row.keys - unique_key_names)
59
- on_duplicate_key_update(*update_cols).insert(row)
60
- else
61
- # Sqlite
62
- key = row.reject{ |k, v| !unique_key_names.include?(k) }
63
- if where(key).count == 0
64
- insert(row)
65
- else
66
- where(key).update(row)
67
- end
68
- end
69
- model.where(row.select{ |key, _| unique_key_names.include?(key)}).single_record
70
- end
71
54
  end
72
55
  end
73
56
  end
@@ -73,14 +73,14 @@ module PactBroker
73
73
  self
74
74
  end
75
75
 
76
- def create_pact_with_hierarchy consumer_name = "Consumer", consumer_version_number = "1.2.3", provider_name = "Provider", json_content = default_json_content
76
+ def create_pact_with_hierarchy consumer_name = "Consumer", consumer_version_number = "1.2.3", provider_name = "Provider", json_content = nil
77
77
  use_consumer(consumer_name)
78
78
  create_consumer(consumer_name) if !consumer
79
79
  use_provider(provider_name)
80
80
  create_provider provider_name if !provider
81
81
  use_consumer_version(consumer_version_number)
82
82
  create_consumer_version(consumer_version_number) if !consumer_version
83
- create_pact json_content: json_content
83
+ create_pact json_content: json_content || default_json_content
84
84
  self
85
85
  end
86
86
 
@@ -436,10 +436,10 @@ module PactBroker
436
436
  def default_json_content
437
437
  {
438
438
  "consumer" => {
439
- "name" => "Condor"
439
+ "name" => consumer.name
440
440
  },
441
441
  "provider" => {
442
- "name" => "Pricing Service"
442
+ "name" => provider.name
443
443
  },
444
444
  "interactions" => [],
445
445
  "random" => rand
@@ -19,7 +19,7 @@ module PactBroker
19
19
 
20
20
  get "/" do
21
21
  view_model = ViewDomain::IndexItems.new(pacticipant_service.find_index_items, base_url: base_url)
22
- haml 'clusters/show', locals: {relationships: view_model, base_url: base_url}
22
+ haml 'clusters/show', locals: { relationships: view_model, base_url: base_url }, escape_html: true
23
23
  end
24
24
 
25
25
  end
@@ -13,13 +13,13 @@ module PactBroker
13
13
  pacticipant = pacticipant_service.find_pacticipant_by_name(params[:name])
14
14
  erb :'groups/show.html', {
15
15
  locals: {
16
- csv_path: "#{base_url}/groups/#{params[:name]}.csv",
16
+ csv_path: "#{base_url}/groups/#{ERB::Util.url_encode(params[:name])}.csv",
17
17
  pacticipant_name: params[:name],
18
18
  repository_url: pacticipant&.repository_url,
19
19
  base_url: base_url
20
20
  }
21
21
  }, {
22
- layout: 'layouts/main'
22
+ layout: 'layouts/main',
23
23
  }
24
24
  end
25
25
 
@@ -23,8 +23,6 @@ module PactBroker
23
23
  page_size: page_size
24
24
  }
25
25
 
26
- # TODO remove this code when verified
27
- options[:optimised] = true unless params[:optimised] == 'false'
28
26
  index_items = ViewDomain::IndexItems.new(index_service.find_index_items(options), base_url: base_url)
29
27
 
30
28
  page = tags ? :'index/show-with-tags' : :'index/show'
@@ -38,7 +36,7 @@ module PactBroker
38
36
  base_url: base_url
39
37
  }
40
38
 
41
- haml page, {locals: locals, layout: :'layouts/main'}
39
+ haml page, { locals: locals, layout: :'layouts/main', escape_html: true }
42
40
  end
43
41
 
44
42
  def set_headers
@@ -42,7 +42,7 @@ module PactBroker
42
42
  Padrino.logger.exception(e) unless e.is_a?(PactBroker::Error)
43
43
  locals[:errors] = [e.message]
44
44
  end
45
- haml :'matrix/show', {locals: locals, layout: :'layouts/main'}
45
+ haml :'matrix/show', { locals: locals, layout: :'layouts/main', escape_html: true }
46
46
  end
47
47
 
48
48
  get "/provider/:provider_name/consumer/:consumer_name" do
@@ -60,7 +60,7 @@ module PactBroker
60
60
  badge_url: nil,
61
61
  base_url: base_url
62
62
  }
63
- haml :'matrix/show', {locals: locals, layout: :'layouts/main'}
63
+ haml :'matrix/show', { locals: locals, layout: :'layouts/main', escape_html: true }
64
64
  end
65
65
 
66
66
  def create_selector_objects(selector_hashes)
@@ -31,13 +31,13 @@ body{
31
31
  <!-- developed by Duncan Alexander - hypothete.com -->
32
32
  </head>
33
33
  <body>
34
- <h1>Network graph of <%= pacticipant_name %> relationships</h1>
34
+ <h1>Network graph of <%= escape_html(pacticipant_name) %> relationships</h1>
35
35
 
36
36
  <% if repository_url %>
37
37
  <p>Repository URL:
38
38
 
39
39
  <%
40
- repository_link = "<a href='#{repository_url}'>#{escape_html(repository_url)}</a>"
40
+ repository_link = "<a href=\"#{repository_url}\">#{repository_url}</a>"
41
41
  %>
42
42
 
43
43
  <%= Sanitize.fragment(repository_link, Sanitize::Config::BASIC) %>
@@ -105,7 +105,7 @@ var relationshipPath = function(relationship, nodeLocations, pacticipants) {
105
105
 
106
106
  var latestPactUrl = function (consumerName, providerName) {
107
107
  //TODO send this with the relationship data
108
- return '<%= base_url %>/pacts/provider/' + providerName + '/consumer/' + consumerName + '/latest';
108
+ return '<%= base_url %>' + '/pacts/provider/' + encodeURIComponent(providerName) + '/consumer/' + encodeURIComponent(consumerName) + '/latest';
109
109
  };
110
110
 
111
111
  var relationshipData = function(pacticipant, relationships) {
@@ -1,9 +1,9 @@
1
1
  %body
2
- = render :haml, :'index/_css_and_js', :layout => false
2
+ != render :haml, :'index/_css_and_js', :layout => false
3
3
  .container
4
- = render :haml, :'index/_navbar', :layout => false, locals: {tag_toggle: false, base_url: base_url}
4
+ != render :haml, :'index/_navbar', :layout => false, locals: {tag_toggle: false, base_url: base_url}
5
5
  - if index_items.empty?
6
- = render :haml, :'index/_getting-started', :layout => false
6
+ != render :haml, :'index/_getting-started', :layout => false
7
7
  %h1.page-header
8
8
  Pacts
9
9
  %table.table.table-bordered.table-striped{ id: 'relationships' }
@@ -37,10 +37,10 @@
37
37
  %tr{'data-pact-versions-url': index_item.pact_versions_url, 'data-consumer-name': index_item.consumer_name, 'data-provider-name': index_item.provider_name, 'data-integration-url': index_item.integration_url}
38
38
  %td.consumer
39
39
  %a{:href => index_item.consumer_group_url }
40
- = escape_html(index_item.consumer_name)
40
+ = index_item.consumer_name
41
41
  %td.consumer-version-number{"data-text": index_item.consumer_version_order}
42
42
  %div.clippable
43
- = escape_html(index_item.consumer_version_number)
43
+ = index_item.consumer_version_number
44
44
  - if index_item.consumer_version_number
45
45
  %button.clippy.invisible{ title: "Copy to clipboard" }
46
46
  %span.glyphicon.glyphicon-copy
@@ -49,7 +49,7 @@
49
49
  latest
50
50
  - index_item.consumer_version_latest_tag_names.each do | tag_name |
51
51
  .tag.label.label-primary
52
- = escape_html(tag_name)
52
+ = tag_name
53
53
  %td.pact
54
54
  %span.pact
55
55
  %a{ href: index_item.pact_url, title: "View pact" }
@@ -57,16 +57,16 @@
57
57
  %a{ href: index_item.pact_matrix_url, title: "View pact matrix" }
58
58
  %td.provider
59
59
  %a{ href: index_item.provider_group_url }
60
- = escape_html(index_item.provider_name)
60
+ = index_item.provider_name
61
61
  %td.provider-version-number
62
62
  %div.clippable
63
- = escape_html(index_item.provider_version_number)
63
+ = index_item.provider_version_number
64
64
  - if index_item.provider_version_number
65
65
  %button.clippy.invisible{ title: "Copy to clipboard" }
66
66
  %span.glyphicon.glyphicon-copy
67
67
  - index_item.provider_version_latest_tag_names.each do | tag_name |
68
68
  .tag.label.label-primary
69
- = escape_html(tag_name)
69
+ = tag_name
70
70
  %td{"data-text": index_item.publication_date_of_latest_pact_order}
71
71
  = index_item.publication_date_of_latest_pact.gsub("about ", "")
72
72
  %td{ class: index_item.webhook_status }
@@ -86,7 +86,7 @@
86
86
  %div.pagination
87
87
 
88
88
  - pagination_locals = { page_number: page_number, page_size: page_size, pagination_record_count: pagination_record_count, current_page_size: current_page_size }
89
- = render :haml, :'index/_pagination', :layout => false, locals: pagination_locals
89
+ != render :haml, :'index/_pagination', :layout => false, locals: pagination_locals
90
90
 
91
91
  :javascript
92
92
  $(function(){
@@ -1,9 +1,9 @@
1
1
  %body
2
- = render :haml, :'index/_css_and_js', :layout => false
2
+ != render :haml, :'index/_css_and_js', :layout => false
3
3
  .container
4
- = render :haml, :'index/_navbar', :layout => false, locals: {tag_toggle: true, base_url: base_url}
4
+ != render :haml, :'index/_navbar', :layout => false, locals: {tag_toggle: true, base_url: base_url}
5
5
  - if index_items.empty?
6
- = render :haml, :'index/_getting-started', :layout => false
6
+ != render :haml, :'index/_getting-started', :layout => false
7
7
  %h1.page-header
8
8
  Pacts
9
9
  %table.table.table-bordered.table-striped{ id: 'relationships' }
@@ -32,7 +32,7 @@
32
32
  %td
33
33
  %td.consumer
34
34
  %a{ href: index_item.consumer_group_url }
35
- = escape_html(index_item.consumer_name)
35
+ = index_item.consumer_name
36
36
  %td.pact
37
37
  %span.pact
38
38
  %a{ href: index_item.latest_pact_url, :title => "View pact" }
@@ -40,7 +40,7 @@
40
40
  %a{ href: index_item.pact_matrix_url, title: "View pact matrix" }
41
41
  %td.provider
42
42
  %a{ href: index_item.provider_group_url }
43
- = escape_html(index_item.provider_name)
43
+ = index_item.provider_name
44
44
  %td
45
45
  %td{"data-text": index_item.publication_date_of_latest_pact_order}
46
46
  = index_item.publication_date_of_latest_pact
@@ -58,7 +58,7 @@
58
58
  %div.pagination
59
59
 
60
60
  - pagination_locals = { page_number: page_number, page_size: page_size, pagination_record_count: pagination_record_count, current_page_size: current_page_size }
61
- = render :haml, :'index/_pagination', :layout => false, locals: pagination_locals
61
+ != render :haml, :'index/_pagination', :layout => false, locals: pagination_locals
62
62
 
63
63
 
64
64
  :javascript
@@ -6,4 +6,4 @@
6
6
  %link{rel: 'stylesheet', href: "#{base_url}/css/bootstrap.min.css"}
7
7
  %script{type: 'text/javascript', src:"#{base_url}/javascripts/jquery-3.3.1.min.js"}
8
8
  %script{type: 'text/javascript', src:"#{base_url}/js/bootstrap.min.js"}
9
- = yield
9
+ != yield
@@ -20,15 +20,14 @@
20
20
  - if defined?(errors) && errors.any?
21
21
  - errors.each do | error |
22
22
  %div.alert.alert-danger
23
- = escape_html(error)
24
-
23
+ = error
25
24
 
26
25
  %form{action: "#{base_url}/matrix", onsubmit:'return onSubmit()'}
27
26
  - selectors.each_with_index do | selector, index |
28
27
  .selector
29
28
  %label{for: "pacticipant#{index}"}
30
29
  Pacticipant name
31
- %input{name: 'q[]pacticipant', id: "pacticipant1#{index}", class: 'pacticipant_name', value: escape_html(selector.pacticipant_name)}
30
+ %input{name: 'q[]pacticipant', id: "pacticipant1#{index}", class: 'pacticipant_name', value: selector.pacticipant_name}
32
31
 
33
32
  .input-group
34
33
 
@@ -45,9 +44,9 @@
45
44
  %option{ value: 'specify-all-tagged', selected: selector.specify_all_tagged }
46
45
  All versions with tag...
47
46
 
48
- %input{name: 'q[]version', type: 'text', id: "pacticipant#{index}_version", class: 'version', value: escape_html(selector.pacticipant_version_number)}
47
+ %input{name: 'q[]version', type: 'text', id: "pacticipant#{index}_version", class: 'version', value: selector.pacticipant_version_number}
49
48
 
50
- %input{name: 'q[]tag', type: 'text', id: "pacticipant#{index}_tag", class: 'tag', value: escape_html(selector.tag)}
49
+ %input{name: 'q[]tag', type: 'text', id: "pacticipant#{index}_tag", class: 'tag', value: selector.tag}
51
50
 
52
51
  %input{name: 'q[]latest', value: 'true', hidden: true, class: 'latest-flag'}
53
52
 
@@ -3,16 +3,14 @@ require 'pact_broker/domain/verification'
3
3
  module PactBroker
4
4
  module Verifications
5
5
  class LatestVerificationIdForPactVersionAndProviderVersion < Sequel::Model(:latest_verification_id_for_pact_version_and_provider_version)
6
-
7
6
  unrestrict_primary_key
7
+ set_primary_key [:pact_version_id, :provider_version_id]
8
+
9
+ plugin :upsert, identifying_columns: [:pact_version_id, :provider_version_id]
8
10
 
9
11
  dataset_module do
10
12
  include PactBroker::Repositories::Helpers
11
13
  end
12
-
13
- def upsert
14
- self.class.upsert(to_hash, [:pact_version_id, :provider_version_id])
15
- end
16
14
  end
17
15
  end
18
16
  end
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '2.58.0'
2
+ VERSION = '2.59.2'
3
3
  end
@@ -56,10 +56,8 @@ module PactBroker
56
56
  created_at: Sequel.datetime_class.now,
57
57
  updated_at: Sequel.datetime_class.now
58
58
  }
59
- id = PactBroker::Domain::Version.dataset.insert_ignore.insert(version_params)
60
- version = PactBroker::Domain::Version.find(number: args[:number], pacticipant_id: args[:pacticipant_id])
61
- PactBroker::Domain::OrderVersions.(version)
62
- version.refresh # reload with the populated order
59
+
60
+ PactBroker::Domain::Version.new(version_params).insert_ignore
63
61
  end
64
62
 
65
63
  def find_by_pacticipant_id_and_number_or_create pacticipant_id, number
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require 'pact_broker/api/paths'
2
3
 
3
4
  module Rack
4
5
  module PactBroker
@@ -13,7 +14,7 @@ module Rack
13
14
  end
14
15
 
15
16
  def request_for_api?(env)
16
- explicit_request_for_api(env) || no_accept_header(env) || (accept_all(env) && !is_web_extension(env))
17
+ explicit_request_for_api(env) || no_accept_header(env) || is_badge_request?(env) || (accept_all(env) && !is_web_extension(env))
17
18
  end
18
19
 
19
20
  private
@@ -38,6 +39,10 @@ module Rack
38
39
  API_CONTENT_TYPES.any?{ |content_type| header.include?(content_type) }
39
40
  end
40
41
 
42
+ def is_badge_request?(env)
43
+ env['HTTP_ACCEPT'].include?('svg') && ::PactBroker::Api::Paths.is_badge_path?(env['PATH_INFO'])
44
+ end
45
+
41
46
  # default curl Accept header
42
47
  # Also used by browsers to request various web assets like woff files
43
48
  def accept_all(env)
@@ -0,0 +1,69 @@
1
+ # This plugin can be included into a Sequel model to allow the original record
2
+ # to be loaded into the model if a duplicate is inserted.
3
+ # This is to handle race conditions when two requests come in in parallel to create
4
+ # the same resource.
5
+
6
+ # Rather than re-writing the whole save method and all the hooks and validation logic in it,
7
+ # it naughtily overrides the private _insert_dataset.
8
+
9
+ module Sequel
10
+ module Plugins
11
+ module InsertIgnore
12
+ def self.configure(model, opts=OPTS)
13
+ model.instance_exec do
14
+ @insert_ignore_plugin_identifying_columns = opts.fetch(:identifying_columns)
15
+ end
16
+ end
17
+
18
+ module ClassMethods
19
+ attr_reader :insert_ignore_plugin_identifying_columns
20
+ end
21
+
22
+ module InstanceMethods
23
+ def insert_ignore(opts = {})
24
+ save(opts)
25
+ load_values_from_previously_inserted_object unless id
26
+ self
27
+ rescue Sequel::NoExistingObject
28
+ load_values_from_previously_inserted_object
29
+ end
30
+
31
+ private
32
+
33
+ def load_values_from_previously_inserted_object
34
+ set_primary_key_columns_from_previously_inserted_object
35
+ refresh
36
+ end
37
+
38
+ def set_primary_key_columns_from_previously_inserted_object
39
+ if !primary_key_columns_are_same_as_identifying_columns
40
+ existing_record = find_previously_inserted_object
41
+ insert_ignore_primary_key_columns.each do | column |
42
+ self.send("#{column}=".to_sym, existing_record[column])
43
+ end
44
+ end
45
+ end
46
+
47
+ def find_previously_inserted_object
48
+ query = self.class.insert_ignore_plugin_identifying_columns.each_with_object({}) do | column_name, q |
49
+ q[column_name] = values[column_name]
50
+ end
51
+ model.where(query).single_record
52
+ end
53
+
54
+ def insert_ignore_primary_key_columns
55
+ @insert_ignore_primary_key_columns ||= [*primary_key].sort
56
+ end
57
+
58
+ def primary_key_columns_are_same_as_identifying_columns
59
+ insert_ignore_primary_key_columns == self.class.insert_ignore_plugin_identifying_columns.sort
60
+ end
61
+
62
+ # naughty override of Sequel private method
63
+ def _insert_dataset
64
+ super.insert_ignore
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end