Skip to content

Commit

Permalink
feat: publish provider contracts using all in one endpoint (#141)
Browse files Browse the repository at this point in the history
  • Loading branch information
bethesque authored Sep 6, 2023
1 parent 4451e4f commit a9f87a8
Show file tree
Hide file tree
Showing 15 changed files with 884 additions and 120 deletions.
94 changes: 94 additions & 0 deletions doc/pacts/markdown/Pact Broker Client - Pactflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@

* [A request for the index resource](#a_request_for_the_index_resource)

* [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:publish-provider-contract_relation_exists_in_the_index_resource) given the pb:publish-provider-contract relation exists in the index resource

* [A request to create a provider contract](#a_request_to_create_a_provider_contract)

* [A request to create a provider contract](#a_request_to_create_a_provider_contract_given_there_is_a_pf:ui_href_in_the_response) given there is a pf:ui href in the response

* [A request to create a webhook for a team](#a_request_to_create_a_webhook_for_a_team_given_a_team_with_UUID_2abbc12a-427d-432a-a521-c870af1739d9_exists) given a team with UUID 2abbc12a-427d-432a-a521-c870af1739d9 exists

* [A request to publish a provider contract](#a_request_to_publish_a_provider_contract)

#### Interactions

<a name="a_request_for_the_index_resource"></a>
Expand Down Expand Up @@ -45,6 +49,33 @@ PactFlow will respond with:
}
}
```
<a name="a_request_for_the_index_resource_given_the_pb:publish-provider-contract_relation_exists_in_the_index_resource"></a>
Given **the pb:publish-provider-contract relation exists in the index resource**, upon receiving **a request for the index resource** from Pact Broker Client, with
```json
{
"method": "GET",
"path": "/",
"headers": {
"Accept": "application/hal+json"
}
}
```
PactFlow will respond with:
```json
{
"status": 200,
"headers": {
"Content-Type": "application/hal+json;charset=utf-8"
},
"body": {
"_links": {
"pf:publish-provider-contract": {
"href": "http://localhost:1235/HAL-REL-PLACEHOLDER-PF-PUBLISH-PROVIDER-CONTRACT-{provider}"
}
}
}
}
```
<a name="a_request_to_create_a_provider_contract"></a>
Upon receiving **a request to create a provider contract** from Pact Broker Client, with
```json
Expand Down Expand Up @@ -171,3 +202,66 @@ PactFlow will respond with:
}
}
```
<a name="a_request_to_publish_a_provider_contract"></a>
Upon receiving **a request to publish a provider contract** from Pact Broker Client, with
```json
{
"method": "post",
"path": "/HAL-REL-PLACEHOLDER-PF-PUBLISH-PROVIDER-CONTRACT-Bar",
"headers": {
"Content-Type": "application/json",
"Accept": "application/hal+json"
},
"body": {
"pacticipantVersionNumber": "1",
"tags": [
"dev"
],
"branch": "main",
"buildUrl": "http://build",
"contract": {
"content": "LS0tCnNvbWU6IGNvbnRyYWN0Cg==",
"contentType": "application/yaml",
"specification": "oas",
"selfVerificationResults": {
"success": true,
"content": "c29tZSByZXN1bHRz",
"contentType": "text/plain",
"format": "text",
"verifier": "my custom tool",
"verifierVersion": "1.0"
}
}
}
}
```
PactFlow will respond with:
```json
{
"status": 200,
"headers": {
"Content-Type": "application/hal+json;charset=utf-8"
},
"body": {
"notices": [
{
"text": "some notice",
"type": "info"
}
],
"_embedded": {
"version": {
"number": "1"
}
},
"_links": {
"pb:pacticipant-version-tags": [
{
}
],
"pb:branch-version": {
}
}
}
}
```
3 changes: 2 additions & 1 deletion lib/pactflow/client/cli/provider_contract_commands.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def self.included(thor)
method_option :verification_results_format, desc: "The format of the verification output eg. junit, text"
method_option :verifier, desc: "The tool used to verify the provider contract"
method_option :verifier_version, desc: "The version of the tool used to verify the provider contract"
#method_option :build_url, desc: "The build URL that created the pact"
method_option :build_url, desc: "The build URL that created the provider contract"

output_option_json_or_text
shared_authentication_options
Expand Down Expand Up @@ -70,6 +70,7 @@ def publish_provider_contract_command_params(provider_contract_path)
provider_version_number: options.provider_app_version.strip,
branch_name: options.branch && options.branch.strip,
tags: (options.tag && options.tag.collect(&:strip)) || [],
build_url: options.build_url,
contract: {
content: File.read(provider_contract_path),
content_type: options.content_type,
Expand Down
104 changes: 48 additions & 56 deletions lib/pactflow/client/provider_contracts/publish.rb
Original file line number Diff line number Diff line change
@@ -1,76 +1,45 @@
require "pact_broker/client/base_command"
require "pact_broker/client/versions/create"
require 'pact_broker/client/colorize_notices'
require "base64"
require "pact_broker/client/base_command"
require "pact_broker/client/colorize_notices"
require "pactflow/client/provider_contracts/publish_the_old_way"

module Pactflow
module Client
module ProviderContracts
class Publish < PactBroker::Client::BaseCommand
attr_reader :branch_name, :tags, :provider_name, :provider_version_number, :contract, :verification_results
PUBLISH_RELATION = "pf:publish-provider-contract"


def initialize(params, options, pact_broker_client_options)
super
@provider_name = params[:provider_name]
@provider_version_number = params[:provider_version_number]
@branch_name = params[:branch_name]
@tags = params[:tags] || []
@build_url = params[:build_url]
@contract = params[:contract]
@verification_results = params[:verification_results]
end

private

def do_call
create_branch_version_and_tags
render_response(create_contract)
end
attr_reader :provider_name, :provider_version_number, :branch_name, :tags, :build_url, :contract, :verification_results

def render_response(res)
notices = [
{ type: 'success', text: "Successfully published provider contract for #{provider_name} version #{provider_version_number} to PactFlow"},
]
if res.body && res.body['_links'] && res.body['_links']['pf:ui']['href']
notices.concat([{ text: "View the uploaded contract at #{res.body['_links']['pf:ui']['href']}" }])
def do_call
if enabled? && index_resource.assert_success!.can?(PUBLISH_RELATION)
publish_provider_contracts
PactBroker::Client::CommandResult.new(success?, message)
else
PublishTheOldWay.call(params, options, pact_broker_client_options)
end
notices.concat(next_steps)
PactBroker::Client::CommandResult.new(true, PactBroker::Client::ColorizeNotices.call(notices.collect do |n|
OpenStruct.new(n)
end).join("\n"))
end

def next_steps
[
{ type: 'prompt', text: 'Next steps:' },
{ type: 'prompt',
text: ' * Check your application is safe to deploy - https://docs.pact.io/can_i_deploy' },
{ text: " pact-broker can-i-deploy --pacticipant #{provider_name} --version #{provider_version_number} --to-environment <your environment name>" },
{ type: 'prompt',
text: ' * Record deployment or release to specified environment (choose one) - https://docs.pact.io/go/record-deployment' },
{ text: " pact-broker record-deployment --pacticipant #{provider_name} --version #{provider_version_number} --environment <your environment name>" },
{ text: " pact-broker record-release --pacticipant #{provider_name} --version #{provider_version_number} --environment <your environment name>" }
]
end

def create_branch_version_and_tags
if branch_name || tags.any?
pacticipant_version_params = {
pacticipant_name: provider_name,
version_number: provider_version_number,
branch_name: branch_name,
tags: tags
}
result = PactBroker::Client::Versions::Create.call(pacticipant_version_params, options, pact_broker_client_options)
if !result.success
raise PactBroker::Client::Error.new(result.message)
end
end
def enabled?
ENV.fetch("PACT_BROKER_FEATURES", "").include?("publish_provider_contracts_all_in_one")
end

def create_contract
contract_path = "#{pact_broker_base_url}/contracts/provider/{provider}/version/{version}"
entrypoint = create_entry_point(contract_path, pact_broker_client_options)
entrypoint.expand(provider: provider_name, version: provider_version_number).put!(contract_params).response
def publish_provider_contracts
@response_entity = index_resource._link(PUBLISH_RELATION).expand(provider: provider_name).post!(contract_params, headers: { "Accept" => "application/hal+json,application/problem+json" })
end

def contract_params
Expand All @@ -82,22 +51,45 @@ def contract_params
verifier: verification_results[:verifier],
verifierVersion: verification_results[:verifier_version]
}.compact
body_params = {
content: encode_content(contract[:content]),
contractType: contract[:specification],
contentType: contract[:content_type],
}.compact

contract_params = {
content: encode_content(contract[:content]),
specification: contract[:specification],
contentType: contract[:content_type]
}.compact

if verification_results_params.any?
body_params[:verificationResults] = verification_results_params
contract_params[:selfVerificationResults] = verification_results_params
end
body_params

{
pacticipantVersionNumber: provider_version_number,
tags: tags,
branch: branch_name,
buildUrl: build_url,
contract: contract_params
}
end

def encode_content oas
Base64.strict_encode64(oas)
end

def message
if options[:output] == "json"
@response_entity.response.raw_body
else
text_message
end
end

def success?
@response_entity.success?
end

def text_message
PactBroker::Client::ColorizeNotices.call(@response_entity.notices.collect{ |n| OpenStruct.new(n) } )
end
end
end
end
Expand Down
104 changes: 104 additions & 0 deletions lib/pactflow/client/provider_contracts/publish_the_old_way.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
require "pact_broker/client/base_command"
require "pact_broker/client/versions/create"
require 'pact_broker/client/colorize_notices'
require "base64"

module Pactflow
module Client
module ProviderContracts
class PublishTheOldWay < PactBroker::Client::BaseCommand
attr_reader :branch_name, :tags, :provider_name, :provider_version_number, :contract, :verification_results

def initialize(params, options, pact_broker_client_options)
super
@provider_name = params[:provider_name]
@provider_version_number = params[:provider_version_number]
@branch_name = params[:branch_name]
@tags = params[:tags] || []
@contract = params[:contract]
@verification_results = params[:verification_results]
end

private

def do_call
create_branch_version_and_tags
render_response(create_contract)
end

def render_response(res)
notices = [
{ type: 'success', text: "Successfully published provider contract for #{provider_name} version #{provider_version_number} to PactFlow"},
]
if res.body && res.body['_links'] && res.body['_links']['pf:ui']['href']
notices.concat([{ text: "View the uploaded contract at #{res.body['_links']['pf:ui']['href']}" }])
end
notices.concat(next_steps)
PactBroker::Client::CommandResult.new(true, PactBroker::Client::ColorizeNotices.call(notices.collect do |n|
OpenStruct.new(n)
end).join("\n"))
end

def next_steps
[
{ type: 'prompt', text: 'Next steps:' },
{ type: 'prompt',
text: ' * Check your application is safe to deploy - https://docs.pact.io/can_i_deploy' },
{ text: " pact-broker can-i-deploy --pacticipant #{provider_name} --version #{provider_version_number} --to-environment <your environment name>" },
{ type: 'prompt',
text: ' * Record deployment or release to specified environment (choose one) - https://docs.pact.io/go/record-deployment' },
{ text: " pact-broker record-deployment --pacticipant #{provider_name} --version #{provider_version_number} --environment <your environment name>" },
{ text: " pact-broker record-release --pacticipant #{provider_name} --version #{provider_version_number} --environment <your environment name>" }
]
end

def create_branch_version_and_tags
if branch_name || tags.any?
pacticipant_version_params = {
pacticipant_name: provider_name,
version_number: provider_version_number,
branch_name: branch_name,
tags: tags
}
result = PactBroker::Client::Versions::Create.call(pacticipant_version_params, options, pact_broker_client_options)
if !result.success
raise PactBroker::Client::Error.new(result.message)
end
end
end

def create_contract
contract_path = "#{pact_broker_base_url}/contracts/provider/{provider}/version/{version}"
entrypoint = create_entry_point(contract_path, pact_broker_client_options)
entrypoint.expand(provider: provider_name, version: provider_version_number).put!(contract_params).response
end

def contract_params
verification_results_params = {
success: verification_results[:success],
content: verification_results[:content] ? encode_content(verification_results[:content]) : nil,
contentType: verification_results[:content_type],
format: verification_results[:format],
verifier: verification_results[:verifier],
verifierVersion: verification_results[:verifier_version]
}.compact

body_params = {
content: encode_content(contract[:content]),
contractType: contract[:specification],
contentType: contract[:content_type],
}.compact

if verification_results_params.any?
body_params[:verificationResults] = verification_results_params
end
body_params
end

def encode_content oas
Base64.strict_encode64(oas)
end
end
end
end
end
Loading

0 comments on commit a9f87a8

Please sign in to comment.