pact_broker 1.3.2.rc1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/README.md +76 -17
  4. data/db/migrations/16_add_pact_content_foreign_key_to_pacts.rb +6 -0
  5. data/db/migrations/migration_helper.rb +11 -1
  6. data/example/Gemfile +1 -1
  7. data/example/pact_broker_database.sqlite3 +0 -0
  8. data/lib/db.rb +1 -0
  9. data/lib/pact_broker/api.rb +11 -2
  10. data/lib/pact_broker/api/decorators.rb +1 -1
  11. data/lib/pact_broker/api/decorators/base_decorator.rb +3 -3
  12. data/lib/pact_broker/api/decorators/basic_pacticipant_decorator.rb +0 -1
  13. data/lib/pact_broker/api/decorators/embedded_tag_decorator.rb +26 -0
  14. data/lib/pact_broker/api/decorators/embedded_version_decorator.rb +20 -0
  15. data/lib/pact_broker/api/decorators/pact_collection_decorator.rb +9 -3
  16. data/lib/pact_broker/api/decorators/pact_decorator.rb +70 -6
  17. data/lib/pact_broker/api/decorators/pact_pacticipant_decorator.rb +2 -2
  18. data/lib/pact_broker/api/decorators/pacticipant_collection_decorator.rb +4 -28
  19. data/lib/pact_broker/api/decorators/pacticipant_decorator.rb +2 -2
  20. data/lib/pact_broker/api/decorators/representable_pact.rb +4 -1
  21. data/lib/pact_broker/api/decorators/tag_decorator.rb +24 -1
  22. data/lib/pact_broker/api/decorators/timestamps.rb +2 -2
  23. data/lib/pact_broker/api/decorators/version_decorator.rb +27 -3
  24. data/lib/pact_broker/api/decorators/webhooks_decorator.rb +1 -1
  25. data/lib/pact_broker/api/pact_broker_urls.rb +10 -3
  26. data/lib/pact_broker/api/resources/index.rb +5 -5
  27. data/lib/pact_broker/api/resources/pact_content_diff.rb +40 -0
  28. data/lib/pact_broker/api/resources/previous_distinct_pact_version.rb +39 -0
  29. data/lib/pact_broker/api/resources/version.rb +36 -0
  30. data/lib/pact_broker/app.rb +2 -2
  31. data/lib/pact_broker/date_helper.rb +93 -0
  32. data/lib/pact_broker/doc/controllers/app.rb +1 -1
  33. data/lib/pact_broker/doc/views/consumer.markdown +7 -0
  34. data/lib/pact_broker/doc/views/diff-previous-distinct.markdown +5 -0
  35. data/lib/pact_broker/doc/views/latest-pact-version.markdown +7 -0
  36. data/lib/pact_broker/doc/views/latest-pact-versions.markdown +5 -0
  37. data/lib/pact_broker/doc/views/pact-versions.markdown +3 -0
  38. data/lib/pact_broker/doc/views/pact-webhooks.markdown +50 -0
  39. data/lib/pact_broker/doc/views/pacticipants.markdown +2 -0
  40. data/lib/pact_broker/doc/views/provider.markdown +7 -0
  41. data/lib/pact_broker/doc/views/tag-prod-version.markdown +7 -0
  42. data/lib/pact_broker/doc/views/tag-version.markdown +4 -0
  43. data/lib/pact_broker/doc/views/webhooks.markdown +2 -0
  44. data/lib/pact_broker/domain/pact.rb +6 -1
  45. data/lib/pact_broker/domain/tag.rb +0 -1
  46. data/lib/pact_broker/locale/en.yml +37 -1
  47. data/lib/pact_broker/pacts/all_pacts.rb +8 -0
  48. data/lib/pact_broker/pacts/create_formatted_diff.rb +20 -0
  49. data/lib/pact_broker/pacts/diff.rb +105 -0
  50. data/lib/pact_broker/pacts/repository.rb +43 -7
  51. data/lib/pact_broker/repositories/version_repository.rb +1 -0
  52. data/lib/pact_broker/services.rb +6 -1
  53. data/lib/pact_broker/services/pact_service.rb +6 -0
  54. data/lib/pact_broker/services/version_service.rb +2 -2
  55. data/lib/pact_broker/ui.rb +8 -3
  56. data/lib/pact_broker/ui/app.rb +2 -1
  57. data/lib/pact_broker/ui/controllers/base_controller.rb +1 -1
  58. data/lib/pact_broker/ui/controllers/clusters.rb +1 -1
  59. data/lib/pact_broker/ui/views/relationships/show.haml +11 -4
  60. data/lib/pact_broker/version.rb +1 -1
  61. data/pact_broker.gemspec +6 -4
  62. data/public/images/doc-text.svg +1 -0
  63. data/public/stylesheets/relationships.css +43 -1
  64. data/script/publish-2.sh +3 -0
  65. data/script/publish.sh +3 -0
  66. data/spec/features/delete_pact_spec.rb +1 -1
  67. data/spec/features/get_diff.rb +52 -0
  68. data/spec/features/get_previous_distinct_version.rb +51 -0
  69. data/spec/features/get_version.rb +45 -0
  70. data/spec/features/publish_pact_spec.rb +1 -1
  71. data/spec/fixtures/a_consumer-a_provider-2.json +22 -0
  72. data/spec/fixtures/a_consumer-a_provider.json +22 -0
  73. data/spec/fixtures/consumer-provider.json +4 -3
  74. data/spec/integration/app_spec.rb +6 -1
  75. data/spec/lib/pact_broker/api/decorators/embedded_tag_decorator_spec.rb +40 -0
  76. data/spec/lib/pact_broker/api/decorators/embedded_version_decorator_spec.rb +39 -0
  77. data/spec/lib/pact_broker/api/decorators/pact_decorator_spec.rb +52 -10
  78. data/spec/lib/pact_broker/api/decorators/pact_version_decorator_spec.rb +9 -1
  79. data/spec/lib/pact_broker/api/decorators/pacticipant_collection_decorator_spec.rb +36 -0
  80. data/spec/lib/pact_broker/api/decorators/tag_decorator_spec.rb +57 -0
  81. data/spec/lib/pact_broker/api/decorators/version_decorator_spec.rb +49 -0
  82. data/spec/lib/pact_broker/api/decorators/webhooks_decorator_spec.rb +2 -2
  83. data/spec/lib/pact_broker/pacts/create_formatted_diff_spec.rb +33 -0
  84. data/spec/lib/pact_broker/pacts/diff_spec.rb +72 -0
  85. data/spec/lib/pact_broker/pacts/pact_params_spec.rb +2 -2
  86. data/spec/lib/pact_broker/pacts/repository_spec.rb +90 -0
  87. data/spec/lib/pact_broker/repositories/version_repository_spec.rb +10 -2
  88. data/spec/lib/pact_broker/ui/controllers/relationships_spec.rb +1 -1
  89. data/vendor/hal-browser/README.md +6 -0
  90. data/vendor/hal-browser/browser.html +5 -3
  91. data/vendor/hal-browser/js/hal.js +3 -0
  92. data/vendor/hal-browser/js/hal/resource.js +1 -0
  93. data/vendor/hal-browser/js/hal/views/embedded_resource.js +1 -0
  94. data/vendor/hal-browser/js/hal/views/query_uri_dialog.js +12 -2
  95. data/vendor/hal-browser/js/hal/views/resource.js +6 -1
  96. data/vendor/hal-browser/styles.css +10 -0
  97. data/vendor/hal-browser/vendor/js/{jquery-1.9.1.js → jquery-1.10.2.js} +4394 -4202
  98. data/vendor/hal-browser/vendor/js/jquery-1.10.2.min.js +6 -0
  99. data/vendor/hal-browser/vendor/js/jquery-1.10.2.min.map +1 -0
  100. metadata +90 -18
  101. data/db/migrations/15_add_value_to_tag.rb.wip +0 -5
  102. data/lib/pact_broker/doc/views/latest-pacts.markdown +0 -3
