Skip to content

Commit 04b6955

Browse files
authored
Merge pull request #865 from pivotal-cf-experimental/bsus-pr
Service broker update instance schemas
2 parents 8f54bbb + 54b0e10 commit 04b6955

File tree

19 files changed

+951
-319
lines changed

19 files changed

+951
-319
lines changed

app/models/services/service_plan.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ class ServicePlan < Sequel::Model
66

77
add_association_dependencies service_plan_visibilities: :destroy
88

9-
export_attributes :name, :free, :description, :service_guid, :extra, :unique_id, :public, :bindable, :active, :create_instance_schema
9+
export_attributes :name, :free, :description, :service_guid, :extra, :unique_id, :public, :bindable, :active, :create_instance_schema, :update_instance_schema
1010

1111
export_attributes_from_methods bindable: :bindable?
1212

13-
import_attributes :name, :free, :description, :service_guid, :extra, :unique_id, :public, :bindable, :create_instance_schema
13+
import_attributes :name, :free, :description, :service_guid, :extra, :unique_id, :public, :bindable, :create_instance_schema, :update_instance_schema
1414

1515
strip_attributes :name
1616

app/presenters/v2/service_plan_presenter.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def entity_hash(controller, plan, opts, depth, parents, orphans=nil)
1212
schemas = present_schemas(plan)
1313
entity.merge!(schemas)
1414
entity.delete('create_instance_schema')
15+
entity.delete('update_instance_schema')
1516

1617
entity
1718
end
@@ -20,11 +21,15 @@ def entity_hash(controller, plan, opts, depth, parents, orphans=nil)
2021

2122
def present_schemas(plan)
2223
create_instance_schema = parse_schema(plan.create_instance_schema)
24+
update_instance_schema = parse_schema(plan.update_instance_schema)
2325
{
2426
'schemas' => {
2527
'service_instance' => {
2628
'create' => {
2729
'parameters' => create_instance_schema
30+
},
31+
'update' => {
32+
'parameters' => update_instance_schema
2833
}
2934
}
3035
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Sequel.migration do
2+
change do
3+
add_column :service_plans, :update_instance_schema, :text, null: true
4+
end
5+
end

lib/services/service_brokers/service_manager.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ def update_or_create_plans(catalog)
6565
bindable: catalog_plan.bindable,
6666
active: true,
6767
extra: catalog_plan.metadata.try(:to_json),
68-
create_instance_schema: catalog_plan.schemas.create_instance.try(:to_json)
68+
create_instance_schema: catalog_plan.schemas.create_instance.try(:to_json),
69+
update_instance_schema: catalog_plan.schemas.update_instance.try(:to_json)
6970
})
7071
@services_event_repository.with_service_plan_event(plan) do
7172
plan.save(changed: true)

lib/services/service_brokers/v2.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module VCAP::Services::ServiceBrokers::V2 end
55
require 'services/service_brokers/v2/catalog_service'
66
require 'services/service_brokers/v2/catalog_plan'
77
require 'services/service_brokers/v2/catalog_schemas'
8+
require 'services/service_brokers/v2/schema'
89
require 'services/service_brokers/v2/http_client'
910
require 'services/service_brokers/v2/client'
1011
require 'services/service_brokers/v2/orphan_mitigator'
Lines changed: 50 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,78 @@
1-
require 'json-schema'
2-
31
module VCAP::Services::ServiceBrokers::V2
4-
MAX_SCHEMA_SIZE = 65_536
52
class CatalogSchemas
6-
attr_reader :errors, :create_instance
3+
attr_reader :errors, :create_instance, :update_instance
74

8-
def initialize(schema)
5+
def initialize(schemas)
96
@errors = VCAP::Services::ValidationErrors.new
10-
validate_and_populate_create_instance(schema)
11-
end
12-
13-
def valid?
14-
errors.empty?
15-
end
16-
17-
private
18-
19-
def validate_and_populate_create_instance(schemas)
207
return unless schemas
21-
unless schemas.is_a? Hash
22-
errors.add("Schemas must be a hash, but has value #{schemas.inspect}")
23-
return
24-
end
258

26-
path = []
27-
['service_instance', 'create', 'parameters'].each do |key|
28-
path += [key]
29-
schemas = schemas[key]
30-
return nil unless schemas
9+
return unless validate_structure(schemas, [])
10+
service_instance_path = ['service_instance']
11+
return unless validate_structure(schemas, service_instance_path)
3112

32-
unless schemas.is_a? Hash
33-
errors.add("Schemas #{path.join('.')} must be a hash, but has value #{schemas.inspect}")
34-
return nil
35-
end
36-
end
13+
create_schema = get_method('create', schemas)
14+
@create_instance = Schema.new(create_schema) if create_schema
3715

38-
create_instance_schema = schemas
39-
create_instance_path = path.join('.')
16+
update_schema = get_method('update', schemas)
17+
@update_instance = Schema.new(update_schema) if update_schema
18+
end
4019

41-
validate_schema(create_instance_path, create_instance_schema)
42-
return unless errors.empty?
20+
def valid?
21+
return false unless errors.empty?
4322

44-
@create_instance = create_instance_schema
45-
end
23+
if create_instance && !create_instance.validate
24+
add_schema_validation_errors(create_instance.errors, 'service_instance.create.parameters')
25+
end
4626

47-
def validate_schema(path, schema)
48-
schema_validations.each do |validation|
49-
break if errors.present?
50-
send(validation, path, schema)
27+
if update_instance && !update_instance.validate
28+
add_schema_validation_errors(update_instance.errors, 'service_instance.update.parameters')
5129
end
52-
end
5330

