pact_broker 2.0.0.beta.6 → 2.0.0.beta.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +12 -12
  4. data/db/migrations/08_create_latest_pact_view.rb +4 -4
  5. data/db/migrations/14_add_timestamps_to_pact_views.rb +5 -4
  6. data/db/migrations/20_add_pact_version_content_sha_to_all_pacts_view.rb +12 -10
  7. data/db/migrations/28_create_all_pact_publications.rb +6 -5
  8. data/lib/pact_broker/api/decorators/verification_decorator.rb +0 -3
  9. data/lib/pact_broker/api/decorators/verification_summary_decorator.rb +36 -0
  10. data/lib/pact_broker/api/decorators/versions_decorator.rb +1 -1
  11. data/lib/pact_broker/api/resources/latest_verifications_for_consumer_version.rb +5 -5
  12. data/lib/pact_broker/api/resources/versions.rb +1 -1
  13. data/lib/pact_broker/app.rb +2 -0
  14. data/lib/pact_broker/configuration.rb +6 -1
  15. data/lib/pact_broker/domain/order_versions.rb +15 -5
  16. data/lib/pact_broker/domain/verification.rb +1 -1
  17. data/lib/pact_broker/domain/webhook.rb +0 -1
  18. data/lib/pact_broker/domain/webhook_request.rb +6 -4
  19. data/lib/pact_broker/logging.rb +4 -0
  20. data/lib/pact_broker/pacticipants/repository.rb +3 -2
  21. data/lib/pact_broker/pacticipants/service.rb +12 -9
  22. data/lib/pact_broker/pacts/all_pact_publications.rb +2 -2
  23. data/lib/pact_broker/pacts/repository.rb +11 -2
  24. data/lib/pact_broker/pacts/service.rb +4 -0
  25. data/lib/pact_broker/tags/repository.rb +5 -5
  26. data/lib/pact_broker/verifications/repository.rb +4 -3
  27. data/lib/pact_broker/verifications/service.rb +8 -0
  28. data/lib/pact_broker/verifications/summary_for_consumer_version.rb +41 -0
  29. data/lib/pact_broker/version.rb +1 -1
  30. data/lib/pact_broker/versions/parse_semantic_version.rb +14 -4
  31. data/lib/pact_broker/versions/repository.rb +1 -1
  32. data/lib/pact_broker/webhooks/job.rb +46 -0
  33. data/lib/pact_broker/webhooks/service.rb +9 -8
  34. data/lib/pact_broker/webhooks/webhook.rb +1 -1
  35. data/pact_broker.gemspec +4 -3
  36. data/pact_broker_client-pact_broker.json +4 -4
  37. data/script/foo-bar.json +22 -0
  38. data/script/publish-new.sh +7 -0
  39. data/script/publish.sh +2 -2
  40. data/script/recreate-pg-db.sh +10 -0
  41. data/spec/features/get_verifications_for_consumer_version_spec.rb +1 -1
  42. data/spec/fixtures/a_consumer-a_provider-2.json +1 -1
  43. data/spec/fixtures/a_consumer-a_provider-3.json +1 -1
  44. data/spec/fixtures/a_consumer-a_provider-conflict.json +1 -1
  45. data/spec/fixtures/a_consumer-a_provider-merged.json +2 -2
  46. data/spec/fixtures/a_consumer-a_provider.json +1 -1
  47. data/spec/fixtures/consumer-provider.json +1 -1
  48. data/spec/fixtures/renderer_pact.json +1 -1
  49. data/spec/lib/pact_broker/api/decorators/{verifications_decorator_spec.rb → verification_summary_decorator_spec.rb} +16 -13
  50. data/spec/lib/pact_broker/api/resources/latest_verifications_for_consumer_version_spec.rb +5 -5
  51. data/spec/lib/pact_broker/domain/order_versions_spec.rb +30 -10
  52. data/spec/lib/pact_broker/domain/webhook_request_spec.rb +3 -1
  53. data/spec/lib/pact_broker/pacticipants/repository_spec.rb +16 -0
  54. data/spec/lib/pact_broker/pacticipants/service_spec.rb +74 -24
  55. data/spec/lib/pact_broker/verifications/summary_for_consumer_version_spec.rb +72 -0
  56. data/spec/lib/pact_broker/versions/parse_semantic_version_spec.rb +5 -2
  57. data/spec/lib/pact_broker/webhooks/job_spec.rb +67 -0
  58. data/spec/lib/pact_broker/webhooks/service_spec.rb +40 -3
  59. data/spec/support/provider_state_builder.rb +3 -2
  60. data/tasks/db.rake +3 -2
  61. metadata +35 -14
  62. data/lib/pact_broker/api/decorators/verifications_decorator.rb +0 -30
  63. data/lib/pact_broker/pacts/all_pacts.rb +0 -12
  64. data/lib/pact_broker/pacts/latest_pacts.rb +0 -12
