Skip to content

Commit

Permalink
Support request media type configuration for an API service.
Browse files Browse the repository at this point in the history
  • Loading branch information
nebolsin committed Jun 24, 2015
1 parent 9edf244 commit 5240801
Show file tree
Hide file tree
Showing 10 changed files with 208 additions and 131 deletions.
27 changes: 25 additions & 2 deletions features/html_generation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,25 @@ Feature: html generation

Lurker generates pretty info pages based on schema information
and form which allow you to test live api.
When testing your api ensure that CSRF protection is handled properlya
When testing your api ensure that CSRF protection is handled properly
with `protect_from_forgery :null_session`

@javascript
Scenario: json schema gets generated into html preview using "users/create"
Given an empty directory named "public/lurker"
And a service file with:
"""yml
---
name: Lurker Demo Application
basePath: ''
description: ''
domains:
'[Sandbox] Heroku': 'http://lurker-app.herokuapp.com'
'[Real] razum2um.me': 'http://lurker.razum2um.me'
consumes:
- application/x-www-form-urlencoded
- application/json
"""
And a file named "lurker/api/v1/users-POST.json.yml" with:
"""yml
---
Expand Down Expand Up @@ -62,7 +75,7 @@ Feature: html generation
And I fill in the submit form field "name" with "Jim"
Then I should see:
"""
curl -X POST -d "user%5Bname%5D=Jim"
curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'user%5Bname%5D=Jim'
"""

And I submit lurk form
Expand All @@ -74,3 +87,13 @@ Feature: html generation

Then I should see JSON response with "can't be blank"

When I select "application/json" request media type
And I fill in the submit form field "name" with "Jim"
Then I should see:
"""
curl -X POST -H 'Content-Type: application/json' -d '{"user":{"name":"Jim"}}'
"""

And I submit lurk form

Then I should see JSON response with "Jim"
2 changes: 1 addition & 1 deletion features/multidomain_support.feature
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,6 @@ Feature: mutidomain support
When I select "[Sandbox] Heroku" hostname
Then I should see:
"""
curl -X DELETE "http://lurker-app.herokuapp.com/api/v1/users/1"
curl -X DELETE 'http://lurker-app.herokuapp.com/api/v1/users/1'
"""

9 changes: 8 additions & 1 deletion features/step_definitions/additional_cli_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,14 @@
end
# FIXME: see multidomain feature - cannot select node in phantomjs
page.execute_script("window.submitForm.setState({host: jQuery('#hostname').val()});")
page.execute_script("window.submitForm.afterSetPartialState()")
end

When /^I select "([^"]*)" request media type$/ do |type|
within(:xpath, "//*[@id='requestMediaType']") do
select(type)
end
# FIXME: cannot select node in phantomjs
page.execute_script("window.submitForm.setState({requestMediaType: jQuery('#requestMediaType').val()});")
end

When(/^I fill in the submit form field "([^"]*)" with "([^"]*)"$/) do |field, name|
Expand Down
53 changes: 22 additions & 31 deletions lib/lurker/form_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,55 +11,46 @@ def html

private

def add_to_buffer(params, parent_labels = [])
params.each do |label, value|
if parent_labels.present?
label = "[#{label}]"
end

new_parent_labels = parent_labels.clone << label
def add_to_buffer(params, parent_accessors = [])
params.each do |name, value|

accessors = parent_accessors.clone << name
if value.is_a?(Hash)
add_legend_to_buffer(parent_labels, label)

add_to_buffer(value, new_parent_labels)
add_to_buffer(value, accessors)
elsif value.is_a?(Array)
value.each do |v|
value.each_with_index do |v, i|
if v.is_a?(Hash)
add_legend_to_buffer(parent_labels, label)

add_to_buffer(v, parent_labels.clone << "#{label}[]")
add_to_buffer(v, accessors << i)
else
add_element_to_buffer(parent_labels, "#{label}[]", v)
add_element_to_buffer(accessors, v)
end
end
else
add_element_to_buffer(parent_labels, label, value)
add_element_to_buffer(accessors, value)
end
end
end

def add_element_to_buffer(parent_labels, label, value)
def add_element_to_buffer(accessors, value)
@_buffer += render(
:partial => 'param_form_element',
:locals => {
:label => "#{print_labels(parent_labels)}#{label}",
:label_text => "#{print_labels(parent_labels)}#{label}",
:value => value
:locals => {
:accessor => "#{accessors.compact.join('.')}",
:label => "#{print_labels(accessors)}",
:label_text => "#{print_labels(accessors)}",
:value => value
}
)
end

def add_legend_to_buffer(parent_labels, label)
return
@_buffer += render(
:partial => 'param_form_legend',
:locals => { :label => print_labels(parent_labels.clone << label) }
)
end

def print_labels(parent_labels)
"#{parent_labels * ''}"
def print_labels(accessors)
accessors.inject do |acc, label|
if label.is_a? Numeric
"#{acc}[]"
else
"#{acc}[#{label}]"
end
end
end
end
end
9 changes: 9 additions & 0 deletions lib/lurker/presenters/service_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ def default_domain
'/'
end

def request_media_types
return service.request_media_types if service.request_media_types.present?
['application/x-www-form-urlencoded']
end

def default_request_media_type
request_media_types[0]
end

def name_as_link(options = {})
path = index_path
'<a href="%s">%s %s</a>' % [path, options[:prefix], service.name]
Expand Down
16 changes: 13 additions & 3 deletions lib/lurker/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ def initialize(service_dir, service_name = nil)
Lurker::Json::Schema.new(schema)
else
Lurker::Json::Schema.new(
'name' => service_filename,
'basePath' => '',
'name' => service_filename,
'basePath' => '',
'description' => '',
'domains' => {}
'domains' => {},
'consumes' => %w(application/x-www-form-urlencode application/json),
'produces' => %w(application/json)
)
end
end
Expand Down Expand Up @@ -112,4 +114,12 @@ def discussion
def domains
schema['domains']
end

def request_media_types
schema['consumes']
end

def response_media_types
schema['produces']
end
end
83 changes: 60 additions & 23 deletions lib/lurker/templates/javascripts/lurker.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* jshint quotmark: false */

var window = this.window;
var $ = window.$;
var Lurker = {
Expand All @@ -10,17 +12,6 @@ var Lurker = {
$("#submit-api").attr("disabled", false);
},

detectContentType: function(response) {
var contentType = response.getResponseHeader("Content-Type");
var detectedContentType = null;

if (contentType.match(/application\/json/)) {
detectedContentType = 'json';
}

return detectedContentType;
},

fillInInfoTab: function($tab, xhr) {
$tab.find('.status td.value').text(xhr.status + " " + xhr.statusText);
$tab.find('#headers').text(xhr.getAllResponseHeaders());
Expand All @@ -41,36 +32,82 @@ var Lurker = {
$tab.html(content);
},

onComplete: function(xhr) {
Lurker.lastRequest.endTime = Date.now();
Lurker.enableSubmitButton();
detectContentType: function(response) {
var contentType = response.getResponseHeader("Content-Type");
var detectedContentType = null;

if (contentType.match(/application\/json/)) {
detectedContentType = 'json';
}

return detectedContentType;
},

$("#show-api-response-div [ref^='response']").hide();
buildActionUrl: function(host, template, values) {
for (var i = 0; i < values.length; i++) {
var placeholder = new RegExp(':' + values[i].label);
template = template.replace(placeholder, values[i].value);
}
return host + template;
},

serializePayload: function(payload, contentType) {
if (contentType === 'application/json' || /\+json$/.test(contentType)) {
return JSON.stringify(payload);
} else {
// default to 'application/x-www-form-urlencoded'
return jQuery.param(payload);
}
},

Lurker.fillInInfoTab($("#show-api-response-div").showNavTab("info"), xhr);
Lurker.fillInRawTab($("#show-api-response-div").showNavTab("raw"), xhr);
generateCurlCommand: function(targetUrl, method, payload, contentType) {
var serializedPayload = Lurker.serializePayload(payload, contentType);
var results = [];
results.push('curl');
results.push('-X ' + method);
if (serializedPayload.length > 0) {
results.push("-H 'Content-Type: " + contentType + "'");
results.push("-d '" + serializedPayload + "'");
}
results.push("'" + targetUrl + "'");
return results.join(' ');
},

onSubmit: function($form) {
performRequest: function(host, method, template, values, payload, contentType) {
Lurker.disableSubmitButton();

$.ajax({
url: $form.attr('action'),
method: $form.attr('method'),
data: $form.serialize()
url: Lurker.buildActionUrl(host, template, values),
data: Lurker.serializePayload(payload, contentType),
method: method,
contentType: contentType,
processData: false
}).complete(Lurker.onComplete);

Lurker.lastRequest = {};
Lurker.lastRequest.startTime = Date.now();

return false;
},

onComplete: function(xhr) {
Lurker.lastRequest.endTime = Date.now();
Lurker.enableSubmitButton();

var $responseDiv = $("#show-api-response-div");
$responseDiv.find("[ref^='response']").hide();

Lurker.fillInInfoTab($responseDiv.showNavTab("info"), xhr);
Lurker.fillInRawTab($responseDiv.showNavTab("raw"), xhr);
}
};

$(function($) {
var activeMenuItem = $('#side-menu a[href="' + window.location.pathname + '"]');
var activeMenuItem = $('#side-menu').find('a[href="' + window.location.pathname + '"]');
if (activeMenuItem.length === 1) {
activeMenuItem.addClass('hovered').parents('.collapse').addClass('in').parents('.endpoint-group').addClass('active');
activeMenuItem.addClass('hovered')
.parents('.collapse').addClass('in')
.parents('.endpoint-group').addClass('active');
}

window.domain = window.localStorage.lastDomain || '/';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<% if value === :file %>
<input type="file" name="<%= label %>" ></input>
<% else %>
<input type="text" name="<%= label %>" defaultValue="<%= value.to_s %>" className="form-control" onChange={this.afterSetPartialState}></input>
<input type="text" name="<%= label %>" defaultValue="<%= value.to_s %>" valueLink={this.linkState('payload.<%= accessor %>')} className="form-control"></input>
<% end %>
</div>
</div>

This file was deleted.

0 comments on commit 5240801

Please sign in to comment.