lib/lurker/endpoint.rb in lurker-0.6.1 vs lib/lurker/endpoint.rb in lurker-0.6.2

- old
+ new

@@ -2,223 +2,168 @@ require 'erb' require 'json-schema' # Endpoints represent the schema for an API endpoint # The #consume_* methods will raise exceptions if input differs from the schema -class Lurker::Endpoint +module Lurker + class Endpoint + include Lurker::Utils - attr_reader :schema, :service, :endpoint_path, :current_scaffold, :extensions - attr_accessor :errors + attr_reader :schema, :service, :endpoint_path, :extensions + attr_reader :request_parameters, :response_parameters, :response_codes + attr_accessor :errors - def initialize(endpoint_path, extensions={}, service=Lurker::Service.default_service) - @endpoint_path = endpoint_path - @extensions = extensions - @service = service - @errors = [] - @persisted = false - @schema = File.exist?(endpoint_path) ? load_schema : build_schema - end + def initialize(endpoint_path, extensions = {}, service = Lurker::Service.default_service) + @endpoint_path = endpoint_path + @extensions = extensions + @service = service + @errors = [] + @persisted = false + @schema = File.exist?(endpoint_path) ? load_schema : build_schema - def persist! - schema.ordered!.write_to(endpoint_path) - @persisted = true - end + initialize_schema_properties + end - def indexed? - prefix.present? && description.present? - end + def persist! + schema.ordered! unless persisted? + schema.write_to(endpoint_path) - def consume!(request_params, response_params, status_code, successful=true) - consume_request(request_params, successful) - consume_response(response_params, status_code, successful) - raise_errors! - end + @persisted = true + end - def consume_request(params, successful=true) - if successful - Lurker::SchemaModifier.merge!(request_parameters, stringify_keys(params)) + def indexed? + prefix.present? && description.present? end - end - def consume_response(params, status_code, successful=true) - return validate_response(params, status_code, successful) if persisted? + def consume!(request_params, response_params, status_code, successful = true) + consume_request(request_params, successful) + consume_response(response_params, status_code, successful) - if successful - Lurker::SchemaModifier.merge!(response_parameters, stringify_keys(params)) + raise_errors! end - if !status_code_exists?(status_code, successful) - response_code = { - "status" => status_code, - "successful" => successful, - "description" => "" - } - - Lurker::SchemaModifier.append!(response_codes, response_code) + def consume_request(params, successful = true) + request_parameters.validate(params) if persisted? + request_parameters.add(params) if successful end - end - def verb - @verb ||= endpoint_path.match(/([A-Z]*)\.json(\.yml)?(\.erb)?$/)[1] - end + def consume_response(params, status_code, successful = true) + if persisted? + response_codes.validate!(status_code, successful) + response_parameters.validate(params) if successful - def path - @path ||= endpoint_path. - gsub(service.service_dir, ""). - match(/\/?(.*)[-\/][A-Z]+\.json(\.yml)?(\.erb)?$/)[1] - end + return + end - # properties + response_parameters.add(params) if successful + response_codes.add(status_code, successful) unless response_codes.exists?( + status_code, successful) + end - def deprecated? - @schema["deprecated"] - end + def verb + @verb ||= endpoint_path.match(/([A-Z]*)\.json(\.yml)?(\.erb)?$/)[1] + end + def path + @path ||= endpoint_path. + gsub(service.service_dir, ""). + match(/\/?(.*)[-\/][A-Z]+\.json(\.yml)?(\.erb)?$/)[1] + end - def prefix - @schema["prefix"] - end + # properties - def description - @schema["description"] - end + def deprecated? + @schema["deprecated"] + end - def url_params - (schema.extensions['path_params'] || {}).reject { |k, _| ['action', 'controller', 'format'].include? k } - end + def prefix + @schema["prefix"] + end - def query_params - (schema.extensions['query_params'] || {}) - end + def description + @schema["description"] + end - def request_parameters - @schema["requestParameters"] ||= {} - end + def url_params + (schema.extensions['path_params'] || {}).reject { |k, _| %w(action controller format).include? k } + end - def response_parameters - @schema["responseParameters"] ||= {} - end + def query_params + (schema.extensions['query_params'] || {}) + end - def response_codes - @schema["responseCodes"] ||= [] - end + protected - protected + def initialize_schema_properties + @response_codes = ResponseCodes.new(schema) - def persisted? - !!@persisted - end + @response_parameters = HttpParameters.new(schema, + schema_key: 'responseParameters', schema_id: endpoint_path, human_name: 'Response') - def load_schema - @persisted = true + @request_parameters = HttpParameters.new(schema, + schema_key: 'requestParameters', schema_id: endpoint_path, human_name: 'Request') + end - Lurker::Schema.new( - load_file(endpoint_path), - stringify_keys(extensions) - ) - end + def persisted? + !!@persisted + end - def build_schema - @persisted = false + def load_schema + @persisted = true - Lurker::Schema.new( - { - "prefix" => "", - "description" => "", - "responseCodes" => [] - }, - stringify_keys(extensions) - ) - end - - def load_file(fname) - if fname.match(/\.erb$/) - context = Lurker::ErbSchemaContext.new - erb = ERB.new(IO.read(fname)).result(context.get_binding) - YAML.load(erb) - else - YAML.load_file(fname) + Lurker::Schema.new( + load_file(endpoint_path), + stringify_keys(extensions) + ) end - end - def validate(expected_params, given_params, prefix=nil) - schema = set_additional_properties_false_on(expected_params.dup) - schema['id'] = "file://#{endpoint_path}" - unless (_errors = Lurker::Validator.new(schema, stringify_keys(given_params), record_errors: true).validate).empty? - self.errors << prefix - _errors.each { |e| self.errors << "- #{e}" } - return false - end - true - end + def build_schema + @persisted = false - def validate_response(params, status_code, successful) - if !status_code_exists?(status_code, successful) - raise Lurker::UndocumentedResponseCode, - 'Undocumented response: %s, successful: %s' % [ - status_code, successful - ] - elsif successful - validate(response_parameters, params, 'Response') - else - true + Lurker::Schema.new( + { + "prefix" => "", + "description" => "", + "responseCodes" => [] + }, + stringify_keys(extensions) + ) end - end - def status_code_exists?(status_code, successful) - !!response_codes.detect do |code| - code["successful"] == successful && - (code["status"] == status_code || # 200 - code["status"].to_i == status_code) # "200 OK" + def load_file(fname) + if fname.match(/\.erb$/) + context = Lurker::ErbSchemaContext.new + erb = ERB.new(IO.read(fname)).result(context.get_binding) + YAML.load(erb) + else + YAML.load_file(fname) + end end - end - def raise_errors! - unless errors.empty? - raise Lurker::ValidationError.new(word_wrap(( - # ['Schema', "- #{endpoint_path}"] + - errors - # + ['Diff', current_scaffold.schema.diff(schema)] - ).join("\n"))) - end - end + def raise_errors! + return if response_parameters.errors.empty? - def word_wrap(text) - text.gsub(/\s+in schema/m, "\n in schema") - end - - # default additionalProperties on objects to false - # create a copy, so we don't mutate the input - def set_additional_properties_false_on(value) - if value.kind_of? Hash - copy = value.dup - if value["type"] == "object" || value.has_key?("properties") - copy["additionalProperties"] ||= false + errors = (request_parameters.errors | response_parameters.errors) * "\n" + exception = Lurker::ValidationError.new(word_wrap errors) + if (example = Lurker::Spy.current.block).respond_to?(:metadata) && (metadata = example.metadata).respond_to?(:location, true) + exception.set_backtrace [metadata.send(:location)] end - value.each do |key, hash_val| - unless key == "additionalProperties" - copy[key] = set_additional_properties_false_on(hash_val) - end - end - copy - elsif value.kind_of? Array - copy = value.map do |arr_val| - set_additional_properties_false_on(arr_val) - end - else - value + raise exception end - end - def stringify_keys(obj) - case obj - when Hash - result = {} - obj.each do |k, v| - result[k.to_s] = stringify_keys(v) + def word_wrap(text) + # strip .json# | .json.yml# | .json.yml.erb# + text = text.reverse + text.gsub!(/(\n|^)#bre\./, "\nbre.") + text.gsub!(/(\n|^)#lmy\./, "\nlmy.") + text.gsub!(/(\n|^)#nosj\./, "\nnosj.") + text.strip! + text = text.reverse + + text.gsub!(/\s+in schema/m, "\n in schema") + if defined?(Rails) + text.gsub!(/file:\/\/#{Rails.root}\//m, "") end - result - when Array then obj.map { |v| stringify_keys(v) } - else obj + text end end end