@@ -41,8 +41,8 @@ module PactBroker
41
41
  pacticipant_repository.find_by_name(name)
42
42
  end
43
43
 
44
- def self.find_all_pacticipant_versions name
45
- pacticipant_repository.find_all_pacticipant_versions(name)
44
+ def self.find_all_pacticipant_versions_in_reverse_order name
45
+ pacticipant_repository.find_all_pacticipant_versions_in_reverse_order(name)
46
46
  end
47
47
 
48
48
  def self.find_pacticipant_repository_url_by_pacticipant_name name
@@ -75,14 +75,17 @@ module PactBroker
75
75
  def self.delete name
76
76
  pacticipant = find_pacticipant_by_name name
77
77
  connection = PactBroker::Domain::Pacticipant.new.db
78
- connection.run("delete from tags where version_id IN (select id from versions where pacticipant_id IN (select id from pacticipants where name = '#{name}'))")
79
- connection.run("delete from pact_publications where consumer_version_id IN (select id from versions where pacticipant_id IN (select id from pacticipants where name = '#{name}'))")
80
- connection.run("delete from pact_publications where provider_id IN (select id from pacticipants where name = '#{name}')")
81
- connection.run("delete from pact_versions where provider_id IN (select id from pacticipants where name = '#{name}')")
82
- connection.run("delete from pact_versions where consumer_id IN (select id from pacticipants where name = '#{name}')")
83
- connection.run("delete from versions where pacticipant_id IN (select id from pacticipants where name = '#{name}')")
78
+ select_pacticipant = "select id from pacticipants where name = '#{name}'"
79
+ connection.run("delete from tags where version_id IN (select id from versions where pacticipant_id = #{pacticipant.id})")
80
+ connection.run("delete from pact_publications where consumer_version_id IN (select id from versions where pacticipant_id = #{pacticipant.id})")
81
+ connection.run("delete from pact_publications where provider_id = #{pacticipant.id}")
82
+ connection.run("delete from verifications where pact_version_id IN (select id from pact_versions where provider_id = #{pacticipant.id})")
83
+ connection.run("delete from verifications where pact_version_id IN (select id from pact_versions where consumer_id = #{pacticipant.id})")
84
+ connection.run("delete from pact_versions where provider_id = #{pacticipant.id}")
85
+ connection.run("delete from pact_versions where consumer_id = #{pacticipant.id}")
86
+ connection.run("delete from versions where pacticipant_id = #{pacticipant.id}")
84
87
  webhook_service.delete_by_pacticipant pacticipant
85
- connection.run("delete from pacticipants where name = '#{name}'")
88
+ connection.run("delete from pacticipants where id = #{pacticipant.id}")
86
89
  end
87
90
 
88
91
  def self.pacticipant_names
@@ -46,11 +46,11 @@ module PactBroker
46
46
  end
47
47
 
48
48
  def consumer_version_order_before order
49
- where('consumer_version_order < ?', order)
49
+ where(Sequel.lit("consumer_version_order < ?", order))
50
50
  end
51
51
 
52
52
  def consumer_version_order_after order
53
- where('consumer_version_order > ?', order)
53
+ where(Sequel.lit("consumer_version_order > ?", order))
54
54
  end
55
55
 
56
56
  def latest
@@ -49,10 +49,11 @@ module PactBroker
49
49
  end
50
50
 
51
51
  def find_all_pact_versions_between consumer_name, options
52
+ provider_name = options.fetch(:and)
52
53
  LatestPactPublicationsByConsumerVersion