@@ -1,4 +1,4 @@
1
- require 'padrino'
1
+ require 'padrino-core'
2
2
  require 'redcarpet'
3
3
 
4
4
  Tilt.prefer Tilt::RedcarpetTemplate
@@ -0,0 +1,7 @@
1
+ # Consumer
2
+
3
+ Allowed methods: GET, PATCH, DELETE
4
+
5
+ The application that initiates the HTTP request.
6
+
7
+ See [Pacticipant](/doc/pacticipants) for more information.
@@ -0,0 +1,5 @@
1
+ # Diff with previous distinct version
2
+
3
+ Allowed methods: GET
4
+
5
+ This resource displays the difference between the current pact, and the previous "distinct" version. Given that the pact file does not change every time it is published, the previous distinct version is the most recent version published before the current one where the content is actually different.
@@ -0,0 +1,7 @@
1
+ # Latest pact version
2
+
3
+ Allowed methods: GET
4
+
5
+ The latest pact between this consumer and provider.
6
+
7
+ The "latest" is determined by inspecting the consumer version used when each pact was published.
@@ -0,0 +1,5 @@
1
+ # Latest pact versions
2
+
3
+ Allowed methods: GET
4
+
5
+ A list of the latest pact versions for each consumer/provider pair. The "latest" is determined by inspecting the consumer version used when each pact was published.
@@ -0,0 +1,3 @@
1
+ # Pact Versions
2
+
3
+ Not implemented yet.
@@ -0,0 +1,50 @@
1
+ # Webhooks
2
+
3
+ Allowed methods: GET, POST, DELETE
4
+
5
+ ### Creating
6
+
7
+ 1. To create a webhook, in the HAL Browser, navigate to the pact you want to create the webhook for
8
+ (Click "Go to Entry Point", then select "latest-pacts", then select the pact you want to create the webhook for.)
9
+ 2. Click the "NON-GET" button for the "pact-webhooks" relation.
10
+ 3. Paste in the webhook JSON (example shown below) in the body section and click "Make Request".
11
+
12
+ An example webhook to trigger a Bamboo job.
13
+
14
+ {
15
+ "request": {
16
+ "method": "POST",
17
+ "url": "http://master.ci.my.domain:8085/rest/api/latest/queue/SOME-PROJECT?os_authType=basic",
18
+ "username": "username",
19
+ "password": "password",
20
+ "headers": {
21
+ "Accept": "application/json"
22
+ }
23
+ }
24
+ }
25
+
26
+ A request body can be specified as well.
27
+
28
+ {
29
+ "request": {
30
+ "method": "POST",
31
+ "url": "http://example.org/something",
32
+ "body": {
33
+ "some" : "json"
34
+ }
35
+ }
36
+ }
37
+
38
+ **BEWARE** The password can be reverse engineered from the database, so make a separate account for the Pact Broker to use, don't use your personal account!
39
+
40
+ ### Testing
41
+
42
+ To test a webhook, navigate to the webhook in the HAL browser, then make a POST request to the "execute" relation. The response or error will be shown in the window.
43
+
44
+ ### Deleting
45
+
46
+ Send a DELETE request to the webhook URL.
47
+
48
+ ### Updating
49
+
50
+ Currently not implemented. You will need to delete and re-create the webhook.
@@ -1,5 +1,7 @@
1
1
  # Pacticipants
