-
Notifications
You must be signed in to change notification settings - Fork 437
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5165 from coolo/attribute_events2
More attribute refactoring
- Loading branch information
Showing
25 changed files
with
460 additions
and
405 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,301 +1,73 @@ | ||
class AttributeController < ApplicationController | ||
include ValidationHelper | ||
|
||
validate_action index: { method: :get, response: :directory } | ||
validate_action namespace_definition: { method: :get, response: :attribute_namespace_meta } | ||
validate_action namespace_definition: { method: :delete, response: :status } | ||
validate_action namespace_definition: { method: :put, request: :attribute_namespace_meta, response: :status } | ||
validate_action namespace_definition: { method: :post, request: :attribute_namespace_meta, response: :status } | ||
validate_action attribute_definition: { method: :get, response: :attrib_type } | ||
validate_action attribute_definition: { method: :delete, response: :status } | ||
validate_action attribute_definition: { method: :put, request: :attrib_type, response: :status } | ||
validate_action attribute_definition: { method: :post, request: :attrib_type, response: :status } | ||
|
||
def index | ||
if params[:namespace] | ||
an = AttribNamespace.where(name: params[:namespace]).first | ||
unless an | ||
render_error status: 400, errorcode: 'unknown_namespace', | ||
message: "Attribute namespace does not exist: #{params[:namespace]}" | ||
return | ||
end | ||
list = an.attrib_types.pluck(:name) | ||
validate_action show: { method: :get, response: :attrib_type } | ||
validate_action delete: { method: :delete, response: :status } | ||
validate_action update: { method: :put, request: :attrib_type, response: :status } | ||
validate_action update: { method: :post, request: :attrib_type, response: :status } | ||
before_action :load_attribute, only: [:show, :update, :delete] | ||
|
||
# GET /attribute/:namespace/:name/_meta | ||
def show | ||
if @at | ||
render template: 'attribute/show' | ||
else | ||
list = AttribNamespace.pluck(:name) | ||
end | ||
|
||
builder = Builder::XmlMarkup.new(indent: 2) | ||
xml = builder.directory(count: list.length) do |dir| | ||
list.each do |a| | ||
dir.entry(name: a) | ||
end | ||
render_error message: "Unknown attribute '#{@namespace}':'#{@name}'", | ||
status: 404, errorcode: 'unknown_attribute' | ||
end | ||
|
||
render xml: xml | ||
end | ||
|
||
# /attribute/:namespace/_meta | ||
def namespace_definition | ||
if params[:namespace].nil? | ||
raise MissingParameterError, "parameter 'namespace' is missing" | ||
end | ||
namespace = params[:namespace] | ||
|
||
if request.get? | ||
@an = AttribNamespace.where(name: namespace).select(:id, :name).first | ||
unless @an | ||
render_error message: "Unknown attribute namespace '#{namespace}'", | ||
status: 404, errorcode: 'unknown_attribute_namespace' | ||
end | ||
return | ||
end | ||
|
||
# namespace definitions must be managed by the admin | ||
return unless extract_user | ||
unless User.current.is_admin? | ||
render_error status: 403, errorcode: 'permissions denied', | ||
message: 'Namespace changes are only permitted by the administrator' | ||
return | ||
# DELETE /attribute/:namespace/:name/_meta | ||
# DELETE /attribute/:namespace/:name | ||
def delete | ||
if @at | ||
authorize @at, :destroy? | ||
@at.destroy | ||
end | ||
|
||
if request.post? || request.put? | ||
logger.debug '--- updating attribute namespace definitions ---' | ||
|
||
xml_element = Xmlhash.parse(request.raw_post) | ||
|
||
unless xml_element['name'] == namespace | ||
render_error status: 400, errorcode: 'illegal_request', | ||
message: "Illegal request: PUT/POST #{request.path}: path does not match content" | ||
return | ||
end | ||
|
||
db = AttribNamespace.where(name: namespace).first | ||
if db | ||
logger.debug '* updating existing attribute namespace' | ||
db.update_from_xml(xml_element) | ||
else | ||
logger.debug '* create new attribute namespace' | ||
AttribNamespace.create(name: namespace).update_from_xml(xml_element) | ||
end | ||
|
||
logger.debug '--- finished updating attribute namespace definitions ---' | ||
render_ok | ||
elsif request.delete? | ||
AttribNamespace.where(name: namespace).destroy_all | ||
render_ok | ||
else | ||
render_error status: 400, errorcode: 'illegal_request', | ||
message: "Illegal request: POST #{request.path}" | ||
end | ||
render_ok | ||
end | ||
|
||
# /attribute/:namespace/:name/_meta | ||
def attribute_definition | ||
if params[:namespace].nil? | ||
raise MissingParameterError, "parameter 'namespace' is missing" | ||
end | ||
if params[:name].nil? | ||
raise MissingParameterError, "parameter 'name' is missing" | ||
end | ||
namespace = params[:namespace] | ||
name = params[:name] | ||
ans = AttribNamespace.where(name: namespace).first | ||
unless ans | ||
render_error status: 400, errorcode: 'unknown_attribute_namespace', | ||
message: "Specified attribute namespace does not exist: '#{namespace}'" | ||
return | ||
end | ||
|
||
if request.get? | ||
@at = ans.attrib_types.find_by(name: name) | ||
unless @at | ||
render_error message: "Unknown attribute '#{namespace}':'#{name}'", | ||
status: 404, errorcode: 'unknown_attribute' | ||
end | ||
return | ||
end | ||
|
||
# permission check via User model | ||
return unless extract_user | ||
|
||
if request.post? || request.put? | ||
logger.debug '--- updating attribute type definitions ---' | ||
|
||
xml_element = Xmlhash.parse(request.raw_post) | ||
|
||
unless xml_element && xml_element['name'] == name && xml_element['namespace'] == namespace | ||
render_error status: 400, errorcode: 'illegal_request', | ||
message: "Illegal request: PUT/POST #{request.path}: path does not match content" | ||
return | ||
end | ||
|
||
entry = ans.attrib_types.where('name = ?', name).first | ||
|
||
if entry | ||
authorize entry, :update? | ||
|
||
db = AttribType.find(entry.id) # get a writable object | ||
logger.debug '* updating existing attribute definitions' | ||
db.update_from_xml(xml_element) | ||
else | ||
entry = AttribType.new(name: name, attrib_namespace: ans) | ||
authorize entry, :create? | ||
|
||
logger.debug '* create new attribute definition' | ||
entry.update_from_xml(xml_element) | ||
end | ||
|
||
logger.debug '--- finished updating attribute namespace definitions ---' | ||
#--- end update attribute namespace definitions ---# | ||
|
||
render_ok | ||
elsif request.delete? | ||
at = ans.attrib_types.where('name = ?', name).first | ||
# POST/PUT /attribute/:namespace/:name/_meta | ||
def update | ||
return unless (xml_element = validate_xml) | ||
|
||
if at | ||
authorize at, :destroy? | ||
at.destroy | ||
end | ||
|
||
render_ok | ||
if @at | ||
authorize entry, :update? | ||
@at.update_from_xml(xml_element) | ||
else | ||
render_error status: 400, errorcode: 'illegal_request', | ||
message: "Illegal request: POST #{request.path}" | ||
end | ||
end | ||
|
||
class RemoteProject < APIException | ||
setup 400, 'Attribute access to remote project is not yet supported' | ||
end | ||
|
||
class InvalidAttribute < APIException | ||
end | ||
|
||
# GET | ||
# /source/:project/_attribute/:attribute | ||
# /source/:project/:package/_attribute/:attribute | ||
# /source/:project/:package/:binary/_attribute/:attribute | ||
#-------------------------------------------------------- | ||
def show_attribute | ||
find_attribute_container | ||
|
||
# init | ||
# checks | ||
# exec | ||
if params[:rev] || params[:meta] || params[:view] || @attribute_container.nil? | ||
# old or remote instance entry | ||
render xml: Backend::Api::Sources::Package.attributes(params[:project], params[:package], params) | ||
return | ||
end | ||
|
||
render xml: @attribute_container.render_attribute_axml(params) | ||
end | ||
|
||
# DELETE | ||
# /source/:project/_attribute/:attribute | ||
# /source/:project/:package/_attribute/:attribute | ||
# /source/:project/:package/:binary/_attribute/:attribute | ||
#-------------------------------------------------------- | ||
def delete_attribute | ||
find_attribute_container | ||
|
||
# init | ||
if params[:namespace].blank? || params[:name].blank? | ||
render_error status: 400, errorcode: 'missing_attribute', | ||
message: 'No attribute got specified for delete' | ||
return | ||
end | ||
ac = @attribute_container.find_attribute(params[:namespace], params[:name], @binary) | ||
|
||
# checks | ||
unless ac | ||
render_error(status: 404, errorcode: 'not_found', | ||
message: "Attribute #{params[:attribute]} does not exist") && return | ||
end | ||
unless User.current.can_create_attribute_in? @attribute_container, namespace: params[:namespace], name: params[:name] | ||
render_error status: 403, errorcode: 'change_attribute_no_permission', | ||
message: "user #{user.login} has no permission to change attribute" | ||
return | ||
create(xml_element) | ||
end | ||
|
||
# exec | ||
ac.destroy | ||
@attribute_container.write_attributes(params[:comment]) | ||
render_ok | ||
end | ||
|
||
# POST | ||
# /source/:project/_attribute/:attribute | ||
# /source/:project/:package/_attribute/:attribute | ||
# /source/:project/:package/:binary/_attribute/:attribute | ||
#-------------------------------------------------------- | ||
def cmd_attribute | ||
find_attribute_container | ||
|
||
# init | ||
req = ActiveXML::Node.new(request.raw_post) | ||
|
||
# This is necessary for checking the authorization and do not create the attribute | ||
# The attribute creation will happen in @attribute_container.store_attribute_axml | ||
req.each('attribute') do |attr| | ||
attrib_type = AttribType.find_by_namespace_and_name!(attr.value('namespace'), attr.value('name')) | ||
attrib = Attrib.new(attrib_type: attrib_type) | ||
|
||
attr.each('value') do |value| | ||
attrib.values.new(value: value.text) | ||
end | ||
|
||
attrib.container = @attribute_container | ||
|
||
unless attrib.valid? | ||
raise APIException, message: attrib.errors.full_messages.join('\n'), status: 400 | ||
end | ||
|
||
authorize attrib, :create? | ||
end | ||
private | ||
|
||
# exec | ||
changed = false | ||
req.each('attribute') do |attr| | ||
changed = true if @attribute_container.store_attribute_axml(attr, @binary) | ||
def load_attribute | ||
@namespace = params[:namespace] | ||
@ans = AttribNamespace.find_by_name!(@namespace) | ||
if params[:name].nil? | ||
raise MissingParameterError, "parameter 'name' is missing" | ||
end | ||
logger.debug "Attributes for #{@attribute_container.class} #{@attribute_container.name} changed, writing to backend" if changed | ||
@attribute_container.write_attributes(params[:comment]) if changed | ||
render_ok | ||
@name = params[:name] | ||
# find_by_name is something else (of course) | ||
@at = @ans.attrib_types.where(name: @name).first | ||
end | ||
|
||
protected | ||
|
||
before_action :require_valid_project_name, only: [:find_attribute_container] | ||
def create(xml_element) | ||
entry = AttribType.new(name: @name, attrib_namespace: @ans) | ||
authorize entry, :create? | ||
|
||
def find_attribute_container | ||
# init and validation | ||
#-------------------- | ||
params[:user] = User.current.login if User.current | ||
@binary = nil | ||
@binary = params[:binary] if params[:binary] | ||
# valid post commands | ||
if params[:package] && params[:package] != '_project' | ||
@attribute_container = Package.get_by_project_and_name(params[:project], params[:package], use_source: false) | ||
else | ||
# project | ||
raise RemoteProject if Project.is_remote_project?(params[:project]) | ||
@attribute_container = Project.get_by_name(params[:project]) | ||
end | ||
entry.update_from_xml(xml_element) | ||
end | ||
|
||
# is the attribute type defined at all ? | ||
return if params[:attribute].blank? | ||
def validate_xml | ||
xml_element = Xmlhash.parse(request.raw_post) | ||
|
||
# Valid attribute | ||
aname = params[:attribute] | ||
name_parts = aname.split(/:/) | ||
if name_parts.length != 2 | ||
raise InvalidAttribute, "attribute '#{aname}' must be in the $NAMESPACE:$NAME style" | ||
end | ||
# existing ? | ||
AttribType.find_by_name!(params[:attribute]) | ||
# only needed for a get request | ||
params[:namespace] = name_parts[0] | ||
params[:name] = name_parts[1] | ||
return xml_element if xml_element && xml_element['name'] == @name && xml_element['namespace'] == @namespace | ||
render_error status: 400, errorcode: 'illegal_request', | ||
message: "Illegal request: PUT/POST #{request.path}: path does not match content" | ||
return | ||
end | ||
end |
Oops, something went wrong.