53
54
  .eager(:tags)
54
55
  .consumer(consumer_name)
55
- .provider(options.fetch(:and))
56
+ .provider(provider_name)
56
57
  .reverse_order(:consumer_version_order)
57
58
  .collect(&:to_domain)
58
59
  end
@@ -65,6 +66,14 @@ module PactBroker
65
66
  end
66
67
  end
67
68
 
69
+ # Returns latest pact version for the consumer_version_number
70
+ def find_by_consumer_version consumer_name, consumer_version_number
71
+ LatestPactPublicationsByConsumerVersion
72
+ .consumer(consumer_name)
73
+ .consumer_version_number(consumer_version_number)
74
+ .collect(&:to_domain_with_content)
75
+ end
76
+
68
77
  def find_by_version_and_provider version_id, provider_id
69
78
  LatestPactPublicationsByConsumerVersion
70
79
  .eager(:tags)
@@ -136,7 +145,7 @@ module PactBroker
136
145
  .consumer(pact.consumer.name)
137
146
  .provider(pact.provider.name)
138
147
  .consumer_version_order_before(pact.consumer_version.order)
139
- .where('pact_version_sha != ?', current_pact_content_sha)
148
+ .where(Sequel.lit("pact_version_sha != ?", current_pact_content_sha))
140
149
  .latest
141
150
  .collect(&:to_domain_with_content)[0]
142
151
  end
@@ -25,6 +25,10 @@ module PactBroker
25
25
  pact_repository.find_pact(params[:consumer_name], params[:consumer_version_number], params[:provider_name], params[:revision_number])
26
26
  end
27
27
 
28
+ def find_by_consumer_version params
29
+ pact_repository.find_by_consumer_version(params[:consumer_name], params[:consumer_version_number])
30
+ end
31
+
28
32
  def delete params
29
33
  logger.info "Deleting pact version with params #{params}"
30
34
  pact_repository.delete(params)
@@ -14,12 +14,12 @@ module PactBroker
14
14
 
15
15
  def find args
16
16
  PactBroker::Domain::Tag
17
- .select(:tags__name, :tags__version_id, :tags__created_at, :tags__updated_at)
17
+ .select(Sequel.qualify("tags", "name"), Sequel.qualify("tags", "version_id"), Sequel.qualify("tags", "created_at"), Sequel.qualify("tags", "updated_at"))
18
18
  .join(:versions, {id: :version_id})
19
- .join(:pacticipants, {pacticipants__id: :versions__pacticipant_id})
20
- .where(name_like(:tags__name, args.fetch(:tag_name)))
21
- .where(name_like(:versions__number, args.fetch(:pacticipant_version_number)))
22
- .where(name_like(:pacticipants__name, args.fetch(:pacticipant_name)))
19
+ .join(:pacticipants, {Sequel.qualify("pacticipants", "id") => Sequel.qualify("versions", "pacticipant_id")})
20
+ .where(name_like(Sequel.qualify("tags", "name"), args.fetch(:tag_name)))
21
+ .where(name_like(Sequel.qualify("versions", "number"), args.fetch(:pacticipant_version_number)))
22
+ .where(name_like(Sequel.qualify("pacticipants", "name"), args.fetch(:pacticipant_name)))
23
23
  .single_record
24
24
  end
25
25
  end
@@ -19,7 +19,7 @@ module PactBroker
19
19
 
20
20
  def find consumer_name, provider_name, pact_version_sha, verification_number
21
21
  PactBroker::Domain::Verification
22
- .join(PactBroker::Pacts::AllPactPublications, pact_version_id: :pact_version_id)
22
+ .join(:all_pact_publications, pact_version_id: :pact_version_id)
23
23
  .consumer(consumer_name)
24
24
  .provider(provider_name)
25
25
  .pact_version_sha(pact_version_sha)
@@ -30,15 +30,16 @@ module PactBroker
30
30
  # Use LatestPactPublicationsByConsumerVersion not AllPactPublcations because we don't
31
31
  # want verifications for shadowed revisions as it would be misleading.
32
32
  LatestVerificationsByConsumerVersion