2
2
 
3
+ Allowed methods: GET, PATCH, DELETE
4
+
3
5
  "Pacticipant" - a party that participates in a pact (ie. a Consumer or a Provider).
4
6
 
5
7
  ### Creating pacticipants
@@ -0,0 +1,7 @@
1
+ # Provider
2
+
3
+ Allowed methods: GET, PATCH, DELETE
4
+
5
+ The application that initiates the HTTP request.
6
+
7
+ See [Pacticipant](/doc/pacticipants) for more information.
@@ -0,0 +1,7 @@
1
+ # Tag Production Version
2
+
3
+ Allowed methods: PUT
4
+
5
+ Add the "prod" tag to this version. This allows you to retrieve the latest version production version using the following URL.
6
+
7
+ http://pact-broker/pacts/provider/{provider}/consumer/{consumer}/latest/prod
@@ -0,0 +1,4 @@
1
+ # Add Tag to Version
2
+
3
+ Allowed methods: PUT
4
+
@@ -1,5 +1,7 @@
1
1
  # Webhooks
2
2
 
3
+ Allowed methods: GET, POST, DELETE
4
+
3
5
  ### Creating
4
6
 
5
7
  1. To create a webhook, in the HAL Browser, navigate to the pact you want to create the webhook for
@@ -1,4 +1,5 @@
1
1
  require 'pact_broker/db'
