Created
October 20, 2012 23:38
-
-
Save ffejneslen/3925249 to your computer and use it in GitHub Desktop.
IODocs changes: configuring headers and elements to build xml/json service request bodies
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git app.js app.js | |
index aa31957..b842869 100755 | |
--- app.js | |
+++ app.js | |
@@ -70,7 +70,7 @@ db.on("error", function(err) { | |
// Load API Configs | |
// | |
var apisConfig; | |
-fs.readFile(__dirname +'/public/data/apiconfig.json', 'utf-8', function(err, data) { | |
+fs.readFile('public/data/apiconfig.json', 'utf-8', function(err, data) { | |
if (err) throw err; | |
apisConfig = JSON.parse(data); | |
if (config.debug) { | |
@@ -276,6 +276,7 @@ function processRequest(req, res, next) { | |
}; | |
var reqQuery = req.body, | |
+ requestBody = reqQuery.rawpayload || '', | |
params = reqQuery.params || {}, | |
methodURL = reqQuery.methodUri, | |
httpMethod = reqQuery.httpMethod, | |
@@ -318,8 +319,51 @@ function processRequest(req, res, next) { | |
path: apiConfig.publicPath + methodURL// + ((paramString.length > 0) ? '?' + paramString : "") | |
}; | |
+ // build the request body if it was not passed in as 'rawpayload' | |
if (['POST','DELETE','PUT'].indexOf(httpMethod) !== -1) { | |
- var requestBody = query.stringify(params); | |
+ if (!requestBody) { | |
+ | |
+ // if passed in a series of elements to use to build a request body, | |
+ // build it here based on the content type we need to send | |
+ if (reqQuery.elementNames && reqQuery.elementNames.length > 0) { | |
+ // consult our header values to find our content type | |
+ var bodyContentType = 'application/json'; | |
+ if (reqQuery.headerNames && reqQuery.headerNames.length > 0) { | |
+ for (var x = 0, len = reqQuery.headerNames.length; x < len; x++) { | |
+ if (reqQuery.headerNames[x] == 'Content-Type') { | |
+ bodyContentType = reqQuery.headerValues[x]; | |
+ } | |
+ } | |
+ } | |
+ | |
+ if ( bodyContentType == 'application/xml') { | |
+ // we think we want XML -- need to find a way to get a real xml | |
+ // builder in here?! | |
+ requestBody += '<'+reqQuery.parentElement+'>'; | |
+ for (var x = 0, len = reqQuery.elementNames.length; x < len; x++) { | |
+ if (reqQuery.elementNames[x] != '') { | |
+ requestBody += '<' + reqQuery.elementNames[x] + '>'; | |
+ requestBody += reqQuery.elementValues[x]; | |
+ requestBody += '</' + reqQuery.elementNames[x] + '>'; | |
+ } | |
+ } | |
+ requestBody += '</'+reqQuery.parentElement+'>'; | |
+ } | |
+ else { | |
+ // we think we want JSON - at least this is easy to build | |
+ var elList = {}; | |
+ for (var x = 0, len = reqQuery.elementNames.length; x < len; x++) { | |
+ if (reqQuery.elementNames[x] != '') { | |
+ elList[reqQuery.elementNames[x]] = reqQuery.elementValues[x]; | |
+ } | |
+ } | |
+ requestBody = JSON.stringify(elList); | |
+ } | |
+ } else { | |
+ // by default, we build a bunch of parameters | |
+ requestBody = query.stringify(params); | |
+ } | |
+ } | |
} | |
if (apiConfig.oauth) { | |
@@ -532,12 +576,28 @@ function processRequest(req, res, next) { | |
} | |
if (requestBody) { | |
- options.headers['Content-Type'] = 'application/x-www-form-urlencoded'; | |
+ if (options.headers['Content-Type']) { | |
+ if (config.debug) { | |
+ console.log('Header: Content-Type already set to: ' + options.headers['Content-Type']); | |
+ } | |
+ } | |
+ else { | |
+ if (config.debug) { | |
+ console.log('Setting header: Content-Type = application/x-www-form-urlencoded '); | |
+ } | |
+ options.headers['Content-Type'] = 'application/x-www-form-urlencoded'; | |
+ } | |
+ // embed our requestBody and the content type | |
+ // into the 'req' structure to pass along as part of the | |
+ // result in the app.post operation | |
+ req.body.requestBody=requestBody; | |
+ req.body.requestBodyContentType=options.headers['Content-Type']; | |
} | |
if (config.debug) { | |
+ console.log('req.body ' + JSON.stringify(req.body)); | |
console.log(util.inspect(options)); | |
- }; | |
+ } | |
var doRequest; | |
if (options.protocol === 'https' || options.protocol === 'https:') { | |
@@ -590,7 +650,7 @@ function processRequest(req, res, next) { | |
// Response body | |
req.result = body; | |
- console.log(util.inspect(body)); | |
+ sconsole.log(util.inspect(body)); | |
next(); | |
}) | |
@@ -620,7 +680,7 @@ app.dynamicHelpers({ | |
if (!req.params.api) { | |
pathName = req.url.replace('/',''); | |
// Is it a valid API - if there's a config file we can assume so | |
- fs.stat(__dirname + '/public/data/' + pathName + '.json', function (error, stats) { | |
+ fs.stat('public/data/' + pathName + '.json', function (error, stats) { | |
if (stats) { | |
req.params.api = pathName; | |
} | |
@@ -647,7 +707,7 @@ app.dynamicHelpers({ | |
}, | |
apiDefinition: function(req, res) { | |
if (req.params.api) { | |
- var data = fs.readFileSync(__dirname + '/public/data/' + req.params.api + '.json'); | |
+ var data = fs.readFileSync('public/data/' + req.params.api + '.json'); | |
return JSON.parse(data); | |
} | |
} | |
@@ -665,13 +725,31 @@ app.get('/', function(req, res) { | |
// Process the API request | |
app.post('/processReq', oauth, processRequest, function(req, res) { | |
+ // embed data into the 'result' that is processed and used | |
+ // for display -- include the requestBody and the | |
+ // content type so that we can display what we want on | |
+ // the result page. | |
+ | |
var result = { | |
headers: req.resultHeaders, | |
response: req.result, | |
call: req.call, | |
- code: req.res.statusCode | |
+ code: req.res.statusCode, | |
+ requestBody: req.body.requestBody, | |
+ requestBodyContentType: req.body.requestBodyContentType | |
}; | |
+ // this is insane... but.. hmmm -- we want to send back all that | |
+ // information we have in the result. but on a 204 or 304 we have no | |
+ // content to send.. In normal operations, this effectively also stops | |
+ // the sending of result data since no response body is present. | |
+ // In such a case, we can try and short circuit the issue | |
+ // by resetting the res.statusCode to something else, and allowing the content | |
+ // in the result to be passed to the view. Remember, we have stashed | |
+ // the acutal response code as 'code' in the result structure. | |
+ if (res.statusCode == 204) { | |
+ res.statusCode = 200; | |
+ } | |
res.send(result); | |
}); | |
@@ -692,7 +770,6 @@ app.post('/upload', function(req, res) { | |
// API shortname, all lowercase | |
app.get('/:api([^\.]+)', function(req, res) { | |
- req.params.api=req.params.api.replace(/\/$/,''); | |
res.render('api'); | |
}); | |
diff --git public/javascripts/docs.js public/javascripts/docs.js | |
index 41942c6..456fb16 100644 | |
--- public/javascripts/docs.js | |
+++ public/javascripts/docs.js | |
@@ -203,6 +203,21 @@ | |
}) | |
.insertAfter($('input[type=submit]', self)); | |
+ // We want to display the request Body passed to the service if a POST or PUT | |
+ // operation was executed. -- examine our parameters to find the http method | |
+ // assume a GET until we learn otherwise. | |
+ var httpMethod='GET'; | |
+ for ( var i = 0 ; i < params.length ; i++ ) { | |
+ var nv = params[i]; | |
+ if (nv.name == 'httpMethod') { | |
+ httpMethod = nv.value; | |
+ } | |
+ } | |
+ if ( httpMethod == 'POST' || httpMethod == 'PUT') { | |
+ resultContainer.append($(document.createElement('h4')).text('Request Body')); | |
+ resultContainer.append($(document.createElement('pre')).addClass('body prettyprint')); | |
+ } | |
+ | |
// Call that was made, add pre elements | |
resultContainer.append($(document.createElement('h4')).text('Call')); | |
resultContainer.append($(document.createElement('pre')).addClass('call')); | |
@@ -240,10 +255,22 @@ | |
if (result.signin) { | |
window.open(result.signin,"_blank","height=900,width=800,menubar=0,resizable=1,scrollbars=1,status=0,titlebar=0,toolbar=0"); | |
} else { | |
+ // make sure we can display something even if we got no response text passed back to us. | |
+ var ct='text/plain'; | |
+ if (result && result.headers['content-type']) { | |
+ ct=result.headers['content-type']; | |
+ } | |
var response, | |
- responseContentType = result.headers['content-type']; | |
+ responseContentType = ct; | |
+ | |
// Format output according to content-type | |
- response = livedocs.formatData(result.response, result.headers['content-type']) | |
+ if (result && result.headers['content-type']) { | |
+ response = livedocs.formatData(result.response, ct); | |
+ } | |
+ else { | |
+ // if there is no content, by all means say so! | |
+ response = 'No Content'; | |
+ } | |
$('pre.response', resultContainer) | |
.toggleClass('error', false) | |
@@ -253,7 +280,17 @@ | |
}) | |
// Complete, runs on error and success | |
.complete(function(result, text) { | |
- var response = JSON.parse(result.responseText); | |
+ | |
+ // make sure the response it set to something | |
+ // if there was no response, perhaps something bad happened? | |
+ var response = JSON.parse('{"code":"500"}'); | |
+ if (result && result.responseText) { | |
+ response = JSON.parse(result.responseText); | |
+ } | |
+ else if (result && result.status) { | |
+ // no response text? well at least grab the status | |
+ response.code = JSON.parse(result.status); | |
+ } | |
if (response.call) { | |
$('pre.call', resultContainer) | |
@@ -270,6 +307,25 @@ | |
.text(formatJSON(response.headers)); | |
} | |
+ // if we did a POST or PUT, we may have sent along the request body and | |
+ // content type -- if so, format the body and embed in the result container. | |
+ if ( httpMethod == 'POST' || httpMethod == 'PUT') { | |
+ var rawpayloadText='Request Body Not Found'; | |
+ if (response.requestBody) { | |
+ rawpayloadText=response.requestBody; | |
+ } | |
+ | |
+ // json encoding is fairly safe if nothing was sent | |
+ var requestContentType='application/json'; | |
+ if (response.requestBodyContentType) { | |
+ requestContentType=response.requestBodyContentType; | |
+ } | |
+ | |
+ var requestBodyText = livedocs.formatData(rawpayloadText, requestContentType); | |
+ $('pre.body', resultContainer).text(requestBodyText); | |
+ } | |
+ | |
+ | |
// Syntax highlighting | |
prettyPrint(); | |
}) | |
diff --git views/api.jade views/api.jade | |
index 97f5a9f..7b111e5 100644 | |
--- views/api.jade | |
+++ views/api.jade | |
@@ -1,4 +1,4 @@ | |
-h1=apiInfo.name | |
+h1=apiInfo.name | |
- if (session.authed && apiInfo.oauth && apiInfo.oauth.type =='three-legged') | |
- var authed ='authed' | |
- else | |
@@ -77,7 +77,7 @@ ul | |
span.description #{method.Synopsis} | |
br | |
br | |
- - if (method.parameters.length > 0) | |
+ - if (method.parameters && method.parameters.length > 0) | |
table.parameters | |
thead | |
tr | |
@@ -127,6 +127,114 @@ ul | |
- each description, choice in parameter.EnumeratedDescription | |
dt #{choice} | |
dd #{description} | |
+ - if (method.elements && method.elements.length > 0) | |
+ table.elements | |
+ thead | |
+ tr | |
+ th Element | |
+ th Value | |
+ th Type | |
+ th Description | |
+ tbody | |
+ - var elCount = -1 | |
+ - each element in method.elements | |
+ - elCount++ | |
+ div(id='element' + elCount) | |
+ - if (element.Required =='Y') | |
+ - var required =true | |
+ - var className ='required' | |
+ - else | |
+ - var required =false | |
+ - var className ='' | |
+ tr(class=className) | |
+ td | |
+ #{element.Name} | |
+ - if (element.Type != 'parentElement') | |
+ input(name='elementNames[' + elCount + ']', type='hidden', value=element.Name) | |
+ td.header | |
+ - if (element.Type =='parentElement') | |
+ input(name='parentElement', value=element.Default, type='hidden') | |
+ #{element.Default} | |
+ - else if (element.Type =='enumerated') | |
+ select(name='elementValues[' + elCount + ']', placeholder=className) | |
+ - if (element.Default =='') | |
+ option(value='') | |
+ - each choice in element.EnumeratedList | |
+ - if (element.Default ==choice) | |
+ option(value=choice, selected=true) #{choice} | |
+ - else | |
+ option(value=choice) #{choice} | |
+ - else if (element.Type =='boolean') | |
+ select(name='elementValues[' + elCount + ']', placeholder=className) | |
+ - if (element.Default =='') | |
+ option(value='') | |
+ - each choice in [apiInfo.booleanTrueVal,apiInfo.booleanFalseVal] | |
+ - if (element.Default ==choice) | |
+ option(value=choice, selected=true) #{choice} | |
+ - else | |
+ option(value=choice) #{choice} | |
+ - else | |
+ input(name='elementValues[' + elCount + ']', value=element.Default, placeholder=className) | |
+ td.type=element.Type | |
+ td.description | |
+ p=element.Description || 'No description' | |
+ - if (element.Type =='enumerated' && element.EnumeratedDescription) | |
+ dl.clearfix | |
+ - each description, choice in element.EnumeratedDescription | |
+ dt #{choice} | |
+ dd #{description} | |
+ - if (method.headers && method.headers.length > 0) | |
+ table.headers | |
+ thead | |
+ tr | |
+ th Request Header | |
+ th Value | |
+ th Type | |
+ th Description | |
+ tbody | |
+ - var headerCount =0 | |
+ - each header in method.headers | |
+ - headerCount++ | |
+ div(id='header' + headerCount) | |
+ - if (header.Required =='Y') | |
+ - var required =true | |
+ - var className ='required' | |
+ - else | |
+ - var required =false | |
+ - var className ='' | |
+ tr(class=className) | |
+ td | |
+ #{header.Name} | |
+ input(name='headerNames[' + headerCount + ']', type='hidden', value=header.Name) | |
+ td.header | |
+ - if (header.Type =='enumerated') | |
+ select(name='headerValues[' + headerCount + ']', placeholder=className) | |
+ - if (header.Default =='') | |
+ option(value='') | |
+ - each choice in header.EnumeratedList | |
+ - if (header.Default ==choice) | |
+ option(value=choice, selected=true) #{choice} | |
+ - else | |
+ option(value=choice) #{choice} | |
+ - else if (header.Type =='boolean') | |
+ select(name='headerValues[' + headerCount + ']', placeholder=className) | |
+ - if (header.Default =='') | |
+ option(value='') | |
+ - each choice in [apiInfo.booleanTrueVal,apiInfo.booleanFalseVal] | |
+ - if (header.Default ==choice) | |
+ option(value=choice, selected=true) #{choice} | |
+ - else | |
+ option(value=choice) #{choice} | |
+ - else | |
+ input(name='headerValues[' + headerCount + ']', value=header.Default, placeholder=className) | |
+ td.type=header.Type | |
+ td.description | |
+ p=header.Description || 'No description' | |
+ - if (header.Type =='enumerated' && header.EnumeratedDescription) | |
+ dl.clearfix | |
+ - each description, choice in header.EnumeratedDescription | |
+ dt #{choice} | |
+ dd #{description} | |
- if (method.headers && method.headers.length > 0) | |
div.headers | |
h4.title | |
@@ -152,5 +260,18 @@ ul | |
a(href='#', class='remove') Remove | |
a(href='#', class='add-headers') Add Header | |
// Create header fields and button to add/remove headers. | |
+ - if (method.rawpayload) | |
+ table.rawpayload | |
+ thead | |
+ tr | |
+ th #{method.rawpayload[0].Name} | |
+ tbody | |
+ tr | |
+ td | |
+ #{method.rawpayload[0].Description} | |
+ tr | |
+ td | |
+ textarea(columns='60', rows='10', style='width:95%', name='rawpayload', placeholder=method.rawpayload[0].Default) | |
+ | #{method.rawpayload[0].Default} | |
- if (!method['read-only']) | |
input(type='submit', id=method.MethodName, value='Try it!') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment