Skip to content

Commit

Permalink
feat: add repository and branch properties to pacticipant
Browse files Browse the repository at this point in the history
* feat: add repository and display name properties to pacticipant

* feat: add mainDevelopmentBranches to pacticipant

* feat: automatically populate display name for pacticipants and migrate existing data

* chore: validate put pacticipant
  • Loading branch information
bethesque authored Mar 1, 2021
1 parent 8666d70 commit 98a799b
Show file tree
Hide file tree
Showing 27 changed files with 488 additions and 184 deletions.
10 changes: 10 additions & 0 deletions db/migrations/20210216_add_pacticipant_columns.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Sequel.migration do
change do
alter_table(:pacticipants) do
add_column(:display_name, String)
add_column(:repository_name, String)
add_column(:repository_namespace, String)
add_column(:main_development_branches, String)
end
end
end
9 changes: 9 additions & 0 deletions lib/pact_broker/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ class Request
def patch?
method == "PATCH"
end

# This makes PATCH go through the PUT state machine path
def put?
method == "PUT" || method == "PATCH"
end

def really_put?
method == "PUT"
end
end
end

Expand Down
9 changes: 7 additions & 2 deletions lib/pact_broker/api/contracts/dry_validation_workarounds.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ def select_first_message(messages)
end

def flatten_array_of_hashes(array_of_hashes)
new_messages = array_of_hashes.collect do | index, hash |
hash.values.flatten.collect { | text | "#{text} at index #{index}"}
new_messages = array_of_hashes.collect do | index, hash_or_array |
array = if hash_or_array.is_a?(Hash)
hash_or_array.values.flatten
else
hash_or_array
end
array.collect { | text | "#{text} at index #{index}"}
end.flatten
end

Expand Down
34 changes: 34 additions & 0 deletions lib/pact_broker/api/contracts/pacticipant_schema.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require 'dry-validation'
require 'pact_broker/api/contracts/dry_validation_workarounds'
require 'pact_broker/api/contracts/dry_validation_predicates'
require 'pact_broker/messages'

module PactBroker
module Api
module Contracts
class PacticipantSchema
extend DryValidationWorkarounds
extend PactBroker::Messages
using PactBroker::HashRefinements

SCHEMA = Dry::Validation.Schema do
configure do
predicates(DryValidationPredicates)
config.messages_file = File.expand_path("../../../locale/en.yml", __FILE__)
end
optional(:name).filled(:str?, :single_line?)
optional(:displayName).maybe(:str?, :single_line?, :not_blank?)
optional(:mainDevelopmentBranches).each(:str?, :single_line?, :no_spaces?)
optional(:repositoryUrl).maybe(:str?, :single_line?)
optional(:repositoryName).maybe(:str?, :single_line?)
optional(:repositoryNamespace).maybe(:str?, :single_line?)
end

def self.call(params_with_string_keys)
params = params_with_string_keys&.symbolize_keys
select_first_message(flatten_indexed_messages(SCHEMA.call(params).messages(full: true)))
end
end
end
end
end
7 changes: 5 additions & 2 deletions lib/pact_broker/api/decorators/pacticipant_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ module PactBroker
module Api
module Decorators
class PacticipantDecorator < BaseDecorator

property :name
property :repository_url, as: :repositoryUrl
property :display_name, camelize: true
property :repository_url, camelize: true
property :repository_name, camelize: true
property :repository_namespace, camelize: true
property :main_development_branches, camelize: true

property :latest_version, as: :latestVersion, :class => PactBroker::Domain::Version, extend: PactBroker::Api::Decorators::EmbeddedVersionDecorator, embedded: true, writeable: false
collection :labels, :class => PactBroker::Domain::Label, extend: PactBroker::Api::Decorators::EmbeddedLabelDecorator, embedded: true
Expand Down
8 changes: 6 additions & 2 deletions lib/pact_broker/api/resources/default_base_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,12 @@ def decorator_class(name)
application_context.decorator_configuration.class_for(name)
end

