Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support multiple branches per version #495

Merged
merged 51 commits into from
Sep 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
42d2feb
chore: add branches tables
bethesque Aug 16, 2021
9b043d5
chore: keep track of the latest version for a branch
bethesque Aug 16, 2021
c4b5a5c
chore: expose branches in version resource
bethesque Aug 16, 2021
7bdd6a4
test: remove branch expectations for creating versions
bethesque Aug 16, 2021
13aa7dc
test: remove tests for updating versions with a branch
bethesque Aug 16, 2021
631788a
chore: update branch head when a version is deleted
bethesque Aug 16, 2021
1980e6a
chore: update code to find versions by branch using the matrix selector
bethesque Aug 16, 2021
575ceb2
chore: update latest_for_branch?
bethesque Aug 16, 2021
59bdb33
refactor: remove latest_version_for_branch code
bethesque Aug 16, 2021
5beecf4
Merge branch 'master' into feat/support-multiple-branches-per-version
bethesque Aug 16, 2021
295eb06
style: rubocop
bethesque Aug 16, 2021
83902a9
chore: update PactPublication.latest_for_consumer_branch
bethesque Aug 16, 2021
573373c
test: update expectations
bethesque Aug 16, 2021
5b5321f
chore: update PactPublication.latest_by_consumer_branch
bethesque Aug 16, 2021
f727156
docs: update sequel annotations
bethesque Aug 16, 2021
8b7d905
chore: update overall_latest test
bethesque Aug 16, 2021
859bd7e
Merge branch 'master' into feat/support-multiple-branches-per-version
bethesque Aug 31, 2021
233afc8
chore: use more efficient query for selecting latest for branches
bethesque Sep 1, 2021
9dfac40
chore: display multiple branches on matrix and index pages
bethesque Sep 1, 2021
984cefe
feat: support adding a version to a branch
bethesque Sep 1, 2021
0ec4704
chore: add branch-version relation to pacticipant
bethesque Sep 1, 2021
747c73d
chore: remove references to version.branch
bethesque Sep 1, 2021
9b23162
chore: update index and dashboard
bethesque Sep 2, 2021
2e621e3
chore: remove domain object usage from migration specs
bethesque Sep 2, 2021
58c82cb
style: rubocop
bethesque Sep 2, 2021
27630f9
style: rubocop
bethesque Sep 2, 2021
4122d96
Merge branch 'master' into feat/support-multiple-branches-per-version
bethesque Sep 2, 2021
44afc59
Merge branch 'master' into feat/support-multiple-branches-per-version
bethesque Sep 2, 2021
8537296
chore: pass in branch for version creation
bethesque Sep 2, 2021
b90ee3a
refactor: remove unused param
bethesque Sep 2, 2021
e3848af
style: rubocop
bethesque Sep 2, 2021
d62cbed
chore: remove unused require
bethesque Sep 2, 2021
69696cd
chore: add latest property to branch version decorator
bethesque Sep 2, 2021
3c4cf5c
chore: update indexes
bethesque Sep 2, 2021
7c3a8a3
chore: update latest by branch query
bethesque Sep 2, 2021
fc3aa06
chore: refactor
bethesque Sep 2, 2021
174eaac
Merge branch 'master' into feat/support-multiple-branches-per-version
bethesque Sep 4, 2021
0121929
chore: migrate version branch column to branch_version object
bethesque Sep 4, 2021
b39a390
style: rubocop
bethesque Sep 4, 2021
5560e71
chore: fix ordering of deployed/released selectors
bethesque Sep 4, 2021
5388b33
chore: sort environment selectors by non prod first
bethesque Sep 4, 2021
6e8bc96
test: include provider in latest_by_consumer_branch
bethesque Sep 5, 2021
e1b21b1
chore: remove unnecessary index
bethesque Sep 5, 2021
437671c
chore: add index
bethesque Sep 5, 2021
4264eaf
chore: update annotations
bethesque Sep 5, 2021
b90fc92
chore: update logging
bethesque Sep 5, 2021
4774770
chore: fix latest_for_consumer_branch
bethesque Sep 5, 2021
358fb25
style: whitespace
bethesque Sep 5, 2021
7d6a048
style: whitespace
bethesque Sep 5, 2021
7bc1579
chore: update hal docs
bethesque Sep 6, 2021
21829ad
Merge branch 'master' into feat/support-multiple-branches-per-version
bethesque Sep 6, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions db/migrations/20210816_create_branches_tables.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
Sequel.migration do
change do
create_table(:branches, charset: "utf8") do
primary_key :id
String :name
foreign_key :pacticipant_id, :pacticipants, null: false, on_delete: :cascade
DateTime :created_at, null: false
DateTime :updated_at, null: false
index [:pacticipant_id, :name], unique: true, name: :branches_pacticipant_id_name_index
end

