class ChefZero::RestBase
Constants
- DEFAULT_REQUEST_VERSION
- DEFAULT_RESPONSE_VERSION
Attributes
Public Class Methods
# File lib/chef_zero/rest_base.rb, line 286 def self.build_uri(base_uri, rest_path) "#{base_uri}/#{rest_path.map { |v| URI.escape(v) }.join('/')}" end
# File lib/chef_zero/rest_base.rb, line 11 def initialize(server) @server = server end
Public Instance Methods
# File lib/chef_zero/rest_base.rb, line 70 def accepts?(request, category, type) # If HTTP_ACCEPT is not sent at all, assume it accepts anything # This parses as per http://tools.ietf.org/html/rfc7231#section-5.3 return true if !request.env["HTTP_ACCEPT"] accepts = request.env["HTTP_ACCEPT"].split(/,\s*/).map { |x| x.split(";", 2)[0].strip } accepts.include?("#{category}/#{type}") || accepts.include?("#{category}/*") || accepts.include?("*/*") end
Returns an Array with the response code, HTTP headers, and JSON body.
@param [Fixnum] response_code The HTTP response code @param [String] json_text The JSON body for the response @param [Hash] options @option options [Hash] :headers ({}) HTTP headers (may override default headers) @option options [Fixnum] :request_version (0) Request API version @option options [Fixnum] :response_version (0) Response API version
@return [Array(Fixnum, Hash{String => String}, String)]
# File lib/chef_zero/rest_base.rb, line 255 def already_json_response(response_code, json_text, options = {}) version_header = FFI_Yajl::Encoder.encode( "min_version" => MIN_API_VERSION.to_s, "max_version" => MAX_API_VERSION.to_s, "request_version" => options[:request_version] || DEFAULT_REQUEST_VERSION.to_s, "response_version" => options[:response_version] || DEFAULT_RESPONSE_VERSION.to_s ) headers = { "Content-Type" => "application/json", "X-Ops-Server-API-Version" => version_header, } headers.merge!(options[:headers]) if options[:headers] [ response_code, headers, json_text ] end
To be called from inside rest endpoints
# File lib/chef_zero/rest_base.rb, line 273 def build_uri(base_uri, rest_path) if server.options[:single_org] # Strip off /organizations/chef if we are in single org mode if rest_path[0..1] != [ "organizations", server.options[:single_org] ] raise "Unexpected URL #{rest_path[0..1]} passed to build_uri in single org mode" end return self.class.build_uri(base_uri, rest_path[2..-1]) end self.class.build_uri(base_uri, rest_path) end
# File lib/chef_zero/rest_base.rb, line 44 def call(request) response = check_api_version(request) return response unless response.nil? method = request.method.downcase.to_sym if !respond_to?(method) accept_methods = [:get, :put, :post, :delete].select { |m| respond_to?(m) } accept_methods_str = accept_methods.map { |m| m.to_s.upcase }.join(", ") return [405, { "Content-Type" => "text/plain", "Allow" => accept_methods_str }, "Bad request method for '#{request.env['REQUEST_PATH']}': #{request.env['REQUEST_METHOD']}"] end if json_only && !accepts?(request, "application", "json") return [406, { "Content-Type" => "text/plain" }, "Must accept application/json"] end # Dispatch to get()/post()/put()/delete() begin send(method, request) rescue RestErrorResponse => e ChefZero::Log.debug("#{e.inspect}\n#{e.backtrace.join("\n")}") error(e.response_code, e.error) end end
# File lib/chef_zero/rest_base.rb, line 21 def check_api_version(request) version = request.api_version if version > MAX_API_VERSION || version < MIN_API_VERSION response = { "error" => "invalid-x-ops-server-api-version", "message" => "Specified version #{version} not supported", "min_api_version" => MIN_API_VERSION, "max_api_version" => MAX_API_VERSION, } return json_response(406, response, request_version: version, response_version: -1 ) end rescue ArgumentError json_response(406, { "username" => request.requestor }, request_version: -1, response_version: -1 ) end
# File lib/chef_zero/rest_base.rb, line 177 def create_data(request, rest_path, name, data, *options) rest_path ||= request.rest_path begin data_store.create(rest_path, name, data, *options, :requestor => request.requestor) rescue DataStore::DataNotFoundError if options.include?(:data_store_exceptions) raise else raise RestErrorResponse.new(404, "Parent not found: #{build_uri(request.base_uri, rest_path)}") end rescue DataStore::DataAlreadyExistsError if options.include?(:data_store_exceptions) raise else raise RestErrorResponse.new(409, "Object already exists: #{build_uri(request.base_uri, rest_path + [name])}") end end end
# File lib/chef_zero/rest_base.rb, line 158 def create_data_dir(request, rest_path, name, *options) rest_path ||= request.rest_path begin data_store.create_dir(rest_path, name, *options, :requestor => request.requestor) rescue DataStore::DataNotFoundError if options.include?(:data_store_exceptions) raise else raise RestErrorResponse.new(404, "Parent not found: #{build_uri(request.base_uri, rest_path)}") end rescue DataStore::DataAlreadyExistsError if options.include?(:data_store_exceptions) raise else raise RestErrorResponse.new(409, "Object already exists: #{build_uri(request.base_uri, rest_path + [name])}") end end end
# File lib/chef_zero/rest_base.rb, line 17 def data_store server.data_store end
# File lib/chef_zero/rest_base.rb, line 107 def delete_data(request, rest_path = nil, *options) rest_path ||= request.rest_path begin data_store.delete(rest_path, *options) rescue DataStore::DataNotFoundError if options.include?(:data_store_exceptions) raise else raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, rest_path)}") end end begin acl_path = ChefData::AclPath.get_acl_data_path(rest_path) data_store.delete(acl_path) if acl_path rescue DataStore::DataNotFoundError end end
# File lib/chef_zero/rest_base.rb, line 126 def delete_data_dir(request, rest_path, *options) rest_path ||= request.rest_path begin data_store.delete_dir(rest_path, *options) rescue DataStore::DataNotFoundError if options.include?(:data_store_exceptions) raise else raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, rest_path)}") end end begin acl_path = ChefData::AclPath.get_acl_data_path(rest_path) data_store.delete(acl_path) if acl_path rescue DataStore::DataNotFoundError end end
# File lib/chef_zero/rest_base.rb, line 206 def error(response_code, error, opts = {}) json_response(response_code, { "error" => [ error ] }, opts) end
# File lib/chef_zero/rest_base.rb, line 196 def exists_data?(request, rest_path = nil) rest_path ||= request.rest_path data_store.exists?(rest_path) end
# File lib/chef_zero/rest_base.rb, line 201 def exists_data_dir?(request, rest_path = nil) rest_path ||= request.rest_path data_store.exists_dir?(rest_path) end
# File lib/chef_zero/rest_base.rb, line 78 def get_data(request, rest_path = nil, *options) rest_path ||= request.rest_path rest_path = rest_path.map { |v| URI.decode(v) } begin data_store.get(rest_path, request) rescue DataStore::DataNotFoundError if options.include?(:nil) nil elsif options.include?(:data_store_exceptions) raise else raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, rest_path)}") end end end
# File lib/chef_zero/rest_base.rb, line 302 def get_data_or_else(request, path, or_else_value) if exists_data?(request, path) parse_json(get_data(request, path)) else or_else_value end end
# File lib/chef_zero/rest_base.rb, line 318 def hashify_list(list) list.reduce({}) { |acc, obj| acc.merge( obj => {} ) } end
rfc090 returns 404 error or 200 with an emtpy body @param [ChefZero::RestRequest] request The HTTP request object
@return (see json_response)
# File lib/chef_zero/rest_base.rb, line 239 def head_request(request) get_data(request) # will raise 404 if non-existant json_response(200, nil) end
# File lib/chef_zero/rest_base.rb, line 66 def json_only true end
Serializes `data` to JSON and returns an Array with the response code, HTTP headers and JSON body.
@param [Fixnum] response_code HTTP response code @param [Hash] data The data for the response body as a Hash @param [Hash] options @option options [Hash] :headers (see already_json_response) @option options [Boolean] :pretty (true) Pretty-format the JSON @option options [Fixnum] :request_version (see already_json_response) @option options [Fixnum] :response_version (see already_json_response)
@return (see already_json_response)
# File lib/chef_zero/rest_base.rb, line 223 def json_response(response_code, data, options = {}) options = { pretty: true }.merge(options) do_pretty_json = !!options.delete(:pretty) # make sure we have a proper Boolean. json = FFI_Yajl::Encoder.encode(data, pretty: do_pretty_json) already_json_response(response_code, json, options) end
# File lib/chef_zero/rest_base.rb, line 94 def list_data(request, rest_path = nil, *options) rest_path ||= request.rest_path begin data_store.list(rest_path) rescue DataStore::DataNotFoundError if options.include?(:data_store_exceptions) raise else raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, rest_path)}") end end end
# File lib/chef_zero/rest_base.rb, line 310 def list_data_or_else(request, path, or_else_value) if exists_data_dir?(request, path) list_data(request, path) else or_else_value end end
# File lib/chef_zero/rest_base.rb, line 294 def parse_json(json) FFI_Yajl::Parser.parse(json) end
# File lib/chef_zero/rest_base.rb, line 322 def policy_name_invalid?(name) !name.is_a?(String) || name.size > 255 || name =~ /[+ !]/ end
# File lib/chef_zero/rest_base.rb, line 290 def populate_defaults(request, response) response end
# File lib/chef_zero/rest_base.rb, line 145 def set_data(request, rest_path, data, *options) rest_path ||= request.rest_path begin data_store.set(rest_path, data, *options, :requestor => request.requestor) rescue DataStore::DataNotFoundError if options.include?(:data_store_exceptions) raise else raise RestErrorResponse.new(404, "Object not found: #{build_uri(request.base_uri, rest_path)}") end end end
# File lib/chef_zero/rest_base.rb, line 230 def text_response(response_code, text) [response_code, { "Content-Type" => "text/plain" }, text] end
# File lib/chef_zero/rest_base.rb, line 298 def to_json(data) FFI_Yajl::Encoder.encode(data, :pretty => true) end