Skip to content

Commit

Permalink
[api] Refactor the attribute controller
Browse files Browse the repository at this point in the history
Extract functions and make the routes match the request - and quite some
followup
  • Loading branch information
coolo committed Jun 26, 2018
1 parent 990fea4 commit 09b8d88
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 142 deletions.
241 changes: 117 additions & 124 deletions src/api/app/controllers/attribute_controller.rb
Expand Up @@ -2,14 +2,16 @@ 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 }
validate_action show_namespace_definition: { method: :get, response: :attribute_namespace_meta }
validate_action delete_namespace_definition: { method: :delete, response: :status }
validate_action update_namespace_definition: { method: :put, request: :attribute_namespace_meta, response: :status }
validate_action update_namespace_definition: { method: :post, request: :attribute_namespace_meta, response: :status }
validate_action show_attribute_definition: { method: :get, response: :attrib_type }
validate_action delete_attribute_definition: { method: :delete, response: :status }
validate_action update_attribute_definition: { method: :put, request: :attrib_type, response: :status }
validate_action update_attribute_definition: { method: :post, request: :attrib_type, response: :status }
before_action :require_admin, only: [:update_namespace_definition, :delete_namespace_definition]
before_action :require_attribute_name, only: [:show_attribute_definition, :update_attribute_definition, :delete_attribute_definition]

def index
if params[:namespace]
Expand All @@ -34,134 +36,76 @@ def index
render xml: xml
end

# /attribute/:namespace/_meta
def namespace_definition
if params[:namespace].nil?
raise MissingParameterError, "parameter 'namespace' is missing"
def show_namespace_definition
if (@an = AttribNamespace.where(name: ensure_namespace).select(:id, :name).first)
render template: 'attribute/namespace_definition'
else
render_error message: "Unknown attribute namespace '#{@namespace}'",
status: 404, errorcode: 'unknown_attribute_namespace'
end
namespace = params[:namespace]
end

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
def delete_namespace_definition
AttribNamespace.where(name: ensure_namespace).destroy_all
render_ok
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'
# /attribute/:namespace/_meta
def update_namespace_definition
xml_element = Xmlhash.parse(request.raw_post)
ensure_namespace

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

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
db = AttribNamespace.where(name: @namespace).first
if db
db.update_from_xml(xml_element)
else
render_error status: 400, errorcode: 'illegal_request',
message: "Illegal request: POST #{request.path}"
AttribNamespace.create(name: @namespace).update_from_xml(xml_element)
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
# GET /attribute/:namespace/:name/_meta
def show_attribute_definition
if (@at = attribute_type)
render template: 'attribute/attribute_definition'
else
render_error message: "Unknown attribute '#{@namespace}':'#{@name}'",
status: 404, errorcode: 'unknown_attribute'
end
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
# DELETE /attribute/:namespace/:name/_meta
# DELETE /attribute/:namespace/:name
def delete_attribute_definition
if (at = attribute_type)
authorize at, :destroy?
at.destroy
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
end

render_ok
elsif request.delete?
at = ans.attrib_types.where('name = ?', name).first
# POST/PUT /attribute/:namespace/:name/_meta
def update_attribute_definition
return unless (xml_element = validate_attribute_definition_xml)

if at
authorize at, :destroy?
at.destroy
end
if (entry = attribute_type)
authorize entry, :update?

render_ok
db = AttribType.find(entry.id) # get a writable object
db.update_from_xml(xml_element)
else
render_error status: 400, errorcode: 'illegal_request',
message: "Illegal request: POST #{request.path}"
create_attribute_definiton(xml_element)
end

render_ok
end

class RemoteProject < APIException
Expand Down Expand Up @@ -233,16 +177,18 @@ def cmd_attribute
find_attribute_container

# init
req = ActiveXML::Node.new(request.raw_post)
req = Xmlhash.parse(request.raw_post)
# Keep compat exception
raise ActiveXML::ParseError unless req

