app/controllers/services/service_plans_controller.rb
module VCAP::CloudController
class ServicePlansController < RestController::ModelController
define_attributes do
attribute :name, String
attribute :free, Message::Boolean
attribute :description, String
attribute :extra, String, default: nil
attribute :unique_id, String, default: nil
to_one :service
to_many :service_instances
attribute :public, Message::Boolean, default: true
end
query_parameters :active, :service_guid, :service_instance_guid, :service_broker_guid, :unique_id
# added :service_broker_guid here for readability, it is actually implemented as a search filter
# in the #get_filtered_dataset_for_enumeration method because ModelControl does not support
# searching on parameters that are not directly associated with the model
allow_unauthenticated_access only: :enumerate
def enumerate
if SecurityContext.missing_token?
raise CloudController::Errors::NotAuthenticated if VCAP::CloudController::FeatureFlag.enabled?(:hide_marketplace_from_unauthenticated_users)
single_filter = @opts[:q][0] if @opts[:q]
service_guid = single_filter.split(':')[1] if single_filter && single_filter.start_with?('service_guid')
plans = ServicePlan.where(active: true, public: true)
if service_guid.present?
services = Service.where(guid: service_guid)
plans = plans.where(service_id: services.select(:id))
end
@opts.delete(:inline_relations_depth)
collection_renderer.render_json(
self.class,
plans,
self.class.path,
@opts,
{}
)
elsif SecurityContext.invalid_token?
raise CloudController::Errors::InvalidAuthToken
else
super
end
end
# # Read operation
# #
# # @param [String] guid The GUID of the object to read.
# This method is needed so the service plan is read during rendering
# with the same service-instance-based privilege escalation that
# is used when access to the object is validated.
def read(guid)
obj = find_guid(guid)
validate_access(:read, obj)
object_renderer.render_json_with_read_privileges(self.class, obj, @opts)
end
def create
404
end
def update(guid)
json_msg = self.class::UpdateMessage.decode(body)
@request_attrs = json_msg.extract(stringify_keys: true).select { |key, _value| key == 'public' }
logger.debug 'cc.update', guid: guid, attributes: redact_attributes(:update, request_attrs)
raise InvalidRequest unless request_attrs
obj = find_guid(guid)
before_update(obj)
model.db.transaction do
obj.lock!
validate_access(:read_for_update, obj, request_attrs)
obj.update_from_hash(request_attrs)
validate_access(:update, obj, request_attrs)
end
after_update(obj)
[HTTP::CREATED, object_renderer.render_json(self.class, obj, @opts)]
end
def delete(guid)
plan = find_guid_and_validate_access(:delete, guid)
raise CloudController::Errors::ApiError.new_from_details('AssociationNotEmpty', 'service_instances', plan.class.table_name) if plan.service_instances.present?
plan.destroy
[HTTP::NO_CONTENT, nil]
end
def self.translate_validation_exception(e, attributes)
name_errors = e.errors.on(%i[service_id name])
if name_errors && name_errors.include?(:unique)
CloudController::Errors::ApiError.new_from_details('ServicePlanNameTaken', "#{attributes['service_id']}-#{attributes['name']}")
else
CloudController::Errors::ApiError.new_from_details('ServicePlanInvalid', e.errors.full_messages)
end
end
def get_filtered_dataset_for_enumeration(model, dataset, query_params, opts)
single_filter = opts[:q][0] if opts[:q]
if single_filter && single_filter.start_with?('service_broker_guid')
service_broker_guid = single_filter.split(/(:| IN )/)[2]
Query.
filtered_dataset_from_query_params(model, dataset, query_params, { q: '' }).
select_all(:service_plans).
left_join(:services, id: :service_plans__service_id).
left_join(:service_brokers, id: :services__service_broker_id).
where(service_brokers__guid: service_broker_guid)
else
super
end
end
define_messages
define_routes
end
end