33
- .join(PactBroker::Pacts::LatestPactPublicationsByConsumerVersion, pact_version_id: :pact_version_id)
33
+ .join(:latest_pact_publications_by_consumer_versions, pact_version_id: :pact_version_id)
34
34
  .consumer(consumer_name)
35
35
  .consumer_version_number(consumer_version_number)
36
36
  .order(:provider_name)
37
+ .all
37
38
  end
38
39
 
39
40
  def find_latest_verification_for consumer_name, provider_name
40
41
  query = LatestVerificationsByConsumerVersion
41
- .join(PactBroker::Pacts::AllPactPublications, pact_version_id: :pact_version_id)
42
+ .join(:all_pact_publications, pact_version_id: :pact_version_id)
42
43
  .consumer(consumer_name)
43
44
  .provider(provider_name)
44
45
  .latest
@@ -1,5 +1,6 @@
1
1
  require 'pact_broker/repositories'
2
2
  require 'pact_broker/api/decorators/verification_decorator'
3
+ require 'pact_broker/verifications/summary_for_consumer_version'
3
4
 
4
5
  module PactBroker
5
6
 
@@ -9,6 +10,7 @@ module PactBroker
9
10
  extend self
10
11
 
11
12
  extend PactBroker::Repositories
13
+ extend PactBroker::Services
12
14
 
13
15
  def next_number_for pact
14
16
  verification_repository.verification_count_for_pact(pact) + 1
@@ -39,6 +41,12 @@ module PactBroker
39
41
  def find_latest_verification_for consumer, provider
40
42
  verification_repository.find_latest_verification_for consumer.name, provider.name
41
43
  end
44
+
45
+ def verification_summary_for_consumer_version params
46
+ verifications = find_latest_verifications_for_consumer_version(params)
47
+ pacts = pact_service.find_by_consumer_version(params)
48
+ SummaryForConsumerVersion.new(verifications, pacts)
49
+ end
42
50
  end
43
51
  end
44
52
  end
@@ -0,0 +1,41 @@
1
+ module PactBroker
2
+ module Verifications
3
+ class SummaryForConsumerVersion
4
+
5
+ attr_reader :verifications
6
+
7
+ def initialize verifications, pacts
8
+ @verifications = verifications
9
+ @pacts = pacts
10
+ end
11
+
12
+ def success
13
+ successful.count == pacts.count
14
+ end
15
+
16
+ def provider_summary
17
+ OpenStruct.new(
18
+ successful: successful,
19
+ failed: failed,
20
+ unknown: unknown
21
+ )
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :pacts
27
+
28
+ def successful
29
+ verifications.select(&:success).collect(&:provider_name)
30
+ end
31
+
32
+ def failed
33
+ verifications.select{|verification| !verification.success }.collect(&:provider_name)
34
+ end
35
+
36
+ def unknown
37
+ pacts.collect(&:provider_name) - verifications.collect(&:provider_name)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '2.0.0.beta.6'
2
+ VERSION = '2.0.0.beta.7'
3
3
  end
@@ -1,18 +1,28 @@
1
1
  require 'semver'
2
+ require 'pact_broker/configuration'
2
3
 
3
4
  module PactBroker
4
5
  module Versions
5
6
  class ParseSemanticVersion
6
- SEMVER_FORMAT = "%M.%m.%p%s%d"
7
+
7
8
 
8
9
  def self.call string_version
9
- version = ::SemVer.parse(string_version, SEMVER_FORMAT)
10
- return SemVerWrapper.new(version) unless version.nil?
10
+ PactBroker.configuration.semver_formats.each do |semver_format|
11
+ parsed_version = ::SemVer.parse(string_version, semver_format)
12
+ return SemVerWrapper.new(parsed_version, semver_format) unless parsed_version.nil?
13
+ end
14
+ nil
11
15
  end
12
16
 
13
17
  class SemVerWrapper < SimpleDelegator
18
+
19
+ def initialize target, semver_format
20
+ super target
21
+ @semver_format = semver_format
22
+ end
23
+
14
24
  def to_s
15
- format(SEMVER_FORMAT)
25
+ format(@semver_format)
16
26
  end
17
27
  end
18
28
  end
@@ -14,7 +14,7 @@ module PactBroker
14
14
 
