diff --git a/lib/pact/hal/http_client.rb b/lib/pact/hal/http_client.rb index 4d28b59b..416f5ec9 100644 --- a/lib/pact/hal/http_client.rb +++ b/lib/pact/hal/http_client.rb @@ -48,7 +48,7 @@ def create_request uri, http_method, body = nil, headers = {} def perform_request request, uri response = Retry.until_true do http = Net::HTTP.new(uri.host, uri.port, :ENV) - http.set_debug_output(output_stream) if verbose + http.set_debug_output(output_stream) if verbose || ENV['VERBOSE'] == 'true' http.use_ssl = (uri.scheme == 'https') http.ca_file = ENV['SSL_CERT_FILE'] if ENV['SSL_CERT_FILE'] && ENV['SSL_CERT_FILE'] != '' http.ca_path = ENV['SSL_CERT_DIR'] if ENV['SSL_CERT_DIR'] && ENV['SSL_CERT_DIR'] != '' diff --git a/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb b/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb index 97856f25..a726cc7b 100644 --- a/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb +++ b/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb @@ -10,7 +10,7 @@ module Pact module PactBroker class FetchPactURIsForVerification include PactSelectionDescription - attr_reader :provider, :consumer_version_selectors, :provider_version_tags, :broker_base_url, :http_client_options, :http_client, :options + attr_reader :provider, :consumer_version_selectors, :provider_version_branch, :provider_version_tags, :broker_base_url, :http_client_options, :http_client, :options PACTS_FOR_VERIFICATION_RELATION = 'pb:provider-pacts-for-verification'.freeze PACTS_FOR_VERIFICATION_RELATION_BETA = 'beta:provider-pacts-for-verification'.freeze @@ -20,9 +20,10 @@ class FetchPactURIsForVerification SELF = 'self'.freeze EMBEDDED = '_embedded'.freeze - def initialize(provider, consumer_version_selectors, provider_version_tags, broker_base_url, http_client_options, options = {}) + def initialize(provider, consumer_version_selectors, provider_version_branch, provider_version_tags, broker_base_url, http_client_options, options = {}) @provider = provider @consumer_version_selectors = consumer_version_selectors || [] + @provider_version_branch = provider_version_branch @provider_version_tags = [*provider_version_tags] @http_client_options = http_client_options @broker_base_url = broker_base_url @@ -30,8 +31,8 @@ def initialize(provider, consumer_version_selectors, provider_version_tags, brok @options = options end - def self.call(provider, consumer_version_selectors, provider_version_tags, broker_base_url, http_client_options, options = {}) - new(provider, consumer_version_selectors, provider_version_tags, broker_base_url, http_client_options, options).call + def self.call(provider, consumer_version_selectors, provider_version_branch, provider_version_tags, broker_base_url, http_client_options, options = {}) + new(provider, consumer_version_selectors, provider_version_branch, provider_version_tags, broker_base_url, http_client_options, options).call end def call @@ -76,6 +77,7 @@ def query q["includePendingStatus"] = true if options[:include_pending_status] q["consumerVersionSelectors"] = consumer_version_selectors if consumer_version_selectors.any? q["providerVersionTags"] = provider_version_tags if provider_version_tags.any? + q["providerVersionBranch"] = provider_version_branch if provider_version_branch q["includeWipPactsSince"] = options[:include_wip_pacts_since] if options[:include_wip_pacts_since] q end diff --git a/lib/pact/provider/configuration/pact_verification_from_broker.rb b/lib/pact/provider/configuration/pact_verification_from_broker.rb index a832cb9f..9a1a6346 100644 --- a/lib/pact/provider/configuration/pact_verification_from_broker.rb +++ b/lib/pact/provider/configuration/pact_verification_from_broker.rb @@ -15,10 +15,11 @@ class PactVerificationFromBroker # in parent scope, it will clash with these ones, # so put an underscore in front of the name to be safer. - attr_accessor :_provider_name, :_pact_broker_base_url, :_consumer_version_tags, :_provider_version_tags, :_basic_auth_options, :_enable_pending, :_include_wip_pacts_since, :_verbose, :_consumer_version_selectors + attr_accessor :_provider_name, :_pact_broker_base_url, :_consumer_version_tags, :_provider_version_branch, :_provider_version_tags, :_basic_auth_options, :_enable_pending, :_include_wip_pacts_since, :_verbose, :_consumer_version_selectors - def initialize(provider_name, provider_version_tags) + def initialize(provider_name, provider_version_branch, provider_version_tags) @_provider_name = provider_name + @_provider_version_branch = provider_version_branch @_provider_version_tags = provider_version_tags @_consumer_version_tags = [] @_consumer_version_selectors = [] @@ -69,6 +70,7 @@ def create_pact_verification fetch_pacts = Pact::PactBroker::FetchPactURIsForVerification.new( _provider_name, consumer_version_selectors, + _provider_version_branch, _provider_version_tags, _pact_broker_base_url, _basic_auth_options.merge(verbose: _verbose), diff --git a/lib/pact/provider/configuration/service_provider_config.rb b/lib/pact/provider/configuration/service_provider_config.rb index 2b3068a2..8a89ba5c 100644 --- a/lib/pact/provider/configuration/service_provider_config.rb +++ b/lib/pact/provider/configuration/service_provider_config.rb @@ -4,9 +4,11 @@ module Configuration class ServiceProviderConfig attr_accessor :application_version + attr_reader :branch - def initialize application_version, tags, publish_verification_results, &app_block + def initialize application_version, branch, tags, publish_verification_results, &app_block @application_version = application_version + @branch = branch @tags = [*tags] @publish_verification_results = publish_verification_results @app_block = app_block diff --git a/lib/pact/provider/configuration/service_provider_dsl.rb b/lib/pact/provider/configuration/service_provider_dsl.rb index 73ce4dde..fccb45d2 100644 --- a/lib/pact/provider/configuration/service_provider_dsl.rb +++ b/lib/pact/provider/configuration/service_provider_dsl.rb @@ -15,7 +15,7 @@ class ServiceProviderDSL extend Pact::DSL - attr_accessor :name, :app_block, :application_version, :tags, :publish_verification_results + attr_accessor :name, :app_block, :application_version, :branch, :tags, :publish_verification_results CONFIG_RU_APP = lambda { unless File.exist? Pact.configuration.config_ru_path @@ -44,6 +44,10 @@ def app_version_tags tags self.tags = tags end + def app_version_branch branch + self.branch = branch + end + def publish_verification_results publish_verification_results self.publish_verification_results = publish_verification_results Pact::RSpec.with_rspec_2 do @@ -65,7 +69,7 @@ def create_pact_verification consumer_name, options, &block end def create_pact_verification_from_broker(&block) - PactVerificationFromBroker.build(name, tags, &block) + PactVerificationFromBroker.build(name, branch, tags, &block) end def finalize @@ -85,7 +89,7 @@ def application_version_blank? end def create_service_provider - Pact.configuration.provider = ServiceProviderConfig.new(application_version, tags, publish_verification_results, &@app_block) + Pact.configuration.provider = ServiceProviderConfig.new(application_version, branch, tags, publish_verification_results, &@app_block) end end end diff --git a/lib/pact/provider/verification_results/publish.rb b/lib/pact/provider/verification_results/publish.rb index 6a388b97..59bb47e5 100644 --- a/lib/pact/provider/verification_results/publish.rb +++ b/lib/pact/provider/verification_results/publish.rb @@ -16,6 +16,7 @@ class Publish PUBLISH_RELATION = 'pb:publish-verification-results'.freeze PROVIDER_RELATION = 'pb:provider'.freeze VERSION_TAG_RELATION = 'pb:version-tag'.freeze + BRANCH_VERSION_RELATION = 'pb:branch-version'.freeze def self.call pact_source, verification_result, options = {} new(pact_source, verification_result, options).call @@ -31,6 +32,7 @@ def initialize pact_source, verification_result, options = {} def call if can_publish_verification_results? + create_branch_version_if_configured tag_versions_if_configured publish_verification_results true @@ -72,8 +74,28 @@ def tag_versions_if_configured end end + def create_branch_version_if_configured + if Pact.configuration.provider.branch + branch_version_link = provider_entity._link(BRANCH_VERSION_RELATION) + if branch_version_link + version_number = Pact.configuration.provider.application_version + branch = Pact.configuration.provider.branch + + Pact.configuration.output_stream.puts "INFO: Creating #{provider_name} version #{version_number} with branch \"#{branch}\"" + branch_entity = branch_version_link.expand( + version: version_number, + branch: branch + ).put + unless branch_entity.success? + raise PublicationError.new("Error returned from tagging request: status=#{branch_entity.response.code} body=#{branch_entity.response.body}") + end + else + raise PublicationError.new("This version of the Pact Broker does not support version branches. Please update to version 2.58.0 or later.") + end + end + end + def tag_versions - provider_entity = pact_entity.get(PROVIDER_RELATION) tag_link = provider_entity._link(VERSION_TAG_RELATION) || hacky_tag_url(provider_entity) provider_application_version = Pact.configuration.provider.application_version @@ -111,6 +133,10 @@ def consumer_name def provider_name pact_source.pact_hash['provider']['name'] end + + def provider_entity + @provider_entity ||= pact_entity.get(PROVIDER_RELATION) + end end end end diff --git a/spec/integration/publish_verification_spec.rb b/spec/integration/publish_verification_spec.rb index bf1684df..0f996b31 100644 --- a/spec/integration/publish_verification_spec.rb +++ b/spec/integration/publish_verification_spec.rb @@ -11,6 +11,7 @@ double('provider_configuration', application_version: '1.2.3', publish_verification_results?: true, + branch: nil, tags: []) end diff --git a/spec/lib/pact/pact_broker/fetch_pact_uris_for_verification_spec.rb b/spec/lib/pact/pact_broker/fetch_pact_uris_for_verification_spec.rb index 764f0ad8..e0690663 100644 --- a/spec/lib/pact/pact_broker/fetch_pact_uris_for_verification_spec.rb +++ b/spec/lib/pact/pact_broker/fetch_pact_uris_for_verification_spec.rb @@ -12,9 +12,10 @@ module PactBroker let(:broker_base_url) { "http://broker.org" } let(:http_client_options) { {} } let(:consumer_version_selectors) { [{ tag: "cmaster", latest: true, fallbackTag: 'blah' }] } + let(:provider_version_branch) { "pbranch" } let(:provider_version_tags) { ["pmaster"] } - subject { FetchPactURIsForVerification.call(provider, consumer_version_selectors, provider_version_tags, broker_base_url, http_client_options)} + subject { FetchPactURIsForVerification.call(provider, consumer_version_selectors, provider_version_branch, provider_version_tags, broker_base_url, http_client_options)} context "when there is an error retrieving the index resource" do before do @@ -39,7 +40,7 @@ module PactBroker let(:provider_version_tags) { "pmaster" } - subject { FetchPactURIsForVerification.new(provider, consumer_version_selectors, provider_version_tags, broker_base_url, http_client_options)} + subject { FetchPactURIsForVerification.new(provider, consumer_version_selectors, provider_version_branch, provider_version_tags, broker_base_url, http_client_options)} it "wraps an array around it" do expect(subject.provider_version_tags).to eq ["pmaster"] diff --git a/spec/lib/pact/provider/configuration/message_provider_dsl_spec.rb b/spec/lib/pact/provider/configuration/message_provider_dsl_spec.rb index f6f5e054..b41f5895 100644 --- a/spec/lib/pact/provider/configuration/message_provider_dsl_spec.rb +++ b/spec/lib/pact/provider/configuration/message_provider_dsl_spec.rb @@ -158,7 +158,7 @@ module Configuration end it "builds a PactVerificationFromBroker" do - expect(PactVerificationFromBroker).to receive(:build).with("some-provider", ["dev"]) + expect(PactVerificationFromBroker).to receive(:build).with("some-provider", nil, ["dev"]) subject end end diff --git a/spec/lib/pact/provider/configuration/pact_verification_from_broker_spec.rb b/spec/lib/pact/provider/configuration/pact_verification_from_broker_spec.rb index 7539d792..79c5c1ae 100644 --- a/spec/lib/pact/provider/configuration/pact_verification_from_broker_spec.rb +++ b/spec/lib/pact/provider/configuration/pact_verification_from_broker_spec.rb @@ -6,6 +6,7 @@ module Configuration describe PactVerificationFromBroker do describe 'build' do let(:provider_name) {'provider-name'} + let(:provider_version_branch) { 'main' } let(:provider_version_tags) { ['master'] } let(:base_url) { "http://broker.org" } let(:since) { "2020-01-01" } @@ -25,7 +26,7 @@ module Configuration context "with valid values" do subject do - PactVerificationFromBroker.build(provider_name, provider_version_tags) do + PactVerificationFromBroker.build(provider_name, provider_version_branch, provider_version_tags) do pact_broker_base_url base_url, basic_auth_options consumer_version_tags tags enable_pending true @@ -43,6 +44,7 @@ module Configuration expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with( provider_name, consumer_version_selectors, + provider_version_branch, provider_version_tags, base_url, basic_auth_opts, @@ -66,6 +68,7 @@ module Configuration anything, anything, anything, + anything, { include_pending_status: true, include_wip_pacts_since: since.xmlschema @@ -78,7 +81,7 @@ module Configuration context "with a missing base url" do subject do - PactVerificationFromBroker.build(provider_name, provider_version_tags) do + PactVerificationFromBroker.build(provider_name, provider_version_branch, provider_version_tags) do end end @@ -90,27 +93,27 @@ module Configuration context "with a non array object for consumer_version_tags" do subject do - PactVerificationFromBroker.build(provider_name, provider_version_tags) do + PactVerificationFromBroker.build(provider_name, provider_version_branch, provider_version_tags) do pact_broker_base_url base_url consumer_version_tags "master" end end it "coerces the value into an array" do - expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(anything, [{ tag: "master", latest: true}], anything, anything, anything, anything) + expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(anything, [{ tag: "master", latest: true}], anything, anything, anything, anything, anything) subject end end context "when no consumer_version_tags are provided" do subject do - PactVerificationFromBroker.build(provider_name, provider_version_tags) do + PactVerificationFromBroker.build(provider_name, provider_version_branch, provider_version_tags) do pact_broker_base_url base_url end end - it "creates an instance of FetchPacts with an emtpy array for the consumer_version_tags" do - expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(anything, [], anything, anything, anything, anything) + it "creates an instance of FetchPacts with an empty array for the consumer_version_tags" do + expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(anything, [], anything, anything, anything, anything, anything) subject end end @@ -119,14 +122,14 @@ module Configuration let(:tags) { [{ name: 'main', all: true, fallback: 'fallback' }] } subject do - PactVerificationFromBroker.build(provider_name, provider_version_tags) do + PactVerificationFromBroker.build(provider_name, provider_version_branch, provider_version_tags) do pact_broker_base_url base_url consumer_version_tags tags end end it "converts them to selectors" do - expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(anything, [{ tag: "main", latest: false, fallbackTag: 'fallback'}], anything, anything, anything, anything) + expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(anything, [{ tag: "main", latest: false, fallbackTag: 'fallback'}], anything, anything, anything, anything, anything) subject end end @@ -135,7 +138,7 @@ module Configuration let(:tags) { [true] } subject do - PactVerificationFromBroker.build(provider_name, provider_version_tags) do + PactVerificationFromBroker.build(provider_name, provider_version_branch, provider_version_tags) do pact_broker_base_url base_url consumer_version_tags tags end @@ -150,27 +153,27 @@ module Configuration let(:tags) { [{ tag: 'main', latest: true, fallback_tag: 'fallback' }] } subject do - PactVerificationFromBroker.build(provider_name, provider_version_tags) do + PactVerificationFromBroker.build(provider_name, provider_version_branch, provider_version_tags) do pact_broker_base_url base_url consumer_version_selectors tags end end it "converts the casing of the key names" do - expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(anything, [{ tag: "main", latest: true, fallbackTag: 'fallback'}], anything, anything, anything, anything) + expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(anything, [{ tag: "main", latest: true, fallbackTag: 'fallback'}], anything, anything, anything, anything, anything) subject end end context "when no verbose flag is provided" do subject do - PactVerificationFromBroker.build(provider_name, provider_version_tags) do + PactVerificationFromBroker.build(provider_name, provider_version_branch, provider_version_tags) do pact_broker_base_url base_url end end it "creates an instance of FetchPactURIsForVerification with verbose: false" do - expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(anything, anything, anything, anything, hash_including(verbose: false), anything) + expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(anything, anything, anything, anything, anything, hash_including(verbose: false), anything) subject end end diff --git a/spec/lib/pact/provider/configuration/service_provider_config_spec.rb b/spec/lib/pact/provider/configuration/service_provider_config_spec.rb index fd5e46cb..2ea62092 100644 --- a/spec/lib/pact/provider/configuration/service_provider_config_spec.rb +++ b/spec/lib/pact/provider/configuration/service_provider_config_spec.rb @@ -1,21 +1,18 @@ -require 'spec_helper' require 'pact/provider/configuration/service_provider_config' module Pact module Provider module Configuration describe ServiceProviderConfig do - describe "app" do let(:app_block) { ->{ Object.new } } - subject { ServiceProviderConfig.new("1.2.3'", [], true, &app_block) } + subject { ServiceProviderConfig.new("1.2.3'", "main", [], true, &app_block) } it "should execute the app_block each time" do expect(subject.app.object_id).to_not equal(subject.app.object_id) end - end end end diff --git a/spec/lib/pact/provider/configuration/service_provider_dsl_spec.rb b/spec/lib/pact/provider/configuration/service_provider_dsl_spec.rb index c8e72de8..e5b52669 100644 --- a/spec/lib/pact/provider/configuration/service_provider_dsl_spec.rb +++ b/spec/lib/pact/provider/configuration/service_provider_dsl_spec.rb @@ -156,6 +156,7 @@ module Configuration subject do ServiceProviderDSL.build 'some-provider' do app {} + app_version_branch 'main' app_version_tags ['dev'] honours_pacts_from_pact_broker do end @@ -163,7 +164,7 @@ module Configuration end it 'builds a PactVerificationFromBroker' do - expect(PactVerificationFromBroker).to receive(:build).with('some-provider', ['dev']) + expect(PactVerificationFromBroker).to receive(:build).with('some-provider', 'main', ['dev']) subject end end diff --git a/spec/lib/pact/provider/verification_results/publish_spec.rb b/spec/lib/pact/provider/verification_results/publish_spec.rb index 72cca405..1e5b67ba 100644 --- a/spec/lib/pact/provider/verification_results/publish_spec.rb +++ b/spec/lib/pact/provider/verification_results/publish_spec.rb @@ -48,6 +48,9 @@ module VerificationResults }, 'pb:version-tag' => { 'href' => 'http://provider/version/{version}/tag/{tag}' + }, + 'pb:branch-version' => { + 'href' => 'http://provider/branches/{branch}/versions/{version}' } } }.to_json @@ -65,6 +68,7 @@ module VerificationResults let(:verification_json) { '{"foo": "bar"}' } let(:publish_verification_results) { false } let(:publishable) { true } + let(:branch) { nil } let(:tags) { [] } let(:verification) do instance_double("Pact::Verifications::Verification", @@ -75,7 +79,7 @@ module VerificationResults end let(:provider_configuration) do - double('provider config', publish_verification_results?: publish_verification_results, tags: tags, application_version: '1.2.3') + double('provider config', publish_verification_results?: publish_verification_results, branch: branch, tags: tags, application_version: '1.2.3') end before do @@ -84,6 +88,7 @@ module VerificationResults allow(Pact.configuration).to receive(:provider).and_return(provider_configuration) stub_request(:post, stubbed_publish_verification_url).to_return(status: 200, headers: { 'Content-Type' => 'application/hal+json'}, body: created_verification_body) stub_request(:put, 'http://provider/version/1.2.3/tag/foo').to_return(status: 200, headers: { 'Content-Type' => 'application/hal+json'}, body: tag_body) + stub_request(:put, "http://provider/branches/main/versions/1.2.3").to_return(status: 200, body: "{}", headers: { 'Content-Type' => 'application/hal+json' }) stub_request(:get, provider_url).to_return(status: 200, headers: { 'Content-Type' => 'application/hal+json'}, body: provider_body) allow(Retry).to receive(:until_true) { |&block| block.call } end @@ -117,6 +122,35 @@ module VerificationResults end end + context "with a branch" do + let(:branch) { "main" } + + it "creates the branch version" do + subject + expect(WebMock).to have_requested(:put, 'http://provider/branches/main/versions/1.2.3').with(headers: {'Content-Type' => 'application/json'}) + end + + context "when there is an error creating the branch version" do + before do + stub_request(:put, "http://provider/branches/main/versions/1.2.3").to_return(status: 500, body: { some: "error" }.to_json, headers: { 'Content-Type' => 'application/hal+json' }) + end + + it "raises an error" do + expect { subject }.to raise_error PublicationError, /500.*some.*error/ + end + end + + context "when the broker does not support creating branch versions" do + let(:provider_body) do + {}.to_json + end + + it "raises an error" do + expect { subject }.to raise_error PublicationError, /does not support/ + end + end + end + context "with tags" do let(:tags) { ['foo'] } diff --git a/spec/service_providers/pact_ruby_fetch_pacts_for_verification_test.rb b/spec/service_providers/pact_ruby_fetch_pacts_for_verification_test.rb index 1eec32d4..6260dce9 100644 --- a/spec/service_providers/pact_ruby_fetch_pacts_for_verification_test.rb +++ b/spec/service_providers/pact_ruby_fetch_pacts_for_verification_test.rb @@ -16,16 +16,18 @@ let(:pacts_for_verification_relation) { Pact::PactBroker::FetchPactURIsForVerification::PACTS_FOR_VERIFICATION_RELATION } let(:body) do { + "providerVersionBranch" => "main", "providerVersionTags" => ["pdev"], "consumerVersionSelectors" => [{ "tag" => "cdev", "latest" => true}], "includePendingStatus" => true } end + let(:provider_version_branch) { "main" } let(:provider_version_tags) { %w[pdev] } let(:consumer_version_selectors) { [ { tag: "cdev", latest: true }] } let(:options) { { include_pending_status: true }} - subject { Pact::PactBroker::FetchPactURIsForVerification.call(provider, consumer_version_selectors, provider_version_tags, broker_base_url, basic_auth_options, options) } + subject { Pact::PactBroker::FetchPactURIsForVerification.call(provider, consumer_version_selectors, provider_version_branch, provider_version_tags, broker_base_url, basic_auth_options, options) } describe 'fetch pacts' do let(:provider) { 'Bar' } diff --git a/spec/support/bar_pact_helper.rb b/spec/support/bar_pact_helper.rb index ae03faea..8f1752f9 100644 --- a/spec/support/bar_pact_helper.rb +++ b/spec/support/bar_pact_helper.rb @@ -16,6 +16,7 @@ def call env Pact.service_provider "Bar" do app { BarApp.new } app_version '1.2.3' + app_version_branch 'master' app_version_tags ['master'] publish_verification_results true diff --git a/tasks/foo-bar.rake b/tasks/foo-bar.rake index ea5abf81..ebe3129c 100644 --- a/tasks/foo-bar.rake +++ b/tasks/foo-bar.rake @@ -41,7 +41,7 @@ Pact::VerificationTask.new('foobar:wip') do | pact | end Pact::VerificationTask.new(:foobar_using_broker) do | pact | - pact.uri "#{BROKER_BASE_URL}/pacts/provider/Bar/consumer/Foo/version/1.0.0", :pact_helper => './spec/support/bar_pact_helper.rb', username: BROKER_USERNAME, password: BROKER_PASSWORD + pact.uri nil, :pact_helper => './spec/support/bar_pact_helper.rb', username: BROKER_USERNAME, password: BROKER_PASSWORD end Pact::VerificationTask.new('foobar_using_broker:fail') do | pact |