2
+ require 'pact_broker/json'
2
3
 
3
4
  module PactBroker
4
5
 
@@ -18,7 +19,7 @@ module PactBroker
18
19
  end
19
20
 
20
21
  def to_s
21
- "Pact: provider_id=#{provider_id} created_at=#{created_at} updated_at=#{updated_at}"
22
+ "Pact: consumer=#{consumer.name} provider=#{provider.name}"
22
23
  end
23
24
 
24
25
  def to_json options = {}
@@ -32,6 +33,10 @@ module PactBroker
32
33
  def version_and_updated_date
33
34
  "Version #{consumer_version_number} - #{updated_at.to_time.localtime.strftime("%d/%m/%Y")}"
34
35
  end
36
+
37
+ def content_hash
38
+ JSON.parse(json_content, PACT_PARSING_OPTIONS)
39
+ end
35
40
  end
36
41
 
37
42
  end
@@ -9,7 +9,6 @@ module PactBroker
9
9
 
10
10
  associate(:many_to_one, :version, :class => "PactBroker::Domain::Version", :key => :version_id, :primary_key => :id)
11
11
 
12
-
13
12
  end
14
13
 
15
14
  Tag.plugin :timestamps, :update_on_create=>true
@@ -34,4 +34,40 @@ en:
34
34
  "503":
35
35
  title: 503 Service Unavailable
36
36
  message: The server is currently unable to handle the request due to a temporary overloading or maintenance of the server.
37
-
37
+ # Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
38
+ datetime:
39
+ distance_in_words:
40
+ half_a_minute: "half a minute"
41
+ less_than_x_seconds:
42
+ one: "less than 1 second"
43
+ other: "less than %{count} seconds"
44
+ x_seconds:
45
+ one: "1 second"
46
+ other: "%{count} seconds"
47
+ less_than_x_minutes:
48
+ one: "less than a minute"
49
+ other: "less than %{count} minutes"
50
+ x_minutes:
51
+ one: "1 minute"
52
+ other: "%{count} minutes"
53
+ about_x_hours:
54
+ one: "about 1 hour"
55
+ other: "about %{count} hours"
56
+ x_days:
57
+ one: "1 day"
58
+ other: "%{count} days"
59
+ about_x_months:
60
+ one: "about 1 month"
61
+ other: "about %{count} months"
62
+ x_months:
63
+ one: "1 month"
64
+ other: "%{count} months"
65
+ about_x_years:
66
+ one: "about 1 year"
67
+ other: "about %{count} years"
68
+ over_x_years:
69
+ one: "over 1 year"
70
+ other: "over %{count} years"
71
+ almost_x_years:
72
+ one: "almost 1 year"
73
+ other: "almost %{count} years"
@@ -46,9 +46,17 @@ module PactBroker
46
46
  where('consumer_version_order < ?', order)
47
47
  end
48
48
 
49
+ def consumer_version_order_after order
50
+ where('consumer_version_order > ?', order)
51
+ end
52
+
49
53
  def latest
50
54
  reverse_order(:consumer_version_order).limit(1)
51
55
  end
56
+
57
+ def earliest
58
+ order(:consumer_version_order).limit(1)
59
+ end
52
60
  end
53
61
 
54
62
  def to_domain
