Skip to content

Commit

Permalink
feat: add command to publish provider contracts to pactflow (feature …
Browse files Browse the repository at this point in the history
…toggle required) (#107)
  • Loading branch information
bethesque authored May 10, 2022
1 parent 20f1e01 commit 80cc10e
Show file tree
Hide file tree
Showing 11 changed files with 402 additions and 0 deletions.
4 changes: 4 additions & 0 deletions bin/pactflow
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env ruby
require "pactflow/client/cli/pactflow"

Pactflow::Client::CLI::Pactflow.start
36 changes: 36 additions & 0 deletions doc/pacts/markdown/Pact Broker Client - Pactflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

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

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

* [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

#### Interactions
Expand Down Expand Up @@ -41,6 +43,40 @@ Pactflow will respond with:
}
}
```
<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
{
"method": "put",
"path": "/contracts/provider/Bar/version/1",
"headers": {
"Content-Type": "application/json",
"Accept": "application/hal+json"
},
"body": {
"content": "LS0tCjpzb21lOiBjb250cmFjdAo=",
"contractType": "oas",
"contentType": "application/yaml",
"verificationResults": {
"success": true,
"content": "c29tZSByZXN1bHRz",
"contentType": "text/plain",
"format": "text",
"verifier": "my custom tool",
"verifierVersion": "1.0"
}
}
}
```
Pactflow will respond with:
```json
{
"status": 201,
"headers": {
"Content-Type": "application/hal+json;charset=utf-8"
}
}
```
<a name="a_request_to_create_a_webhook_for_a_team_given_a_team_with_UUID_2abbc12a-427d-432a-a521-c870af1739d9_exists"></a>
Given **a team with UUID 2abbc12a-427d-432a-a521-c870af1739d9 exists**, upon receiving **a request to create a webhook for a team** from Pact Broker Client, with
```json
Expand Down
4 changes: 4 additions & 0 deletions lib/pact_broker/client/hal_client_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ def create_index_entry_point(pact_broker_base_url, pact_broker_client_options)
PactBroker::Client::Hal::EntryPoint.new(pact_broker_base_url, create_http_client(pact_broker_client_options))
end

def create_entry_point(entry_point, pact_broker_client_options)
PactBroker::Client::Hal::EntryPoint.new(entry_point, create_http_client(pact_broker_client_options))
end

def create_http_client(pact_broker_client_options)
PactBroker::Client::Hal::HttpClient.new(pact_broker_client_options.merge(pact_broker_client_options[:basic_auth] || {}))
end
Expand Down
12 changes: 12 additions & 0 deletions lib/pactflow/client/cli/pactflow.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
require "pactflow/client/cli/provider_contract_commands"
require "pact_broker/client/cli/custom_thor"

module Pactflow
module Client
module CLI
class Pactflow < PactBroker::Client::CLI::CustomThor
include ::Pactflow::Client::CLI::ProviderContractCommands
end
end
end
end
68 changes: 68 additions & 0 deletions lib/pactflow/client/cli/provider_contract_commands.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
require "pact_broker/client/hash_refinements"

module Pactflow
module Client
module CLI
module ProviderContractCommands
using PactBroker::Client::HashRefinements

def self.included(thor)
thor.class_eval do

if ENV.fetch("PACTFLOW_FEATURES", "").include?("publish-provider-contract")

desc 'publish-provider-contract CONTRACT_FILE ...', "Publish provider contract to Pactflow"
method_option :provider, required: true, desc: "The provider name"
method_option :provider_app_version, required: true, aliases: "-a", desc: "The provider application version"
method_option :branch, aliases: "-h", desc: "Repository branch of the provider version"
#method_option :auto_detect_version_properties, hidden: true, type: :boolean, default: false, desc: "Automatically detect the repository branch from known CI environment variables or git CLI."
method_option :tag, aliases: "-t", type: :array, banner: "TAG", desc: "Tag name for provider version. Can be specified multiple times."
#method_option :tag_with_git_branch, aliases: "-g", type: :boolean, default: false, required: false, desc: "Tag consumer version with the name of the current git branch. Default: false"
method_option :specification, default: "oas", desc: "The contract specification"
method_option :content_type, desc: "The content type. eg. application/yml"
method_option :verification_success, type: :boolean
method_option :verification_results, desc: "The path to the file containing the output from the verification process"
method_option :verification_results_content_type, desc: "The content type of the verification output eg. text/plain, application/yaml"
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"

output_option_json_or_text
shared_authentication_options

def publish_provider_contract(provider_contract_path)
require "pactflow/client/provider_contracts/publish"

params = params = {
provider_name: options.provider.strip,
provider_version_number: options.provider_app_version.strip,
branch_name: options.branch && options.branch.strip,
tags: (options.tag && options.tag.collect(&:strip)) || [],
contract: {
content: File.read(provider_contract_path),
content_type: options.content_type,
specification: options.specification
},
verification_results: {
success: options.verification_success,
content: options.verification_results ? File.read(options.verification_results) : nil,
content_type: options.verification_results_content_type,
format: options.verification_results_format,
verifier: options.verifier,
verifier_version: options.verifier_version
}
}

command_options = { verbose: options.verbose, output: options.output }
result = ::Pactflow::Client::ProviderContracts::Publish.call(params, command_options, pact_broker_client_options)
$stdout.puts result.message
exit(1) unless result.success
end
end
end
end
end
end
end
end
79 changes: 79 additions & 0 deletions lib/pactflow/client/provider_contracts/publish.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
require "pact_broker/client/base_command"
require "pact_broker/client/versions/create"
require "base64"

module Pactflow
module Client
module ProviderContracts
class Publish < 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
create_contract
PactBroker::Client::CommandResult.new(true, green("Successfully published provider contract for #{provider_name} version #{provider_version_number}"))
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)
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
49 changes: 49 additions & 0 deletions script/oas.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
openapi: 3.0.0
info:
title: Sample API
description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
version: 0.1.9
servers:
- url: https://test.pactflow.io
description: Prod
- url: https://test.test.pactflow.io
description: Test
paths:
/admin/teams/{uuid}:
get:
summary: Returns a team
parameters:
- name: uuid
in: path
description: The UUID of the team to return
required: true
example: 85ad09f5-e014-4e0f-a146-4377fa64b5ef
schema:
type: string
responses:
'200':
description: A JSON team
content:
application/json:
schema:
type: object
additionalProperties: false
properties:
name:
type: string
example: Team Awesome
uuid:
type: string
example: 85ad09f5-e014-4e0f-a146-4377fa64b5ef
numberOfMembers:
type: integer
example: 4
_links:
type: object
properties:
self:
type: object
properties:
href:
type: string
example: http://test.pactflow.io/admin/teams/85ad09f5-e014-4e0f-a146-4377fa64b5ef
27 changes: 27 additions & 0 deletions script/publish-provider-contract.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export PACT_BROKER_BASE_URL="http://localhost:9292"
export PACTFLOW_FEATURES=publish-provider-contract
# bundle exec bin/pactflow publish-provider-contract \
# script/oas.yml \
# --provider Foo \
# --provider-app-version 1013b5650d61214e19f10558f97fb5a3bb082d44 \
# --branch main \
# --tag dev \
# --specification oas \
# --content-type application/yml \
# --no-verification-success \
# --verification-results script/verification-results.txt \
# --verification-results-content-type text/plain \
# --verification-results-format text \
# --verifier my-custom-tool \
# --verifier-version "1.0" \
# --verbose


bundle exec bin/pactflow publish-provider-contract \
script/oas.yml \
--provider Foo \
--provider-app-version 1013b5650d61214e19f10558f97fb5a3bb082d44 \
--branch main \
--tag dev \
--specification oas \
--content-type application/yml
1 change: 1 addition & 0 deletions script/verification-results.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
These are some results.
30 changes: 30 additions & 0 deletions spec/pacts/pact_broker_client-pactflow.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,36 @@
"name": "Pactflow"
},
"interactions": [
{
"description": "a request to create a provider contract",
"request": {
"method": "put",
"path": "/contracts/provider/Bar/version/1",
"headers": {
"Content-Type": "application/json",
"Accept": "application/hal+json"
},
"body": {
"content": "LS0tCjpzb21lOiBjb250cmFjdAo=",
"contractType": "oas",
"contentType": "application/yaml",
"verificationResults": {
"success": true,
"content": "c29tZSByZXN1bHRz",
"contentType": "text/plain",
"format": "text",
"verifier": "my custom tool",
"verifierVersion": "1.0"
}
}
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/hal+json;charset=utf-8"
}
}
},
{
"description": "a request for the index resource",
"request": {
Expand Down
Loading

0 comments on commit 80cc10e

Please sign in to comment.