Skip to content

Commit b15ba85

Browse files
committed
feat: allow webhook with optional consumer and/or provider to be created by posting to /webhooks
1 parent e60460e commit b15ba85

File tree

5 files changed

+200
-5
lines changed

5 files changed

+200
-5
lines changed

lib/pact_broker/api/resources/all_webhooks.rb

+55-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
require 'pact_broker/services'
22
require 'pact_broker/api/resources/base_resource'
33
require 'pact_broker/api/decorators/webhooks_decorator'
4+
require 'pact_broker/api/decorators/webhook_decorator'
5+
require 'pact_broker/api/contracts/webhook_contract'
46

57
module PactBroker
68
module Api
@@ -11,17 +13,69 @@ def content_types_provided
1113
[["application/hal+json", :to_json]]
1214
end
1315

16+
def content_types_accepted
17+
[["application/json", :from_json]]
18+
end
19+
1420
def allowed_methods
15-
["GET"]
21+
["GET", "POST"]
22+
end
23+
24+
def create_path
25+
webhook_url next_uuid, base_url
26+
end
27+
28+
def post_is_create?
29+
true
30+
end
31+
32+
def malformed_request?
33+
if request.post?
34+
return invalid_json? || validation_errors?(webhook)
35+
end
36+
false
1637
end
1738

1839
def to_json
1940
Decorators::WebhooksDecorator.new(webhooks).to_json(user_options: decorator_context(resource_title: "Webhooks"))
2041
end
2142

43+
def from_json
44+
saved_webhook = webhook_service.create next_uuid, webhook, consumer, provider
45+
response.body = Decorators::WebhookDecorator.new(saved_webhook).to_json(user_options: { base_url: base_url })
46+
end
47+
48+
private
49+
50+
def validation_errors? webhook
51+
errors = webhook_service.errors(webhook)
52+
53+
unless errors.empty?
54+
set_json_validation_error_messages(errors.messages)
55+
end
56+
57+
!errors.empty?
58+
end
59+
60+
def consumer
61+
webhook.consumer ? pacticipant_service.find_pacticipant_by_name(webhook.consumer.name) : nil
62+
end
63+
64+
def provider
65+
webhook.provider ? pacticipant_service.find_pacticipant_by_name(webhook.provider.name) : nil
66+
end
67+
2268
def webhooks
2369
webhook_service.find_all
2470
end
71+
72+
def webhook
73+
@webhook ||= Decorators::WebhookDecorator.new(PactBroker::Domain::Webhook.new).from_json(request_body)
74+
end
75+
76+
def next_uuid
77+
@next_uuid ||= webhook_service.next_uuid
78+
end
2579
end
2680
end
2781
end

lib/pact_broker/domain/webhook.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ def description
2929
"A webhook for the pact between #{consumer.name} and #{provider.name}"
3030
elsif provider
3131
"A webhook for all pacts with provider #{provider.name}"
32-
else
32+
elsif consumer
3333
"A webhook for all pacts with consumer #{consumer.name}"
34+
else
35+
"A webhook for all pacts"
3436
end
3537
end
3638

spec/features/create_webhook_spec.rb

+25-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
}
2828
end
2929

30-
subject { post path, webhook_json, headers }
30+
subject { post(path, webhook_json, headers) }
3131

3232
context "for a consumer and provider" do
3333
let(:path) { "/webhooks/provider/Some%20Provider/consumer/Some%20Consumer" }
@@ -75,7 +75,11 @@
7575
end
7676

7777
context "for a provider" do
78-
let(:path) { "/webhooks/provider/Some%20Provider" }
78+
let(:path) { "/webhooks" }
79+
80+
before do
81+
webhook_hash[:provider] = { name: "Some Provider" }
82+
end
7983

8084
it "returns a 201 response" do
8185
subject
@@ -90,7 +94,10 @@
9094
end
9195

9296
context "for a consumer" do
93-
let(:path) { "/webhooks/consumer/Some%20Consumer" }
97+
let(:path) { "/webhooks" }
98+
before do
99+
webhook_hash[:consumer] = { name: "Some Consumer" }
100+
end
94101

95102
it "returns a 201 response" do
96103
subject
@@ -103,4 +110,19 @@
103110
expect(PactBroker::Webhooks::Webhook.first.provider).to be nil
104111
end
105112
end
113+
114+
context "with no consumer or provider" do
115+
let(:path) { "/webhooks" }
116+
117+
it "returns a 201 response" do
118+
subject
119+
expect(last_response.status).to be 201
120+
end
121+
122+
it "creates a webhook without a provider" do
123+
subject
124+
expect(PactBroker::Webhooks::Webhook.first.consumer).to be nil
125+
expect(PactBroker::Webhooks::Webhook.first.provider).to be nil
126+
end
127+
end
106128
end

spec/lib/pact_broker/api/resources/all_webhooks_spec.rb

+110
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,116 @@ module Resources
77

88
describe AllWebhooks do
99