@@ -0,0 +1,20 @@
1
+ require 'pact/matchers'
2
+ require 'pact_broker/json'
3
+ require 'pact/matchers/unix_diff_formatter'
4
+
5
+ module PactBroker
6
+ module Pacts
7
+ class CreateFormattedDiff
8
+
9
+ extend Pact::Matchers
10
+
11
+ def self.call pact_json_content, previous_pact_json_content
12
+ pact_hash = JSON.load(pact_json_content, nil, PactBroker::PACT_PARSING_OPTIONS)
13
+ previous_pact_hash = JSON.load(previous_pact_json_content, nil, PactBroker::PACT_PARSING_OPTIONS)
14
+ difference = diff(previous_pact_hash, pact_hash)
15
+ Pact::Matchers::UnixDiffFormatter.call(difference, colour: false, include_explanation: false)
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,105 @@
1
+ require 'trailblazer/operation'
2
+ require 'pact_broker/repositories'
3
+ require 'pact_broker/pacts/create_formatted_diff'
4
+ require 'pact_broker/api/pact_broker_urls'
5
+ require 'yaml'
6
+ require 'pact_broker/date_helper'
7
+
8
+
9
+ module PactBroker
10
+ module Pacts
11
+
12
+ class Diff < Trailblazer::Operation
13
+
14
+ include PactBroker::Repositories
15
+ attr_reader :params, :options
16
+
17
+ def process params, options
18
+ pact = pact_repository.find_pact(params.consumer_name, params.consumer_version_number, params.provider_name)
19
+ previous_distinct_pact = pact_repository.find_previous_distinct_pact pact
20
+ if previous_distinct_pact
21
+ next_pact = pact_repository.find_next_pact previous_distinct_pact
22
+ DiffDecorator.new(pact, previous_distinct_pact, next_pact, options[:base_url]).to_text
23
+ else
24
+ no_previous_version_message pact
25
+ end
26
+ end
27
+
28
+ def no_previous_version_message pact
29
+ "No previous distinct version was found for #{pact.name}"
30
+ end
31
+
32
+ private
33
+
34
+ # The next pact version after the previous distinct version.
35
+ # Eg. v1 (previous distinct) -> pactContentA
36
+ # v2 (next pact) -> pactContentB
37
+ # v3 -> pactContentB
38
+ # v4 (current) -> pactContentB
39
+ # If we are at v4, then the previous distinct pact version is
40
+ # v1, and the next pact after that is v2.
41
+ # The timestamps on v2 are the ones we want - that's when
42
+ # the latest distinct version content was first created.
43
+
44
+ class DiffDecorator
45
+
46
+ attr_reader :pact, :previous_distinct_pact, :next_pact, :base_url
47
+
48
+ def initialize pact, previous_distinct_pact, next_pact, base_url
49
+ @pact = pact
50
+ @previous_distinct_pact = previous_distinct_pact
51
+ @next_pact = next_pact
52
+ @base_url = base_url
53
+ end
54
+
55
+ def to_text
56
+ header + "\n\n" + diff + "\n\n" + links
57
+ end
58
+
59
+ private
60
+
61
+ def change_date_in_words
62
+ DateHelper.local_date_in_words next_pact.updated_at
63
+ end
64
+
65
+ def now
66
+ Time.now
67
+ end
68
+
69
+ def header
70
+ title = "# Diff between versions #{previous_distinct_pact.consumer_version_number} and #{pact.consumer_version_number} of the pact between #{pact.consumer.name} and #{pact.provider.name}"
71
+ description = "The following changes were made #{change_date_ago_in_words} ago (#{change_date_in_words})"
72
+ title + "\n\n" + description
73
+ end
74
+
75
+ def links
76
+ self_url = PactBroker::Api::PactBrokerUrls.pact_url base_url, pact
77
+ previous_distinct_url = PactBroker::Api::PactBrokerUrls.pact_url base_url, previous_distinct_pact
78
+
79
+ links = {
80
+ "current-pact-version" => {
81
+ "title" => "Pact",
82
+ "name" => pact.name,
83
+ "href" => self_url
84
+ },
85
+ "previous-distinct-pact-version" => {
86
+ "title" => "Pact",
87
+ "name" => previous_distinct_pact.name,
88
+ "href" => previous_distinct_url
89
+ }
90
+ }
91
+ "## Links\n" + YAML.dump(links).gsub(/---/,'')
92
+ end
93
+
94
+ def diff
95
+ CreateFormattedDiff.(pact.json_content, previous_distinct_pact.json_content)
96
+ end
97
+
98
+ def change_date_ago_in_words
99
+ DateHelper.distance_of_time_in_words next_pact.updated_at, now
100
+ end
101
+ end
102
+
103
+ end
104
+ end
105
+ end
@@ -5,6 +5,7 @@ require 'pact_broker/logging'
5
5
  require 'pact_broker/pacts/database_model'