def validation_errors_for_schema?(schema, params_to_validate = params)
if (errors = schema.call(params_to_validate)).any?
def schema
nil
end

def validation_errors_for_schema?(schema_to_use = schema, params_to_validate = params)
if (errors = schema_to_use.call(params_to_validate)).any?
set_json_validation_error_messages(errors)
true
else
Expand Down
49 changes: 37 additions & 12 deletions lib/pact_broker/api/resources/pacticipant.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
require 'pact_broker/api/resources/base_resource'

module Webmachine
class Request
def put?
method == "PUT" || method == "PATCH"
end
end
end
require 'pact_broker/api/contracts/pacticipant_schema'

module PactBroker
module Api
Expand All @@ -22,19 +15,31 @@ def content_types_accepted
end

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

def known_methods
super + ['PATCH']
end

def malformed_request?
if request.patch? || request.put?
invalid_json? || validation_errors_for_schema?
else
false
end
end

def from_json
if pacticipant
@pacticipant = pacticipant_service.update params(symbolize_names: false).merge('name' => pacticipant_name)
@pacticipant = update_existing_pacticipant
else
@pacticipant = pacticipant_service.create params.merge(:name => pacticipant_name)
response.headers["Location"] = pacticipant_url(base_url, pacticipant)
if request.patch? # for backwards compatibility, wish I hadn't done this
@pacticipant = create_new_pacticipant
response.headers["Location"] = pacticipant_url(base_url, pacticipant)
else
return 404
end
end
response.body = to_json
end
Expand All @@ -52,9 +57,29 @@ def to_json
decorator_class(:pacticipant_decorator).new(pacticipant).to_json(decorator_options)
end

def parsed_pacticipant(pacticipant)
decorator_class(:pacticipant_decorator).new(pacticipant).from_json(request_body)
end

def policy_name
:'pacticipants::pacticipant'
end

def schema
PactBroker::Api::Contracts::PacticipantSchema
end

def update_existing_pacticipant
if request.really_put?
@pacticipant = pacticipant_service.replace(pacticipant_name, parsed_pacticipant(OpenStruct.new))
else
@pacticipant = pacticipant_service.update(pacticipant_name, parsed_pacticipant(pacticipant))
end
end

def create_new_pacticipant
pacticipant_service.create parsed_pacticipant(OpenStruct.new).to_h.merge(:name => pacticipant_name)
end
end
end
end
Expand Down
13 changes: 9 additions & 4 deletions lib/pact_broker/api/resources/pacticipants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require 'pact_broker/api/decorators/pacticipant_decorator'
require 'pact_broker/domain/pacticipant'
require 'pact_broker/hash_refinements'
require 'pact_broker/api/contracts/pacticipant_schema'

module PactBroker
module Api
Expand All @@ -23,7 +24,7 @@ def allowed_methods

def malformed_request?
if request.post?
return invalid_json? || validation_errors?(new_model)
return invalid_json? || validation_errors_for_schema?
end
false
end
Expand All @@ -33,7 +34,7 @@ def post_is_create?
end

def from_json
created_model = pacticipant_service.create(params.symbolize_keys.snakecase_keys.slice(:name, :repository_url))
created_model = pacticipant_service.create(parsed_pacticipant.to_h)
response.body = decorator_for(created_model).to_json(decorator_options)
end

Expand All @@ -53,8 +54,8 @@ def decorator_for model
decorator_class(:pacticipant_decorator).new(model)
end

def new_model
@new_model ||= decorator_for(PactBroker::Domain::Pacticipant.new).from_json(request.body.to_s)
def parsed_pacticipant
@new_model ||= decorator_for(OpenStruct.new).from_json(request_body)
end

def policy_name
Expand All @@ -63,6 +64,10 @@ def policy_name

private

def schema
PactBroker::Api::Contracts::PacticipantSchema
end

def pacticipants
@pacticipants ||= pacticipant_service.find_all_pacticipants
end
Expand Down
23 changes: 23 additions & 0 deletions lib/pact_broker/db/data_migrations/set_pacticipant_display_name.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require 'pact_broker/db/data_migrations/helpers'
require 'pact_broker/pacticipants/generate_display_name'