# 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|
# The attribute creation will happen in @attribute_container.store_attribute_xml
req.elements('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)
attr.elements('value') do |value|
attrib.values.new(value: value)
end

attrib.container = @attribute_container
Expand All @@ -256,8 +202,8 @@ def cmd_attribute

# exec
changed = false
req.each('attribute') do |attr|
changed = true if @attribute_container.store_attribute_axml(attr, @binary)
req.elements('attribute') do |attr|
changed = true if @attribute_container.store_attribute_xml(attr, @binary)
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
Expand Down Expand Up @@ -298,4 +244,51 @@ def find_attribute_container
params[:namespace] = name_parts[0]
params[:name] = name_parts[1]
end

private

def ensure_namespace
if params[:namespace].nil?
raise MissingParameterError, "parameter 'namespace' is missing"
end
@namespace = params[:namespace]
end

def require_attribute_namespace
ensure_namespace
@ans = AttribNamespace.where(name: @namespace).first
return true if @ans

render_error status: 400, errorcode: 'unknown_attribute_namespace',
message: "Specified attribute namespace does not exist: '#{namespace}'"
false
end

def require_attribute_name
return unless require_attribute_namespace
if params[:name].nil?
raise MissingParameterError, "parameter 'name' is missing"
end
@name = params[:name]
end

def create_attribute_definiton(xml_element)
entry = AttribType.new(name: @name, attrib_namespace: @ans)
authorize entry, :create?

entry.update_from_xml(xml_element)
end

def attribute_type
@ans.attrib_types.where(name: @name).first
end

def validate_attribute_definition_xml
xml_element = Xmlhash.parse(request.raw_post)

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
12 changes: 6 additions & 6 deletions src/api/app/mixins/has_attributes.rb
Expand Up @@ -20,18 +20,18 @@ def write_attributes(comment = nil)
raise AttributeSaveError, e.summary
end

def store_attribute_axml(attrib, binary = nil)
def store_attribute_xml(attrib, binary = nil)
values = []
attrib.each('value') do |val|
values << val.text
attrib.elements('value') do |val|
values << val
end

issues = []
attrib.each('issue') do |i|
issues << Issue.find_or_create_by_name_and_tracker(i.value('name'), i.value('tracker'))
attrib.elements('issue') do |i|
issues << Issue.find_or_create_by_name_and_tracker(i['name'], i['tracker'])
end

store_attribute(attrib.value('namespace'), attrib.value('name'), values, issues, binary)
store_attribute(attrib['namespace'], attrib['name'], values, issues, binary)
end

def store_attribute(namespace, name, values, issues, binary = nil)
Expand Down
13 changes: 9 additions & 4 deletions src/api/config/routes.rb
Expand Up @@ -493,10 +493,15 @@ def self.public_or_about_path?(request)
get 'attribute' => :index
get 'attribute/:namespace' => :index
# FIXME3.0: drop the POST and DELETE here
match 'attribute/:namespace/_meta' => :namespace_definition, via: [:get, :delete, :post, :put]
match 'attribute/:namespace/:name/_meta' => :attribute_definition, via: [:get, :delete, :post, :put]
delete 'attribute/:namespace' => :namespace_definition
delete 'attribute/:namespace/:name' => :attribute_definition
get 'attribute/:namespace/_meta' => :show_namespace_definition
delete 'attribute/:namespace/_meta' => :delete_namespace_definition
delete 'attribute/:namespace' => :delete_namespace_definition
match 'attribute/:namespace/_meta' => :update_namespace_definition, via: [:post, :put]

get 'attribute/:namespace/:name/_meta' => :show_attribute_definition
delete 'attribute/:namespace/:name/_meta' => :delete_attribute_definition
delete 'attribute/:namespace/:name' => :delete_attribute_definition
match 'attribute/:namespace/:name/_meta' => :update_attribute_definition, via: [:post, :put]

get 'source/:project(/:package(/:binary))/_attribute(/:attribute)' => :show_attribute, constraints: cons
post 'source/:project(/:package(/:binary))/_attribute(/:attribute)' => :cmd_attribute, constraints: cons, as: :change_attribute
Expand Down
4 changes: 2 additions & 2 deletions src/api/test/functional/attributes_test.rb
Expand Up @@ -54,11 +54,11 @@ def test_create_namespace_old
login_Iggy
post '/attribute/TEST/_meta', params: data
assert_response 403
assert_match(/Namespace changes are only permitted by the administrator/, @response.body)
assert_match(/Requires admin privileges/, @response.body)

delete '/attribute/OBS/_meta'
assert_response 403
assert_match(/Namespace changes are only permitted by the administrator/, @response.body)
assert_match(/Requires admin privileges/, @response.body)

login_king
# FIXME3.0: POST is deprecated, use PUT
Expand Down

0 comments on commit 09b8d88

Please sign in to comment.