6
6
  require 'pact_broker/pacts/all_pacts'
7
7
  require 'pact_broker/pacts/latest_pacts'
8
+ require 'pact/shared/json_differ'
8
9
 
9
10
  module PactBroker
10
11
  module Pacts
@@ -49,7 +50,7 @@ module PactBroker
49
50
  AllPacts
50
51
  .eager(:tags)
51
52
  .where(consumer_version_id: version_id, provider_id: provider_id)
52
- .limit(1).collect(&:to_domain)[0]
53
+ .limit(1).collect(&:to_domain_with_content)[0]
53
54
  end
54
55
 
55
56
  def find_latest_pacts
@@ -79,11 +80,51 @@ module PactBroker
79
80
  .consumer(pact.consumer.name)
80
81
  .provider(pact.provider.name)
81
82
  .consumer_version_order_before(pact.consumer_version.order)
82
- .latest.collect(&:to_domain)[0]
83
+ .latest.collect(&:to_domain_with_content)[0]
84
+ end
85
+
86
+ def find_next_pact pact
87
+ AllPacts
88
+ .eager(:tags)
89
+ .consumer(pact.consumer.name)
90
+ .provider(pact.provider.name)
91
+ .consumer_version_order_after(pact.consumer_version.order)
92
+ .earliest.collect(&:to_domain_with_content)[0]
93
+ end
94
+
95
+ def find_previous_distinct_pact pact
96
+ previous, current = nil, pact
97
+ loop do
98
+ previous = find_previous_distinct_pact_by_sha current
99
+ return previous if previous.nil? || different?(current, previous)
100
+ current = previous
101
+ end
83
102
  end
84
103
 
85
104
  private
86
105
 
106
+ def find_previous_distinct_pact_by_sha pact
107
+ current_pact_content_sha =
108
+ AllPacts.select(:pact_version_content_sha)
109
+ .consumer(pact.consumer.name)
110
+ .provider(pact.provider.name)
111
+ .consumer_version_number(pact.consumer_version_number)
112
+ .limit(1)
113
+
114
+ AllPacts
115
+ .eager(:tags)
116
+ .consumer(pact.consumer.name)
117
+ .provider(pact.provider.name)
118
+ .consumer_version_order_before(pact.consumer_version.order)
119
+ .where('pact_version_content_sha != ?', current_pact_content_sha)
120
+ .latest
121
+ .collect(&:to_domain_with_content)[0]
122
+ end
123
+
124
+ def different? pact, other_pact
125
+ Pact::JsonDiffer.(pact.content_hash, other_pact.content_hash, allow_unexpected_keys: false).any?
126
+ end
127
+
87
128
  def find_or_create_pact_version_content json_content
88
129
  sha = Digest::SHA1.hexdigest(json_content)
89
130
  PactVersionContent.find(sha: sha) || create_pact_version_content(sha, json_content)
@@ -96,11 +137,6 @@ module PactBroker
96
137
  pact_version_content.save
97
138
  end
98
139
 
99
- def to_domain
100
- database_model = yield
101
- database_model ? database_model.to_domain : nil
102
- end
103
-
104
140
  end
105
141
  end
106
142
  end
@@ -11,6 +11,7 @@ module PactBroker
11
11
 
12
12
  def find_by_pacticipant_name_and_number pacticipant_name, number
13
13
  PactBroker::Domain::Version
14
+ .select(:versions__id, :versions__number, :versions__pacticipant_id, :versions__order, :versions__created_at, :versions__updated_at)
14
15
  .where(number: number)
15
16
  .join(:pacticipants, {id: :pacticipant_id})
16
17
  .where(name: pacticipant_name)