swagger-theme
Version:
Convert any API Specification into an awesome HTML documentation website
594 lines (505 loc) • 18.5 kB
JavaScript
class ZamaPopup {
constructor () {
this._basicAuthToken = null;
this._apiKeyToken = null;
this._oAuthToken = null;
this._authType = null;
this._pathId = null;
}
generateTextField({ label, name, required, disabled, value }) {
return `
<div class="form-group">
<label for="field-${name}" class="col-form-label">${label || name}${required ? ' <i style="color: red;font-size: 8px;vertical-align: super;" class="fas fa-asterisk"></i>' : ''}</label>
<input type="text" placeholder="Enter ${name}" class="form-control" name="field-${name}" id="field-${name}"${required ? ' required' : ''}${disabled ? ' disabled' : ''}${value ? ` value="${value}"` : ''}>
</div>
`;
}
generatePasswordField({ label, name, required, disabled, value }) {
return `
<div class="form-group">
<label for="field-${name}" class="col-form-label">${label || name}${required ? ' <i style="color: red;font-size: 8px;vertical-align: super;" class="fas fa-asterisk"></i>' : ''}</label>
<input type="password" placeholder="Enter ${name}" class="form-control" name="field-${name}" id="field-${name}"${required ? ' required' : ''}${disabled ? ' disabled' : ''}${value ? ` value="${value}"` : ''}>
</div>
`;
}
generateFileField({ label, name, required, disabled, value }) {
return `
<div class="form-group">
<label style="display: flex;justify-content: center;align-items: center;" class="col-form-label">
${name}
<input type="file" style="border: none;" placeholder="${name}" class="form-control" name="field-${name}" id="field-${name}"${required ? ' required' : ''}${disabled ? ' disabled' : ''}>
</label>
</div>
`;
}
generateTextArea({ label, name, required, disabled, modelExample, example, value }) {
return `
<div class="form-group">
<label for="field-${name}" class="col-form-label">${label || name}${required ? ' <i style="color: red;font-size: 8px;vertical-align: super;" class="fas fa-asterisk"></i>' : ''}</label>
<textarea ${modelExample ? `rows="${(modelExample.match(/\n/g) || []).length < 15 ? (modelExample.match(/\n/g) || []).length : 15}"` : ''} placeholder="${name}" name="field-${name}" id="field-${name}" class="form-control"${required ? ' required' : ''}${disabled ? ' disabled' : ''}>${value || modelExample || example || ''}</textarea>
</div>
`;
}
generateSelect({ label, name, options = [], required, disabled, value }) {
return `
<div class="form-group">
<label for="field-${name}" class="col-form-label">${label || name}${required ? ' <i style="color: red;font-size: 8px;vertical-align: super;" class="fas fa-asterisk"></i>' : ''}</label>
<select class="form-control" placeholder="${name}" name="field-${name}" id="field-${name}"${required ? ' required' : ''}${disabled ? ' disabled' : ''}${value ? ` value="${value}"` : ''}>
${options.map(({ name, value }) => `<option value="${value}">${name}</option>`).join('')}
</select>
</div>
`;
}
injectFieldHtml(fieldHtml) {
if ($('#zamaModal').length) {
$('#zamaModal .modal-body').append(fieldHtml);
}
}
injectBreak() {
this.injectFieldHtml("<hr />");
}
injectButton({ onClick, name = '', label = 'Submit'}) {
if ($('#zamaModal').length) {
this.injectFieldHtml(`
<div class="form-group" style="height: 40px;">
<button type="button" style="float: right;" class="zama-internal-${name}-btn btn btn-primary">${label}</button>
</div>
`);
$(`.zama-internal-${name}-btn`).off("click");
$(`.zama-internal-${name}-btn`).click(onClick);
}
}
resetFields() {
if ($('#zamaModal').length) {
$('#zamaModal .modal-body').html('');
}
}
setTitle(title) {
if ($('#zamaModal').length) {
$('#zamaModal .modal-header .modal-title').html(title);
}
}
injectTitle(title) {
this.injectFieldHtml(`<h5>${title}</h5>`);
}
createField({ type, ...props }) {
let fieldHtml = '';
type = type && type.toLowerCase();
if (type === 'array') {
const {items = {}} = props;
const {enum: enums = []} = items;
if (enums.length) {
props.options = enums.map(e => ({name: e, value: e}));
type = 'select'
} else {
type = 'text';
}
}
if (type === 'boolean') {
props.options = [true, false].map(e => ({name: e, value: e}));
type = 'select'
}
if (['string'].includes(type)) type = 'text';
else if (['int', 'integer'].includes(type)) type = 'number';
else if (['boolean'].includes(type)) type = 'select';
else if (['object', '', undefined, null].includes(type)) type = 'textarea';
// type = 'string', format: 'byte' => https://swagger.io/docs/specification/data-models/data-types/#file
// else if (['', '', ''].includes(type)) type = 'file';
if (type === 'text') {
fieldHtml = this.generateTextField(props);
} else if (type === 'number') {
fieldHtml = this.generateTextField(props);
} else if (type === 'password') {
fieldHtml = this.generatePasswordField(props);
} else if (type === 'select') {
fieldHtml = this.generateSelect(props);
} else if (type === 'textarea') {
fieldHtml = this.generateTextArea(props);
} else if (type === 'file') {
fieldHtml = this.generateFileField(props);
}
if (fieldHtml) {
this.injectFieldHtml(fieldHtml);
}
}
attachOnSubmit(cb) {
$('#zamaModal form').off('submit');
$('#zamaModal form').submit((e) => {
e.preventDefault();
this.requestSubmitHandler();
});
}
hide() {
$('#zamaModal').modal('hide');
}
show() {
$('#zamaModal').modal({
backdrop: 'static',
show: true,
})
}
showSubmitButton() {
$('#zamaModal .modal-footer').show();
}
hideSubmitButton() {
$('#zamaModal .modal-footer').hide();
}
getFieldValue(name) {
return $(`*[name="field-${name}"]`).val();
}
createRequestHeaders(headers = []) {
this.injectTitle('Headers');
headers.forEach((header) => {
this.createField(header);
});
}
createRequestPath(paths = []) {
this.injectTitle('Paths');
paths.forEach((path) => {
this.createField(path);
});
}
createRequestQuery(queries = []) {
this.injectTitle('Query');
queries.forEach((query) => {
this.createField(query);
});
}
createRequestBody(bodies = []) {
this.injectTitle('Body');
bodies.forEach((body) => {
this.createField(body);
});
}
createRequestFormData(formData = []) {
this.injectTitle('Form Data');
formData.forEach((form) => {
this.createField(form);
});
}
createRequest(pathId) {
this._pathId = pathId;
const {paths = [], auths = []} = pathsObject;
const path = paths.find(({id}) => id === pathId);
if (!path) return;
if (!this.getAuthToken().type && auths.length) {
this.createAuthenticationForm();
return this.show();
}
let { summary = '', requests = [], methodName = '', pathName = '' } = path;
requests = requests.reduce((acc, { title, items }) => {
acc[title] = [...items];
return acc;
}, {});
if (this.getAuthToken().type === 'basic') {
const { value = '' } = this.getAuthToken();
if (requests['header'] && requests['header'].length) {
requests['header'].unshift({ name: 'Authorization', value: `Basic ${value}`, disabled: true, type: 'text' });
} else {
requests['header'] = [{ name: 'Authorization', value: `Basic ${value}`, disabled: true, type: 'text' }];
}
}
if (this.getAuthToken().type === 'apiKey') {
const { value = '' } = this.getAuthToken();
const {in: inPlace, name} = auths.find(e => e.type === 'apiKey');
if (inPlace === 'header') {
if (requests['header'] && requests['header'].length) {
requests['header'].unshift({ name, value, disabled: true, type: 'text' });
} else {
requests['header'] = [{ name, value, disabled: true, type: 'text' }];
}
} else {
if (requests['query'] && requests['query'].length) {
requests['query'].unshift({ name, value, disabled: true, type: 'text' });
} else {
requests['query'] = [{ name, value, disabled: true, type: 'text' }];
}
}
}
this.setTitle(summary);
this.showSubmitButton();
this.resetFields();
this.injectPathUrl({methodName, pathName});
if (!!requests['header']) {
this.createRequestHeaders(requests['header']);
}
if (requests['query']) {
this.createRequestQuery(requests['query']);
}
if (requests['path']) {
this.createRequestPath(requests['path']);
}
if (requests['body']) {
this.createRequestBody(requests['body']);
}
if (requests['formData']) {
this.createRequestFormData(requests['formData']);
}
this.attachOnSubmit(this.requestSubmitHandler);
this.show();
}
getAuthToken() {
const {_apiKeyToken, _basicAuthToken, _oAuthToken} = this;
return {type: this._authType, value: _apiKeyToken || _basicAuthToken || _oAuthToken}
}
createAuthenticationForm() {
const {auths = []} = pathsObject;
this.setTitle('Authentication');
this.hideSubmitButton();
this.resetFields();
auths.forEach(({type = '', ...restProps}, i) => {
if (type === 'basic') {
this.createBasicAuth();
}
if (type === 'apiKey') {
this.createApiKeyAuth(restProps);
}
if (type === 'oauth2') {
this.createOauth(restProps);
}
if (auths.length > i + 1) {
this.injectBreak();
}
});
}
createBasicAuth() {
this.injectTitle('Basic Authentication');
this.createField({ type: 'text', name: 'Username', placeholder: 'Enter username' });
this.createField({ type: 'password', name: 'Password', placeholder: 'Enter password' });
this.injectButton({
name: 'basic-auth',
label: 'Authenticate',
onClick: () => {
const user = this.getFieldValue('Username');
const pass = this.getFieldValue('Password');
this._basicAuthToken = btoa(`${user}:${pass}`);
this._authType = 'basic';
this._apiKeyToken = null;
this._oAuthToken = null;
this.resetFields();
this.showSubmitButton();
if (this._pathId) {
return this.createRequest(this._pathId);
}
return this.hide();
}
});
}
createApiKeyAuth(props) {
const { in: inPlace = 'header', name = '', } = props;
this.injectTitle('API Key');
this.injectFieldHtml(`<p>An API key with the name of <code>${name}</code>, will be send in <code>${inPlace}</code></p>`);
this.createField({ type: 'text', name });
this.injectButton({
name: 'api-key',
label: 'Authenticate',
onClick: () => {
const token = this.getFieldValue(name);
this._authType = 'apiKey';
this._apiKeyToken = token;
this._basicAuthToken = null;
this._oAuthToken = null;
this.resetFields();
this.showSubmitButton();
if (this._pathId) {
return this.createRequest(this._pathId);
}
return this.hide();
}
});
}
createOauth(props) {
const {authorizationUrl = '', flow = '', scopes, tokenUrl = ''} = props;
this.injectTitle('Oauth 2.0');
this.injectFieldHtml(`
<p>Oauth Server Details</p>
<table class="table basic_table_info">
<tr>
<td>Authorization URL</td>
<td>${authorizationUrl}<td/>
</tr>
<tr>
<td>Token URL</td>
<td>${tokenUrl}<td/>
</tr>
<tr>
<td>Flow</td>
<td>${flow}<td/>
</tr>
</table>
<p>Scopes</p>
<table class="table basic_table_info">
${Object.keys(scopes).map(name => `
<tr>
<td>${name}</td>
<td>${scopes[name]}<td/>
</tr>
`).join('')}
</table>
`);
}
createResponseBody(body) {
let viewCode = '';
try {
viewCode = JSON.stringify(JSON.parse(body), null, 4);
} catch (e) {
viewCode = body;
}
this.injectFieldHtml(`
<p>Response Body</p>
<div class="highlight javascript">
<pre><code class="language-javascript" data-lang="javascript"><div class="zama-response-code">${viewCode}</div></code></pre>
</div>
`);
Prism.highlightElement($('.zama-response-code')[0])
}
createResponseHeaders(headers) {
this.injectFieldHtml(`
<p>Response Headers</p>
<table class="table basic_table_info">
${headers.map(({name, value}) => `
<tr>
<td>${name}</td>
<td>${value}<td/>
</tr>
`).join('')}
</table>
`);
}
createResponse({body, headers}) {
this.resetFields();
this.hideSubmitButton();
this.createResponseBody(body);
this.injectBreak();
this.createResponseHeaders(headers);
}
requestSubmitHandler(e) {
const {auths, paths, rootUrl = '' } = pathsObject;
const path = paths.find(({id}) => id === this._pathId);
let { requests = [], methodName = '', pathName = '' } = path;
requests = requests.reduce((acc, { title, items }) => {
acc[title] = [...items];
return acc;
}, {});
if (this.getAuthToken().type === 'basic') {
const { value = '' } = this.getAuthToken();
if (requests['header'] && requests['header'].length) {
requests['header'].unshift({ name: 'Authorization', value: `Basic ${value}`, disabled: true, type: 'text' });
} else {
requests['header'] = [{ name: 'Authorization', value: `Basic ${value}`, disabled: true, type: 'text' }];
}
}
if (this.getAuthToken().type === 'apiKey') {
const { value = '' } = this.getAuthToken();
const {in: inPlace, name} = auths.find(e => e.type === 'apiKey');
if (inPlace === 'header') {
if (requests['header'] && requests['header'].length) {
requests['header'].unshift({ name, value, disabled: true, type: 'text' });
} else {
requests['header'] = [{ name, value, disabled: true, type: 'text' }];
}
} else {
if (requests['query'] && requests['query'].length) {
requests['query'].unshift({ name, value, disabled: true, type: 'text' });
} else {
requests['query'] = [{ name, value, disabled: true, type: 'text' }];
}
}
}
let url = `${rootUrl}${pathName}`;
const options = {
method: methodName,
};
if (requests['header'] && requests['header'].length) {
options.headers = requests['header'].reduce((acc, cur) => {
const _value = cur.value || this.getFieldValue(cur.name);
if (_value) {
acc[cur.name] = _value;
}
return acc;
}, {});
}
if (requests['query'] && requests['query'].length) {
url += encodeURI(`?${requests['query'].map(({ name, value }) => {
const _value = value || this.getFieldValue(name);
if (!_value) return '';
return `${name}=${_value}`;
}).filter(e => e).join('&')}`);
}
if (requests['path'] && requests['path'].length) {
requests['path'].forEach(({name, value}) => {
const _value = value || this.getFieldValue(name);
if (_value) {
url = url.replace(`{${name}}`, _value);
}
});
}
if (requests['body'] && requests['body'].length) {
options.body = requests['body'].reduce((acc, cur) => {
const value = cur.value || this.getFieldValue(cur.name);
if (value) {
acc[cur.name] = value;
}
return acc;
}, {});
}
if (requests['formData'] && requests['formData'].length) {
options.body = encodeURI(`?${requests['formData'].map(({ name, value }) => {
const _value = value || this.getFieldValue(name);
return _value ? `${name}=${_value}` : '';
}).filter(e => e).join('&')}`)
}
let responseHeaders = [];
this.resetFields();
this.startLoading();
fetch(url, options)
.then(e => {
responseHeaders = Array.from(e.headers.entries()).map(e => ({name: e[0], value: e[1]}));
return e.text();
})
.then(e => this.createResponse({body: e, headers: responseHeaders}));
}
injectPathUrl({ methodName = '', pathName = 'get' }) {
const { rootUrl = '' } = pathsObject;
const _html = `
<p class="api-path mb-30" style="justify-content: flex-start; margin-bottom: 20px !important; overflow: scroll;">
<span class="api-method api-method-${methodName.toLowerCase()}">${methodName}</span>
<span class="api-path-root">${rootUrl}</span>
<span class="api-path-name">${pathName}</span>
</p>
`;
$('#zamaModal .modal-body').prepend(_html);
}
startLoading() {
this.injectFieldHtml(`
<div style="display: flex;justify-content: center;">
<img src="${window.ASSETS_ROOT}assets/img/loading.svg" />
</div>
`);
}
injectPopup() {
const { auths = [] } = pathsObject;
const _temp = `
<div class="modal fade" id="zamaModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<form class="modal-content">
<div class="modal-header">
<h5 class="modal-title"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body"></div>
<div class="modal-footer">
${auths.length ? `<span class="btn-re-auth" style="color: #10b3d6; cursor: pointer;">Authenticate</span>` : ''}
<button type="submit" class="btn btn-primary send-request-btn">Send Request</button>
</div>
</form>
</div>
</div>
`;
$(document.body).append(_temp);
$('.btn-re-auth').off('click');
$('.btn-re-auth').click(() => this.createAuthenticationForm());
}
}