10+
let(:webhook_service) { PactBroker::Webhooks::Service }
11+
let(:uuid) { '1483234k24DKFGJ45K' }
12+
let(:path) { "/webhooks" }
13+
let(:headers) { {'CONTENT_TYPE' => 'application/json'} }
14+
let(:webhook) { double('webhook', consumer: parsed_consumer, provider: parsed_provider) }
15+
let(:parsed_provider) { instance_double(PactBroker::Domain::Pacticipant, name: "Some Provider") }
16+
let(:parsed_consumer) { instance_double(PactBroker::Domain::Pacticipant, name: "Some Consumer") }
17+
let(:consumer) { double('consumer', name: "Some Consumer") }
18+
let(:provider) { double('provider', name: "Some Provider") }
19+
let(:saved_webhook) { double('saved_webhook')}
20+
let(:webhook_decorator) { instance_double(Decorators::WebhookDecorator, from_json: webhook) }
21+
22+
before do
23+
allow(PactBroker::Pacticipants::Service).to receive(:find_pacticipant_by_name).with("Some Provider").and_return(provider)
24+
allow(PactBroker::Pacticipants::Service).to receive(:find_pacticipant_by_name).with("Some Consumer").and_return(consumer)
25+
allow(Decorators::WebhookDecorator).to receive(:new).and_return(webhook_decorator)
26+
end
27+
28+
describe "POST" do
29+
let(:webhook_json) do
30+
{
31+
some: 'json'
32+
}.to_json
33+
end
34+
35+
let(:next_uuid) { '123k2nvkkwjrwk34' }
36+
let(:valid) { true }
37+
let(:errors) { double("errors", empty?: valid, messages: ['messages']) }
38+
39+
before do
40+
allow(webhook_service).to receive(:create).and_return(saved_webhook)
41+
allow(webhook_service).to receive(:next_uuid).and_return(next_uuid)
42+
allow(webhook_service).to receive(:errors).and_return(errors)
43+
allow(PactBroker::Domain::Webhook).to receive(:new).and_return(webhook)
44+
end
45+
46+
subject { post path, webhook_json, headers }
47+
48+
context "with malformed JSON" do
49+
let(:webhook_json) { "{" }
50+
51+
it "returns a 400 error" do
52+
subject
53+
expect(last_response.status).to eq 400
54+
end
55+
end
56+
57+
context "with invalid attributes" do
58+
59+
let(:valid) { false }
60+
61+
it "returns a 400" do
62+
subject
63+
expect(last_response.status).to be 400
64+
end
65+
66+
it "returns a HAL JSON content type" do
67+
subject
68+
expect(last_response.headers['Content-Type']).to eq 'application/hal+json;charset=utf-8'
69+
end
70+
71+
it "returns the validation errors" do
72+
subject
73+
expect(JSON.parse(last_response.body, symbolize_names: true)).to eq errors: ['messages']
74+
end
75+
76+
end
77+
78+
context "with valid attributes" do
79+
80+
let(:webhook_response_json) { { some: 'webhook' }.to_json }
81+
82+
before do
83+
allow_any_instance_of(Decorators::WebhookDecorator).to receive(:to_json).and_return(webhook_response_json)
84+
allow(webhook_decorator).to receive(:to_json).and_return(webhook_response_json)
85+
end
86+
87+
it "saves the webhook" do
88+
expect(webhook_service).to receive(:create).with(next_uuid, webhook, consumer, provider)
89+
subject
90+
end
91+
92+
it "returns a 201 response" do
93+
subject
94+
expect(last_response.status).to be 201
95+
end
96+
97+
it "returns the Location header" do
98+
subject
99+
expect(last_response.headers['Location']).to include(next_uuid)
100+
end
101+
102+
it "returns a HAL JSON content type" do
103+
subject
104+
expect(last_response.headers['Content-Type']).to eq 'application/hal+json;charset=utf-8'
105+
end
106+
107+
it "generates the JSON response body" do
108+
expect(Decorators::WebhookDecorator).to receive(:new).with(saved_webhook).and_return(webhook_decorator)
109+
expect(webhook_decorator).to receive(:to_json).with(user_options: { base_url: 'http://example.org' })
110+
subject
111+
end
112+
113+
it "returns the JSON representation of the webhook" do
114+
subject
115+
expect(last_response.body).to eq webhook_response_json
116+
end
117+
end
118+
end
119+
10120
describe "GET" do
11121

12122
subject { get "/webhooks" }

spec/lib/pact_broker/domain/webhook_spec.rb

+7
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ module Domain
3030

3131
it { is_expected.to eq "A webhook for all pacts with provider Provider" }
3232
end
33+
34+
context "with neither a consumer nor a provider" do
35+
let(:consumer) { nil }
36+
let(:provider) { nil }
37+
38+
it { is_expected.to eq "A webhook for all pacts" }
39+
end
3340
end
3441

3542
describe "execute" do

0 commit comments

Comments
 (0)