refapp
Version:
Parse Refract JSON to API Reference Jquery SPA
355 lines (325 loc) • 10.5 kB
JavaScript
/**
* @author E-Com Club <ti@e-com.club>
* @license MIT
*/
(function ($) {
'use strict'
// save request samples
var transaction = 0
var Req = [{}]
var Res = [{}]
var nodeValue = function (node) {
if (typeof node === 'object' && node.content !== undefined) {
return node.content
} else {
return node
}
}
var elementMeta = function (element, prop) {
// metadata from API Element object
if (typeof element === 'object' && element !== null) {
var meta = element.meta
if (typeof meta === 'object' && meta !== null) {
// valid meta object
if (!prop) {
return meta
} else if (meta[prop]) {
var node = Array.isArray(meta[prop]) ? meta[prop][0] : meta[prop]
if (node) {
return nodeValue(node)
} else {
return meta[prop]
}
}
}
}
// not found
if (prop) {
return ''
} else {
// empty object
return {}
}
}
var handleHeaders = function (headers, obj) {
// request or response HTTP headers
// parse to array of nested objects
// { key: value }
if (typeof headers === 'object') {
headers = headers.content
if (Array.isArray(headers)) {
// reset
obj.headers = {}
for (var i = 0; i < headers.length; i++) {
var header = headers[i]
try {
obj.headers[header.content.key.content] = header.content.value.content
} catch (e) {
console.error('Malformed HTTP header object', header, e)
}
}
}
}
}
var consume = function (refract, anchor, options, $body, $list, parent, endpoint) {
var i, doIfDeep, className, title
// check refract object
if (typeof refract === 'object' && refract !== null) {
// treat API Element object
// Ref.: https://api-elements.readthedocs.io/en/latest/element-definitions.html
var type = refract.element
var content = refract.content
// API Element attributes
var attr = refract.attributes
// get request and response objects
var req = Req[transaction]
var res = Res[transaction]
if (type !== 'httpResponse') {
// treat attributes first
if (typeof attr === 'object' && attr !== null) {
if (typeof nodeValue(attr.method) === 'string') {
req.method = nodeValue(attr.method)
}
if (typeof nodeValue(attr.href) === 'string') {
// endpoint pattern
req.href = nodeValue(attr.href)
}
if (attr.headers) {
// request HTTP headers
handleHeaders(attr.headers, req)
}
if (attr.hrefVariables) {
// URL params
// parse to array of nested objects
// { key, type, value, description, required }
var params = attr.hrefVariables
if (typeof params === 'object') {
params = params.content
if (Array.isArray(params)) {
// reset
req.params = []
for (i = 0; i < params.length; i++) {
var param = params[i]
try {
var paramObject = {
key: param.content.key.content,
// boolean required
required: !(param.attributes.typeAttributes[0] === 'optional')
}
if (param.content.value) {
paramObject.value = param.content.value.content
} else {
paramObject.value = ''
}
// optional param type and description
if (param.meta) {
paramObject.type = elementMeta(param, 'title') || ''
paramObject.description = elementMeta(param, 'description') || ''
} else {
paramObject.type = paramObject.description = ''
}
// add to request params
req.params.push(paramObject)
} catch (e) {
console.error('Malformed URL param object', param, e)
}
}
}
}
}
}
switch (type) {
case 'copy':
if (typeof content === 'string') {
// Markdown string
// append to parent body element
$body.append('<div class="pb-2">' + options.mdParser(content) + '</div>')
}
break
case 'category':
case 'resource':
className = elementMeta(refract, 'classes')
title = elementMeta(refract, 'title')
var id = anchor + title.toLowerCase().replace(/[^\w\s]/g, '').replace(/\s+/g, '-')
var $li
if (title !== '') {
// show category title
var head
switch (className) {
case 'api':
head = 1
break
case 'resourceGroup':
head = 2
break
default:
head = 3
}
// add title to body DOM
$body.append($('<h' + head + '>', {
'class': 'my-3',
html: $('<a>', {
'class': 'anchor-link text-body',
href: '#' + id,
text: title
}),
id: id
}))
if (head <= 2) {
$body.append('<hr>')
}
$li = $('<li>', {
html: $('<a>', {
href: '#' + id,
text: title
})
})
// add to anchors list
$list.append($li)
doIfDeep = function () {
// create new deeper list for subresources
var $ul = $('<ul>')
$li.append($ul)
// new block for category
var $div = $('<div>', {
'class': 'mb-5'
})
$body.append($div)
// change body and list DOM elements
$body = $div
$list = $ul
}
}
if (req.href) {
// repass default resource endpoint
endpoint = req.href
}
break
case 'transition':
// new card block to API request
title = elementMeta(refract, 'title')
var $card = $('<div>', {
'class': 'card-body',
html: '<h5 class="card-title">' + title + '</h5>'
})
$body.append($('<a>', {
href: 'javascript:;',
'class': 'mt-2 card',
html: $card,
// send request and response objects
click: (function (i) {
return function () { options.actionCallback(Req[i], Res[i]) }
}(transaction))
}))
doIfDeep = function () {
// change body DOM element
$body = $card
}
// set request title
req.title = title
break
case 'httpRequest':
if (req.method) {
var color
switch (req.method) {
case 'POST':
color = 'success'
break
case 'PATCH':
color = 'warning'
break
case 'PUT':
color = 'secondary'
break
case 'DELETE':
color = 'danger'
break
case 'GET':
color = 'info'
break
default:
color = 'light'
}
// styling action card
$body.parent().addClass('text-white bg-' + color)
// show request method
.find('h3,h4,h5').append($('<small>', {
'class': 'text-monospace ml-1 float-right',
text: req.method
}))
}
break
case 'asset':
if (typeof content === 'string') {
// body content
var obj
switch (parent) {
case 'httpRequest':
// request body string
obj = req
break
case 'httpResponse':
// response body
obj = res
break
}
if (obj) {
className = elementMeta(refract, 'classes')
if (className === 'messageBodySchema') {
// JSON Schema
obj.schema = content
} else {
obj.body = content
}
}
}
break
case 'parseResult':
if (Array.isArray(content)) {
// fix root API Element
return content[0]
}
}
} else {
// sample response
if (attr.headers) {
// response HTTP headers
handleHeaders(attr.headers, res)
} else {
// no headers
res.headers = []
}
// HTTP status
if (attr.statusCode) {
res.status = parseInt(attr.statusCode, 10)
} else {
res.status = 200
}
// preset next request and response
// persist request URI
Req.push({ href: (endpoint || req.href) })
Res.push({})
}
// check each child element one by one
if (Array.isArray(content) && content.length) {
if (doIfDeep) {
doIfDeep()
}
// create new deeper list for subresources
for (i = 0; i < content.length; i++) {
// recursion
consume(content[i], anchor, options, $body, $list, type, endpoint)
}
}
if (type === 'httpResponse') {
// pass to next req and res object
transaction++
}
}
// all done
return null
}
// set globally
window.consumeRefract = consume
// window.apiElementMeta = elementMeta
}(jQuery))