15
15
  def find_by_pacticipant_name_and_number pacticipant_name, number
16
16
  PactBroker::Domain::Version
17
- .select(:versions__id, :versions__number, :versions__pacticipant_id, :versions__order, :versions__created_at, :versions__updated_at)
17
+ .select(Sequel[:versions][:id], Sequel[:versions][:number], Sequel[:versions][:pacticipant_id], Sequel[:versions][:order], Sequel[:versions][:created_at], Sequel[:versions][:updated_at])
18
18
  .join(:pacticipants, {id: :pacticipant_id})
19
19
  .where(name_like(:number, number))
20
20
  .where(name_like(:name, pacticipant_name))
@@ -0,0 +1,46 @@
1
+ require 'sucker_punch'
2
+ require 'pact_broker/webhooks/service'
3
+ require 'pact_broker/logging'
4
+
5
+ module PactBroker
6
+ module Webhooks
7
+ class Job
8
+
9
+ BACKOFF_TIMES = [10, 60, 120, 300, 600, 1200] #10 sec, 1 min, 2 min, 5 min, 10 min, 20 min => 38 minutes
10
+
11
+ include SuckerPunch::Job
12
+ include PactBroker::Logging
13
+
14
+ def perform data
15
+ @webhook = data[:webhook]
16
+ @error_count = data[:error_count] || 0
17
+ begin
18
+ webhook_execution_result = PactBroker::Webhooks::Service.execute_webhook_now webhook
19
+ reschedule_job unless webhook_execution_result.success?
20
+ rescue StandardError => e
21
+ handle_error e
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :webhook, :error_count
28
+
29
+ def handle_error e
30
+ log_error e
31
+ reschedule_job
32
+ end
33
+
34
+ def reschedule_job
35
+ case error_count
36
+ when 0...BACKOFF_TIMES.size
37
+ logger.debug "Re-enqeuing job for webhook #{webhook.uuid} to run in #{BACKOFF_TIMES[error_count]} seconds"
38
+ Job.perform_in(BACKOFF_TIMES[error_count], {webhook: webhook, error_count: error_count+1})
39
+ else
40
+ logger.error "Failed to execute webhook #{webhook.uuid} after #{BACKOFF_TIMES.size} times."
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -1,6 +1,8 @@
1
1
  require 'pact_broker/repositories'
2
2
  require 'pact_broker/logging'
3
+ require 'pact_broker/webhooks/job'
3
4
  require 'base64'
5
+ require 'securerandom'
4
6
 
5
7
  module PactBroker
6
8
 
@@ -50,6 +52,7 @@ module PactBroker
50
52
 
51
53
  def self.execute_webhooks pact
52
54
  webhooks = webhook_repository.find_by_consumer_and_provider pact.consumer, pact.provider
55
+
53
56
  if webhooks.any?
54
57
  run_later(webhooks)
55
58
  else
@@ -57,15 +60,13 @@ module PactBroker
57
60
  end
58
61
  end
59
62
 
60
- # TODO background job?
61
63
  def self.run_later webhooks
62
- Thread.new do
63
- webhooks.each do | webhook |
64
- begin
65
- webhook.execute
66
- rescue StandardError => e
67
- # Exceptions are already logged, no need to log again.
68
- end
64
+ webhooks.each do | webhook |
65
+ begin
66
+ logger.info "Scheduling job for #{webhook.description} with uuid #{webhook.uuid}"
67
+ Job.perform_async webhook: webhook
68
+ rescue StandardError => e
69
+ log_error e
69
70
  end
70
71
  end
71
72
  end
@@ -46,7 +46,7 @@ module PactBroker
46
46
  end
47
47
 
48
48
  def request_attributes
49
- values.merge(headers: parsed_headers, body: parsed_body, password: plain_text_password)
49
+ values.merge(headers: parsed_headers, body: parsed_body, password: plain_text_password, uuid: uuid)
50
50
  end
51
51
 
52
52
  def plain_text_password
@@ -24,7 +24,7 @@ Gem::Specification.new do |gem|
24
24
  gem.add_runtime_dependency 'httparty', '~> 0.14'
25
25
  gem.add_runtime_dependency 'json', '> 1.8', '< 3.0'