create_table(:branch_versions, charset: "utf8") do
primary_key :id
foreign_key :branch_id, :branches, null: false, foreign_key_constraint_name: :branch_versions_branches_fk, on_delete: :cascade
foreign_key :version_id, :versions, null: false, foreign_key_constraint_name: :branch_versions_versions_fk, on_delete: :cascade
Integer :version_order, null: false
Integer :pacticipant_id, null: false
String :branch_name, null: false
DateTime :created_at, null: false
DateTime :updated_at, null: false
index [:branch_id, :version_id], unique: true, name: :branch_versions_branch_id_version_id_index
index [:version_id], name: :branch_versions_version_id_index
index [:branch_name], name: :branch_versions_branch_name_index
# Can probably drop this index when the "latest pact" logic changes
index [:pacticipant_id, :branch_id, :version_order], name: :branch_versions_pacticipant_id_branch_id_version_order_index
end

create_table(:branch_heads) do
primary_key :id
foreign_key :branch_id, :branches, null: false, on_delete: :cascade
foreign_key :branch_version_id, :branch_versions, null: false, on_delete: :cascade
Integer :version_id, null: false
Integer :pacticipant_id, null: false
String :branch_name, null: false
index [:branch_id], unique: true, name: :branch_heads_branch_id_index
index [:branch_name], name: :branch_heads_branch_name_index
index [:pacticipant_id], name: :branch_heads_pacticipant_id_index
index [:version_id], name: :branch_heads_version_id_index
end
end
end
1 change: 1 addition & 0 deletions lib/pact_broker/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def self.build_api(application_context = PactBroker::ApplicationContext.default_
add ["pacticipants", :pacticipant_name, "latest-version"], Api::Resources::LatestVersion, {resource_name: "latest_pacticipant_version"}
add ["pacticipants", :pacticipant_name, "versions", :pacticipant_version_number, "tags", :tag_name], Api::Resources::Tag, {resource_name: "pacticipant_version_tag"}
add ["pacticipants", :pacticipant_name, "labels", :label_name], Api::Resources::Label, {resource_name: "pacticipant_label"}
add ["pacticipants", :pacticipant_name, "branches", :branch_name, "versions", :version_number], Api::Resources::BranchVersion, { resource_name: "branch_version" }

# Webhooks
add ["webhooks", "provider", :provider_name, "consumer", :consumer_name ], Api::Resources::PacticipantWebhooks, {resource_name: "pacticipant_webhooks"}
Expand Down
20 changes: 20 additions & 0 deletions lib/pact_broker/api/decorators/branch_version_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require "pact_broker/api/decorators/base_decorator"
require "pact_broker/api/decorators/timestamps"

module PactBroker
module Api
module Decorators
class BranchVersionDecorator < BaseDecorator

link :self do | user_options |
{
title: "Branch version",
href: branch_version_url(represented, user_options.fetch(:base_url))
}
end

include Timestamps
end
end
end
end
6 changes: 4 additions & 2 deletions lib/pact_broker/api/decorators/dashboard_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ def consumer_hash(index_item, _consumer, _consumer_version, base_url)
name: index_item.consumer_name,
version: {
number: index_item.consumer_version_number,
branch: index_item.consumer_version_branch,
branch: index_item.consumer_version_branches.last,
headBranchNames: index_item.consumer_version_branches,
_links: {
self: {
href: version_url(base_url, index_item.consumer_version)
Expand Down Expand Up @@ -87,7 +88,8 @@ def provider_hash(index_item, _provider, base_url)
if index_item.latest_verification
hash[:version] = {
number: index_item.provider_version_number,
branch: index_item.provider_version_branch
branch: index_item.provider_version_branches.last,
headBranchNames: index_item.provider_version_branches
}
end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require_relative "base_decorator"
require_relative "timestamps"

module PactBroker
module Api
module Decorators
class EmbeddedBranchVersionDecorator < BaseDecorator
property :branch_name, as: :name
property :latest?, as: :latest

link :self do | options |
{
title: "Version branch",
name: represented.branch_name,
href: branch_version_url(represented, options[:base_url])
}
end
end
end
end
end
5 changes: 0 additions & 5 deletions lib/pact_broker/api/decorators/embedded_tag_decorator.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
require_relative "base_decorator"
require_relative "pact_pacticipant_decorator"
require_relative "timestamps"

module PactBroker

module Api

module Decorators

class EmbeddedTagDecorator < BaseDecorator

property :name

link :self do | options |
Expand Down
13 changes: 11 additions & 2 deletions lib/pact_broker/api/decorators/matrix_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require "pact_broker/api/pact_broker_urls"
require "pact_broker/api/decorators/reason_decorator"
require "pact_broker/api/decorators/format_date_time"
require "pact_broker/api/decorators/embedded_branch_version_decorator"

module PactBroker
module Api
Expand Down Expand Up @@ -83,7 +84,8 @@ def consumer_hash(line, consumer, consumer_version, base_url)
name: line.consumer_name,
version: {
number: line.consumer_version_number,
branch: line.consumer_version_branch,
branch: line.consumer_version_branch_versions.last&.branch_name,
branches: branches(line.consumer_version_branch_versions, base_url),
_links: {
self: {
href: version_url(base_url, consumer_version)
Expand All @@ -99,6 +101,12 @@ def consumer_hash(line, consumer, consumer_version, base_url)
}
end

def branches(branch_versions, base_url)
branch_versions.collect do | branch_version |
PactBroker::Api::Decorators::EmbeddedBranchVersionDecorator.new(branch_version).to_hash(user_options: { base_url: base_url })
end
end

def tags(tags, base_url)
tags.sort_by(&:created_at).collect do | tag |
{
Expand Down Expand Up @@ -127,7 +135,8 @@ def provider_hash(line, provider, provider_version, base_url)
if !line.provider_version_number.nil?
hash[:version] = {
number: line.provider_version_number,
branch: line.provider_version_branch,
branch: line.provider_version_branch_versions.last&.branch_name,
branches: branches(line.provider_version_branch_versions, base_url),
_links: {
self: {
href: version_url(base_url, provider_version)
Expand Down
8 changes: 8 additions & 0 deletions lib/pact_broker/api/decorators/pacticipant_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ class PacticipantDecorator < BaseDecorator
}
end

link :'pb:branch-version' do | options |
{
title: "Get or add/create a version for a branch of #{represented.name}",
href: templated_branch_version_url_for_pacticipant(represented.name, options[:base_url]),
templated: true
}
end

link :'pb:label' do | options |
{
title: "Get, create or delete a label for #{represented.name}",
Expand Down
2 changes: 1 addition & 1 deletion lib/pact_broker/api/decorators/version_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module Decorators
class VersionDecorator < BaseDecorator

property :number, writeable: false
property :branch
collection :branch_versions, as: :branches, embedded: true, writeable: false, extend: PactBroker::Api::Decorators::EmbeddedBranchVersionDecorator
property :build_url, as: :buildUrl

collection :tags, embedded: true, :extend => PactBroker::Api::Decorators::EmbeddedTagDecorator, class: OpenStruct
Expand Down
8 changes: 8 additions & 0 deletions lib/pact_broker/api/pact_broker_urls.rb
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,18 @@ def tag_url base_url, tag
"#{tags_url(base_url, tag.version)}/#{url_encode(tag.name)}"
end

def branch_version_url(branch_version, base_url = "")
"#{pacticipant_url(base_url, branch_version.pacticipant)}/branches/#{url_encode(branch_version.branch_name)}/versions/#{url_encode(branch_version.version_number)}"
end

def templated_tag_url_for_pacticipant pacticipant_name, base_url = ""
pacticipant_url_from_params({ pacticipant_name: pacticipant_name }, base_url) + "/versions/{version}/tags/{tag}"
end

def templated_branch_version_url_for_pacticipant pacticipant_name, base_url = ""
pacticipant_url_from_params({ pacticipant_name: pacticipant_name }, base_url) + "/branches/{branch}/versions/{version}"
end

def templated_version_url_for_pacticipant pacticipant_name, base_url = ""
pacticipant_url_from_params({ pacticipant_name: pacticipant_name }, base_url) + "/versions/{version}"
end
Expand Down
48 changes: 48 additions & 0 deletions lib/pact_broker/api/resources/branch_version.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
require "pact_broker/api/resources/base_resource"
require "pact_broker/api/decorators/branch_version_decorator"

module PactBroker
module Api
module Resources
class BranchVersion < BaseResource
def content_types_provided
[["application/hal+json", :to_json]]
end

def content_types_accepted
[["application/json", :from_json]]
end

def allowed_methods
["GET", "PUT", "OPTIONS"]
end

def resource_exists?
!!branch_version
end

def to_json
decorator_class(:branch_version_decorator).new(branch_version).to_json(decorator_options)
end

def from_json
already_existed = !!branch_version
@branch_version = branch_service.find_or_create_branch_version(identifier_from_path)
# Make it return a 201 by setting the Location header
response.headers["Location"] = branch_version_url(branch_version, base_url) unless already_existed
response.body = to_json
end

def policy_name
:'versions::branch_version'
end

private

def branch_version
@branch_version ||= branch_service.find_branch_version(identifier_from_path)
end
end
end
end
end
6 changes: 6 additions & 0 deletions lib/pact_broker/api/resources/index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ def links
title: "Get, create or delete a tag for a pacticipant version",
templated: true
},
"pb:pacticipant-branch-version" =>
{
href: base_url + "/pacticipants/{pacticipant}/branches/{branch}/versions/{version}",
title: "Get or add/create a pacticipant version for a branch",
templated: true
},
"pb:pacticipant-version" =>
{
href: base_url + "/pacticipants/{pacticipant}/versions/{version}",
Expand Down
8 changes: 0 additions & 8 deletions lib/pact_broker/api/resources/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,6 @@ def allowed_methods
["GET", "PUT", "PATCH", "DELETE", "OPTIONS"]
end

def is_conflict?
if (errors = version_service.conflict_errors(version, parsed_version, resource_url)).any?
set_json_validation_error_messages(errors)
else
false
end
end

def resource_exists?
!!version
end
Expand Down
97 changes: 97 additions & 0 deletions lib/pact_broker/db/data_migrations/create_branches.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
require "pact_broker/db/data_migrations/helpers"

module PactBroker
module DB
module DataMigrations
class CreateBranches
extend Helpers

def self.call connection
if required_columns_exist?(connection)
branch_ids = create_branch_versions(connection)
upsert_branch_heads(connection, branch_ids)
end
end

def self.required_columns_exist?(connection)
column_exists?(connection, :versions, :branch) &&
connection.table_exists?(:branches) &&
connection.table_exists?(:branch_versions) &&
connection.table_exists?(:branch_heads)
end

def self.create_branch_versions(connection)
versions_without_a_branch_version(connection).collect do | version |
create_branch_version(connection, version)
end.uniq
end

def self.upsert_branch_heads(connection, branch_ids)
branch_ids.each do | branch_id |
upsert_branch_head(connection, branch_id)
end
end

def self.versions_without_a_branch_version(connection)
branch_versions_join = {
Sequel[:versions][:id] => Sequel[:branch_versions][:version_id],
Sequel[:branch_versions][:branch_name] => Sequel[:versions][:branch]
}

connection[:versions]
.select(Sequel[:versions].*)
.exclude(branch: nil)
.left_outer_join(:branch_versions, branch_versions_join)
.where(Sequel[:branch_versions][:branch_name] => nil)
.order(:pacticipant_id, :order)
end

def self.create_branch_version(connection, version)
branch_values = {
name: version[:branch],
pacticipant_id: version[:pacticipant_id],
created_at: version[:created_at],
updated_at: version[:created_at]
}
connection[:branches].insert_ignore.insert(branch_values)
branch_id = connection[:branches].select(:id).where(pacticipant_id: version[:pacticipant_id], name: version[:branch]).single_record[:id]

branch_version_values = {
pacticipant_id: version[:pacticipant_id],
version_id: version[:id],
version_order: version[:order],
branch_id: branch_id,
branch_name: version[:branch],
created_at: version[:created_at],
updated_at: version[:created_at]
}

connection[:branch_versions].insert_ignore.insert(branch_version_values)
branch_id
end

def self.upsert_branch_head(connection, branch_id)
latest_branch_version = connection[:branch_versions].where(branch_id: branch_id).order(:version_order).last

if connection[:branch_heads].where(branch_id: branch_id).empty?
branch_head_values = {
pacticipant_id: latest_branch_version[:pacticipant_id],
branch_id: branch_id,
branch_version_id: latest_branch_version[:id],
version_id: latest_branch_version[:version_id],
branch_name: latest_branch_version[:branch_name]
}
connection[:branch_heads].insert(branch_head_values)
else
connection[:branch_heads]
.where(branch_id: branch_id)
.update(
branch_version_id: latest_branch_version[:id],
version_id: latest_branch_version[:version_id]
)
end
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/pact_broker/db/migrate_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def self.call database_connection, _options = {}
DataMigrations::SetWebhookUuid.call(database_connection)
DataMigrations::SetConsumerVersionOrderForPactPublications.call(database_connection)
DataMigrations::SetExtraColumnsForTags.call(database_connection)
DataMigrations::CreateBranches.call(database_connection)
end
end
end
Expand Down
Loading