54-
def schema_validations
55-
[
56-
:validate_schema_size,
57-
:validate_metaschema,
58-
:validate_no_external_references,
59-
:validate_schema_type
60-
]
31+
errors.empty?
6132
end
6233

63-
def validate_schema_type(path, schema)
64-
add_schema_error_msg(path, 'must have field "type", with value "object"') if schema['type'] != 'object'
65-
end
34+
private
6635

67-
def validate_schema_size(path, schema)
68-
errors.add("Schema #{path} is larger than 64KB") if schema.to_json.length > MAX_SCHEMA_SIZE
69-
end
36+
def validate_structure(schemas, path)
37+
unless schemas.is_a? Hash
38+
add_schema_type_error_msg(path, schemas)
39+
return false
40+
end
41+
schema = path.reduce(schemas) { |current, key|
42+
return false unless current.key?(key)
43+
current.fetch(key)
44+
}
45+
return false unless schema
46+
47+
unless schema.is_a? Hash
48+
add_schema_type_error_msg(path, schema)
49+
return false
50+
end
7051

71-
def validate_metaschema(path, schema)
72-
JSON::Validator.schema_reader = JSON::Schema::Reader.new(accept_uri: false, accept_file: false)
73-
file = File.read(JSON::Validator.validator_for_name('draft4').metaschema)
52+
true
53+
end
7454

75-
metaschema = JSON.parse(file)
55+
def get_method(method, schema)
56+
path = ['service_instance', method]
57+
return unless validate_structure(schema, path)
7658

77-
begin
78-
errors = JSON::Validator.fully_validate(metaschema, schema)
79-
rescue => e
80-
add_schema_error_msg(path, e)
81-
return nil
82-
end
59+
path = ['service_instance', method, 'parameters']
60+
return unless validate_structure(schema, path)
8361

84-
errors.each do |error|
85-
add_schema_error_msg(path, "Must conform to JSON Schema Draft 04: #{error}")
86-
end
62+
schema['service_instance'][method]['parameters']
8763
end
8864

89-
def validate_no_external_references(path, schema)
90-
JSON::Validator.schema_reader = JSON::Schema::Reader.new(accept_uri: false, accept_file: false)
91-
92-
begin
93-
JSON::Validator.validate!(schema, {})
94-
rescue JSON::Schema::SchemaError => e
95-
add_schema_error_msg(path, "Custom meta schemas are not supported: #{e}")
96-
rescue JSON::Schema::ReadRefused => e
97-
add_schema_error_msg(path, "No external references are allowed: #{e}")
98-
rescue JSON::Schema::ValidationError
99-
# We don't care if our input fails validation on broker schema
100-
rescue => e
101-
add_schema_error_msg(path, e)
65+
def add_schema_validation_errors(schema_errors, path)
66+
schema_errors.messages.each do |_, error_list|
67+
error_list.each do |error_msg|
68+
errors.add("Schema #{path} is not valid. #{error_msg}")
69+
end
10270
end
10371
end
10472

105-
def add_schema_error_msg(path, err)
106-
errors.add("Schema #{path} is not valid. #{err}")
73+
def add_schema_type_error_msg(path, value)
74+
path = path.empty? ? '' : " #{path.join('.')}"
75+
errors.add("Schemas#{path} must be a hash, but has value #{value.inspect}")
10776
end
10877
end
10978
end
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
require 'json-schema'
2+
3+
module VCAP::Services::ServiceBrokers::V2
4+
class Schema
5+
include ActiveModel::Validations
6+
7+
MAX_SCHEMA_SIZE = 65_536
8+
9+
validates :to_json, length: { maximum: MAX_SCHEMA_SIZE, message: 'Must not be larger than 64KB' }
10+
validate :validate_schema_type, :validate_metaschema, :validate_no_external_references
11+
12+
def initialize(schema)
13+
@schema = schema
14+
end
15+
16+
def to_json
17+
@schema.to_json
18+
end
19+
20+
private
21+
22+
def validate_schema_type
23+
return unless errors.blank?
24+
add_schema_error_msg('must have field "type", with value "object"') if @schema['type'] != 'object'
25+
end
26+
27+
def validate_metaschema
28+
return unless errors.blank?
29+
JSON::Validator.schema_reader = JSON::Schema::Reader.new(accept_uri: false, accept_file: false)
30+
file = File.read(JSON::Validator.validator_for_name('draft4').metaschema)
31+
32+
metaschema = JSON.parse(file)
33+
34+
begin
35+
errors = JSON::Validator.fully_validate(metaschema, @schema, errors_as_objects: true)
36+
rescue => e
37+
add_schema_error_msg(e)
38+
return nil
39+
end
40+
41+
errors.each do |error|
42+
add_schema_error_msg("Must conform to JSON Schema Draft 04: #{error[:message]}")
43+
end
44+
end
45+
46+
def validate_no_external_references
47+
return unless errors.blank?
48+
JSON::Validator.schema_reader = JSON::Schema::Reader.new(accept_uri: false, accept_file: false)
49+
50+
begin
51+
JSON::Validator.validate!(@schema, {})
52+
rescue JSON::Schema::SchemaError
53+
add_schema_error_msg('Custom meta schemas are not supported.')
54+
rescue JSON::Schema::ReadRefused => e
55+
add_schema_error_msg("No external references are allowed: #{e}")
56+
rescue JSON::Schema::ValidationError
57+
# We don't care if our input fails validation on broker schema
58+
rescue => e
59+
add_schema_error_msg(e)
60+
end
61+
end
62+
63+
def add_schema_error_msg(err)
64+
errors.add(:base, err.to_s)
65+
end
66+
end
67+
end

0 commit comments

Comments
 (0)