26
26
  gem.add_runtime_dependency 'roar', '~> 1.1'
27
- gem.add_runtime_dependency 'reform', '~> 2.2.0'
27
+ gem.add_runtime_dependency 'reform', '~> 2.2'
28
28
  gem.add_runtime_dependency 'dry-validation', '~> 0.10.5'
29
29
  gem.add_runtime_dependency 'sequel', '~> 4.23'
30
30
  gem.add_runtime_dependency 'webmachine', '1.4.0'
@@ -35,9 +35,10 @@ Gem::Specification.new do |gem|
35
35
  gem.add_runtime_dependency 'pact-support', '~>0.4', '>=0.4.2'
36
36
  gem.add_runtime_dependency 'padrino-core', '~>0.12.4'
37
37
  gem.add_runtime_dependency 'haml', '~>4.0'
38
+ gem.add_runtime_dependency 'sucker_punch', '~>2.0'
38
39
 
39
- gem.add_development_dependency 'sqlite3', '~>1.3'
40
- gem.add_development_dependency 'pry'
40
+ gem.add_development_dependency 'sqlite3'
41
+ gem.add_development_dependency 'pry-byebug'
41
42
  gem.add_development_dependency 'rake', '~>10.0'
42
43
  gem.add_development_dependency 'fakefs', '~>0.4'
43
44
  gem.add_development_dependency 'mysql2', '~>0.3.15'
@@ -35,7 +35,7 @@
35
35
  },
36
36
  {
37
37
  "description": "a request to publish a pact",
38
- "provider_state": "an error occurs while publishing a pact",
38
+ "providerState": "an error occurs while publishing a pact",
39
39
  "request": {
40
40
  "method": "put",
41
41
  "path": "/pacticipant/Condor/versions/1.3.0/pacts/Pricing%20Service",
@@ -72,7 +72,7 @@
72
72
  },
73
73
  {
74
74
  "description": "a request to retrieve the repository URL of the 'Pricing Service'",
75
- "provider_state": "the 'Pricing Service' does not exist in the pact-broker",
75
+ "providerState": "the 'Pricing Service' does not exist in the pact-broker",
76
76
  "request": {
77
77
  "method": "get",
78
78
  "path": "/pacticipant/Pricing%20Service/repository_url",
@@ -90,7 +90,7 @@
90
90
 
91
91
  {
92
92
  "description": "a request to register the repository URL of a pacticipant",
93
- "provider_state": "the 'Pricing Service' does not exist in the pact-broker",
93
+ "providerState": "the 'Pricing Service' does not exist in the pact-broker",
94
94
  "request": {
95
95
  "method": "patch",
96
96
  "path": "/pacticipant/Pricing%20Service",
@@ -108,7 +108,7 @@
108
108
  },
109
109
  {
110
110
  "description": "a request to retrieve the repository URL of the 'Pricing Service'",
111
- "provider_state": "the 'Pricing Service' already exists in the pact-broker",
111
+ "providerState": "the 'Pricing Service' already exists in the pact-broker",
112
112
  "request": {
113
113
  "method": "get",
114
114
  "path": "/pacticipant/Pricing%20Service/repository_url",
@@ -0,0 +1,22 @@
1
+ {
2
+ "consumer": {
3
+ "name": "Foo"
4
+ },
5
+ "provider": {
6
+ "name": "Bar"
7
+ },
8
+ "interactions": [
9
+ {
10
+ "description" : "a request for something",
11
+ "providerState": null,
12
+ "request": {
13
+ "method": "get",
14
+ "path" : "/something"
15
+ },
16
+ "response": {
17
+ "status": 200,
18
+ "body" : "something"
19
+ }
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,7 @@
1
+ # set -x
2
+ BODY=$(ruby -e "require 'json'; j = JSON.parse(File.read('script/foo-bar.json')); j['interactions'][0]['providerState'] = 'it is ' + Time.now.to_s; puts j.to_json")
3
+ echo ${BODY} >> tmp.json
4
+ curl -v -XPUT \-H "Content-Type: application/json" \
5
+ -d@tmp.json \
6
+ http://127.0.0.1:9292/pacts/provider/Bar/consumer/Foo/version/1.0.0
7
+ rm tmp.json