diff --git a/README.md b/README.md index 6fa4738..581988b 100644 --- a/README.md +++ b/README.md @@ -33,19 +33,36 @@ Write tests using the following convention: Example: describe 'Arenas Requests' do - describe 'GET /v1/arenas/{id}' do - it 'responds with the requested arena' do - arena = create :arena, foursquare_id: '5104' - get v1_arena_path(arena) - - response.status.should eq(200) + describe 'arenas [/v1/arenas]' do + describe 'create an arena [POST]' do + it 'responds with the created arena' do + arena = build :arena, foursquare_id: '5104' + post v1_arena_path(arena) + + response.status.should eq(201) + end + end end - end end The output: - # GET /v1/arenas/{id} + # Arenas + + ## arenas [/v1/arenas] + + ### create an arena [POST] + + Request + + Headers + + + Body + { + "id": "4e9dbbc2-830b-41a9-b7db-9987735a0b2a", + "name": "Clinton St. Baking Co. & Restaurant", + "latitude": 40.721294, + "longitude": -73.983994, + "foursquare_id": "5104" + } + Response 200 (application/json) diff --git a/lib/rspec_api_blueprint.rb b/lib/rspec_api_blueprint.rb index 3ad1b5f..f1eb6f1 100644 --- a/lib/rspec_api_blueprint.rb +++ b/lib/rspec_api_blueprint.rb @@ -1,15 +1,9 @@ require "rspec_api_blueprint/version" require "rspec_api_blueprint/string_extensions" - +require "rspec_api_blueprint/spec_blueprint_translator" RSpec.configure do |config| config.before(:suite) do - if defined? Rails - api_docs_folder_path = File.join(Rails.root, '/docs/', '/api_docs/') - else - api_docs_folder_path = File.join(File.expand_path('.'), '/api_docs/') - end - Dir.mkdir(api_docs_folder_path) unless Dir.exists?(api_docs_folder_path) Dir.glob(File.join(api_docs_folder_path, '*_blueprint.md')).each do |f| @@ -18,14 +12,7 @@ end config.after(:suite) do - - if defined? Rails - api_docs_folder_path = File.join(Rails.root, '/docs/', '/api_docs/') - else - api_docs_folder_path = File.join(File.expand_path('.'), '/api_docs/') - end - - append = ->(handle, file){ handle.puts File.read(File.join(api_docs_folder_path, file)) } + append = -> (handle, file){ handle.puts File.read(File.join(api_docs_folder_path, file)) } File.open(File.join(api_docs_folder_path ,'apiary.apib'), 'wb') do |apiary| append.call(apiary, 'introduction.md') @@ -45,98 +32,19 @@ end config.after(:each, type: :request) do |example| - next unless example.metadata[:document] === true - - if response - example_group = example.example_group.metadata - example_groups = [] - - while example_group - example_groups << example_group - example_group = example_group[:parent_example_group] - end - - if example_groups[-2] - action = example_groups[-2][:description_args].first - extra_description = example_groups[-2][:extra_documentation] - end - example_groups[-1][:description_args].first.match(/(.+)\sRequests/) - file_name = $1.gsub(' ','').underscore - - if defined? Rails - file = File.join(Rails.root, "/docs/api_docs/#{file_name}_blueprint.md") - else - file = File.join(File.expand_path('.'), "/api_docs/#{file_name}_blueprint.md") - end - - - File.open(file, 'a') do |f| - # Resource & Action - query_strings = URI.decode(request.env['QUERY_STRING']).split('&') - params = query_strings.map do |value| - value.gsub("[","%5B").gsub("]","%5D") - end - - f.write "# #{action}" - - unless params.empty? - f.write "?#{params.join('&')}" - end - - f.write("\n") - if extra_description.present? - f.write File.read(File.join(Rails.root, '/docs/api_docs', extra_description)) - end - - # Request - request_body = request.body.read - - current_env = request.env ? request.env : request.headers - - authorization_header = current_env['HTTP_AUTHORIZATION'] || - env['X-HTTP_AUTHORIZATION'] || - env['X_HTTP_AUTHORIZATION'] || - env['REDIRECT_X_HTTP_AUTHORIZATION'] || - env['AUTHORIZATION'] - - - if request_body.present? || authorization_header.present? || request.env['QUERY_STRING'] - f.write "+ Request #{request.content_type}\n\n" - - if request.env['QUERY_STRING'].present? - f.write "+ Parameters\n\n".indent(4) - query_strings = URI.decode(request.env['QUERY_STRING']).split('&') + translator = SpecBlueprintTranslator.new(example, request, response) - query_strings.each do |value| - key, example = value.split('=') - f.write "+ #{key} = '#{example}'\n".indent(12) - end - f.write("\n") - end - - allowed_headers = %w(HTTP_AUTHORIZATION AUTHORIZATION CONTENT_TYPE) - f.write "+ Headers\n\n".indent(4) - current_env.each do |header, value| - next unless allowed_headers.include?(header) - header = header.gsub(/HTTP_/, '') if header == 'HTTP_AUTHORIZATION' - f.write "#{header}: #{value}\n".indent(12) - end - f.write "\n" - - # Request Body - if request_body.present? && request.content_type.to_s == 'application/json' - f.write "+ Body\n\n".indent(4) if authorization_header - f.write "#{JSON.pretty_generate(JSON.parse(request_body))}\n\n".indent(authorization_header ? 12 : 8) - end - end - - # Response - f.write "+ Response #{response.status} (#{response.content_type}; charset=#{response.charset})\n\n" - - if response.body.present? && response.content_type.to_s =~ /application\/json/ - f.write "#{JSON.pretty_generate(JSON.parse(response.body))}\n\n".indent(8) - end - end unless response.status == 401 || response.status == 403 || response.status == 301 + if translator.can_make_blueprint? + translator.open_file_from_grouping + translator.write_resource_to_file + translator.write_action_to_file + translator.close_file end end end + +def api_docs_folder_path + return File.join(Rails.root, '/docs/', '/api_docs/') if defined? Rails + + File.join(File.expand_path('.'), '/api_docs/') +end diff --git a/lib/rspec_api_blueprint/spec_blueprint_translator.rb b/lib/rspec_api_blueprint/spec_blueprint_translator.rb new file mode 100644 index 0000000..ff40085 --- /dev/null +++ b/lib/rspec_api_blueprint/spec_blueprint_translator.rb @@ -0,0 +1,146 @@ +class SpecBlueprintTranslator + @@actions_covered = {} + + def initialize(example, request, response) + group_metas = [] + group_meta = example.example_group.metadata + + while group_meta.present? + group_metas << group_meta + group_meta = group_meta[:parent_example_group] + end + + @example = example + @action_group = group_metas[-3] + @resource_group = group_metas[-2] + @grouping_group = group_metas[-1] + + @request = request + @response = response + end + + def can_make_blueprint? + @action_group.present? && @resource_group.present? && @grouping_group.present? && @example.metadata[:document] === true && basic_status? + end + + def basic_status? + response.status == 200 || response.status == 201 || response.status == 202 + end + + def request + @request + end + + def response + @response + end + + def resource + @resource_group[:description_args].first.match(/(.+)\[(.+)\]/) + $2 + end + + def resource_description + @resource_group[:description_args].first.match(/(.+)\[(.+)\]/) + $1 + end + + def action + @action_group[:description_args].first.match(/(.+)\[(.+)\]/) + $2.upcase + end + + def action_description + @action_group[:description_args].first.match(/(.+)\[(.+)\]/) + $1 + end + + def open_file_from_grouping + @handle = File.open(file_path, 'a') + end + + def close_file + @handle.close + end + + def file_path + @grouping_group[:description_args].first.match(/(.+)\sRequests/) + file_name = $1.gsub(' ','').underscore + + "#{api_docs_folder_path}#{file_name}_blueprint.md" + end + + def write_resource_to_file + return if @@actions_covered.has_key?(resource) + + @@actions_covered["#{resource}"] = [] + + @handle.write "## #{resource_description} [#{resource}]" + @handle.write("\n") + end + + def write_action_to_file + return if @@actions_covered["#{resource}"].include?(action) + + @@actions_covered["#{resource}"] << action + + @handle.write "### #{action_description} [#{action}]" + + query_strings = URI.decode(request.env['QUERY_STRING']).split('&') + + @handle.write("\n") + write_request_to_file + write_response_to_file + end + + def write_request_to_file + request_body = request.body.read + + current_env = request.env ? request.env : request.headers + + authorization_header = current_env['HTTP_AUTHORIZATION'] || + env['X-HTTP_AUTHORIZATION'] || + env['X_HTTP_AUTHORIZATION'] || + env['REDIRECT_X_HTTP_AUTHORIZATION'] || + env['AUTHORIZATION'] + + + if request_body.present? || authorization_header.present? || request.env['QUERY_STRING'] + @handle.write "+ Request #{request.content_type}\n\n" + + if request.env['QUERY_STRING'].present? + @handle.write "+ Parameters\n\n".indent(4) + query_strings = URI.decode(request.env['QUERY_STRING']).split('&') + + query_strings.each do |value| + key, example = value.split('=') + @handle.write "+ #{key} = '#{example}'\n".indent(12) + end + @handle.write("\n") + end + + allowed_headers = %w(HTTP_AUTHORIZATION AUTHORIZATION CONTENT_TYPE) + @handle.write "+ Headers\n\n".indent(4) + current_env.each do |header, value| + next unless allowed_headers.include?(header) + header = header.gsub(/HTTP_/, '') if header == 'HTTP_AUTHORIZATION' + @handle.write "#{header}: #{value}\n".indent(12) + end + @handle.write "\n" + + # Request Body + if request_body.present? && request.content_type.to_s == 'application/json' + @handle.write "+ Body\n\n".indent(4) if authorization_header + @handle.write "#{JSON.pretty_generate(JSON.parse(request_body))}\n\n".indent(authorization_header ? 12 : 8) + end + end + end + + def write_response_to_file + @handle.write "+ Response #{response.status} (#{response.content_type}; charset=#{response.charset})\n\n" + + if response.body.present? && response.content_type.to_s =~ /application\/json/ + @handle.write "#{JSON.pretty_generate(JSON.parse(response.body))}\n\n".indent(8) + end + end +end diff --git a/lib/rspec_api_blueprint/version.rb b/lib/rspec_api_blueprint/version.rb index f89b636..9e51ecc 100644 --- a/lib/rspec_api_blueprint/version.rb +++ b/lib/rspec_api_blueprint/version.rb @@ -1,3 +1,3 @@ module RspecApiBlueprint - VERSION = "0.0.8" + VERSION = "0.0.9" end