module PactBroker
module DB
module DataMigrations
class SetPacticipantDisplayName
extend Helpers
extend PactBroker::Pacticipants::GenerateDisplayName

def self.call(connection)
if columns_exist?(connection, :pacticipants, [:name, :display_name])
connection[:pacticipants].where(display_name: nil).each do | row |
connection[:pacticipants]
.where(id: row[:id])
.update(display_name: generate_display_name(row[:name]))
end
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 @@ -22,6 +22,7 @@ def self.call database_connection, options = {}
DataMigrations::SetCreatedAtForLatestPactPublications.call(database_connection)
DataMigrations::SetCreatedAtForLatestVerifications.call(database_connection)
DataMigrations::SetExtraColumnsForTags.call(database_connection)
DataMigrations::SetPacticipantDisplayName.call(database_connection)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/pact_broker/deployments/environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
module PactBroker
module Deployments
class Environment < Sequel::Model
OPEN_STRUCT_TO_JSON = lambda { |thing| Sequel.object_to_json(thing.collect(&:to_h)) }
OPEN_STRUCT_TO_JSON = lambda { | open_struct | Sequel.object_to_json(open_struct.collect(&:to_h)) }
JSON_TO_OPEN_STRUCT = lambda { | json | Sequel.parse_json(json).collect{ | hash| OpenStruct.new(hash) } }
plugin :upsert, identifying_columns: [:uuid]
plugin :serialization
Expand Down
25 changes: 17 additions & 8 deletions lib/pact_broker/domain/pacticipant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,22 @@
require 'pact_broker/repositories/helpers'
require 'pact_broker/versions/latest_version'
require 'pact_broker/domain/label'
require 'pact_broker/string_refinements'
require 'pact_broker/pacticipants/generate_display_name'

module PactBroker
module Domain
class Pacticipant < Sequel::Model

include Messages
plugin :insert_ignore, identifying_columns: [:name]
include PactBroker::Pacticipants::GenerateDisplayName
using PactBroker::StringRefinements

plugin :serialization
SPACE_DELIMITED_STRING_TO_ARRAY = lambda { |string| string.split(" ") }
ARRAY_TO_SPACE_DELIMITED_STRING = lambda { |array| array.join(" ") }
serialize_attributes [ARRAY_TO_SPACE_DELIMITED_STRING, SPACE_DELIMITED_STRING_TO_ARRAY], :main_development_branches

plugin :insert_ignore, identifying_columns: [:name]
plugin :timestamps, update_on_create: true

set_primary_key :id
Expand Down Expand Up @@ -42,6 +50,13 @@ def before_destroy
super
end

def before_save
super
if display_name.nil? || display_name.to_s.blank?
self.display_name = generate_display_name(name)
end
end

def latest_version
versions.last
end
Expand All @@ -50,12 +65,6 @@ def to_s
"Pacticipant: id=#{id}, name=#{name}"
end

def validate
messages = []
messages << message('errors.validation.attribute_missing', attribute: 'name') unless name
messages
end

def any_versions?
PactBroker::Domain::Version.where(pacticipant: self).any?
end
Expand Down
27 changes: 27 additions & 0 deletions lib/pact_broker/pacticipants/generate_display_name.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require 'pact_broker/string_refinements'

module PactBroker
module Pacticipants
module GenerateDisplayName
using PactBroker::StringRefinements

def self.call(name)
return nil if name.nil?
name
.to_s
.gsub(/([A-Z])([A-Z])([a-z])/,'\1 \2\3')
.gsub(/([a-z\d])([A-Z])(\S)/,'\1 \2\3')
.gsub(/(\S)([\-_\s\.])(\S)/, '\1 \3')
.gsub(/\s+/, " ")
.strip
.split(" ")
.collect{ |word| word.camelcase(true) }
.join(" ")
end

def generate_display_name(name)
GenerateDisplayName.call(name)
end
end
end
end
Loading

0 comments on commit 98a799b

Please sign in to comment.