Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
fmvilas committed Mar 20, 2020
0 parents commit 48b3129
Show file tree
Hide file tree
Showing 23 changed files with 1,366 additions and 0 deletions.
159 changes: 159 additions & 0 deletions .filters/all.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
const _ = require('lodash');
const Markdown = require('markdown-it');
const OpenAPISampler = require('openapi-sampler');

module.exports = ({ Nunjucks }) => {
Nunjucks.addFilter('split', (string, separator) => {
if (typeof string !== 'string') return string;
const regex = new RegExp(separator, 'g');
return string.split(regex);
});

Nunjucks.addFilter('firstKey', (obj) => {
if (!obj) return '';
return Object.keys(obj)[0];
});

Nunjucks.addFilter('isExpandable', (obj) => {
if (
obj.type() === 'object' ||
obj.type() === 'array' ||
(obj.oneOf() && obj.oneOf().length) ||
(obj.anyOf() && obj.anyOf().length) ||
(obj.allOf() && obj.allOf().length) ||
obj.items() ||
obj.additionalItems() ||
(obj.properties() && Object.keys(obj.properties()).length) ||
obj.additionalProperties() ||
(obj.extensions() && Object.keys(obj.extensions()).filter(e => !e.startsWith('x-parser-')).length) ||
obj.patternProperties()
) return true;

return false;
});

/**
* Check if there is a channel which does not have one of the tags specified.
*/
Nunjucks.addFilter('containTags', (object, tagsToCheck) => {
if (!object) {
throw new Error("object for containsTag was not provided?");
}

if (!tagsToCheck) {
throw new Error("tagsToCheck for containsTag was not provided?");
}

//Ensure if only 1 tag are provided it is converted to array.
if (tagsToCheck && !Array.isArray(tagsToCheck)) {
tagsToCheck = [tagsToCheck];
}

//Check if pubsub contain one of the tags to check.
let check = (tag) => {
let found = false;
for (let tagToCheckIndex in tagsToCheck) {
let tagToCheck = tagsToCheck[tagToCheckIndex]._json;
if (tagToCheck.name === tag.name) {
found = true;
}
}
return found;
};

//Ensure tags are checked for the group tags
let containTags = object._json.tags ? object._json.tags.find(check) != null : false;
return containTags;
});

/**
* Check if there is a channel which does not have one of the tags specified.
*/
Nunjucks.addFilter('containNoTag', (channels, tagsToCheck) => {
if (!channels) {
throw new Error("Channels for containNoTag was not provided?");
}
for (let channelIndex in channels) {
let channel = channels[channelIndex]._json;
//Check if the channel contains publish or subscribe which does not contain tags
if (channel.publish && (!channel.publish.tags || channel.publish.tags.length == 0) ||
channel.subscribe && (!channel.subscribe.tags || channel.subscribe.tags.length == 0)
) {
//one does not contain tags
return true;
}

//Check if channel publish or subscribe does not contain one of the tags to check.
let check = (tag) => {
let found = false;
for (let tagToCheckIndex in tagsToCheck) {
let tagToCheck = tagsToCheck[tagToCheckIndex]._json;
if (tagToCheck.name === tag.name) {
found = true;
}
}
return found;
};

//Ensure pubsub tags are checked for the group tags
let publishContainsNoTag = channel.publish && channel.publish.tags ? channel.publish.tags.find(check) == null : false;
if (publishContainsNoTag === true) return true;
let subscribeContainsNoTag = channel.subscribe && channel.subscribe.tags ? channel.subscribe.tags.find(check) == null : false;
if (subscribeContainsNoTag === true) return true;
}
return false;
});

Nunjucks.addFilter('isArray', (arr) => {
return Array.isArray(arr);
});

Nunjucks.addFilter('isObject', (obj) => {
return typeof obj === 'object' && obj !== null;
});

Nunjucks.addFilter('contains', (array, element) => {
if (!array || !Array.isArray(array)) return false;
return array.includes(element);
});

Nunjucks.addFilter('log', (anything) => {
console.log(anything);
});

Nunjucks.addFilter('markdown2html', (md) => {
return Markdown().render(md || '');
});

Nunjucks.addFilter('getPayloadExamples', (msg) => {
if (Array.isArray(msg.examples()) && msg.examples().find(e => e.payload)) {
// Instead of flat or flatmap use this.
return _.flatMap(msg.examples().map(e => e.payload).filter(Boolean));
}

if (msg.payload() && msg.payload().examples()) {
return msg.payload().examples();
}
});

Nunjucks.addFilter('getHeadersExamples', (msg) => {
if (Array.isArray(msg.examples()) && msg.examples().find(e => e.headers)) {
// Instead of flat or flatmap use this.
return _.flatMap(msg.examples().map(e => e.headers).filter(Boolean));
}

if (msg.headers() && msg.headers().examples()) {
return msg.headers().examples();
}
});

Nunjucks.addFilter('generateExample', (schema) => {
return JSON.stringify(OpenAPISampler.sample(schema) || '', null, 2);
});

Nunjucks.addFilter('nonParserExtensions', (schema) => {
if (!schema || !schema.extensions || typeof schema.extensions !== 'function') return new Map();
const extensions = Object.entries(schema.extensions());
return new Map(extensions.filter(e => !e[0].startsWith('x-parser-')).filter(Boolean));
});
};
7 changes: 7 additions & 0 deletions .partials/content.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% include ".partials/introduction.html" %}
{% if asyncapi.hasServers() %}
{% include ".partials/servers.html" %}
{% endif %}
{% if asyncapi.hasChannels() %}
{% include ".partials/operations.html" %}
{% endif %}
36 changes: 36 additions & 0 deletions .partials/example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{% macro example(msg, channelName) %}

<form>
<input type="radio" checked id="example-payload-{{channelName}}-{{msg.uid()}}" name="tab" class="examples-payload-tab hidden" {% if not msg.payload() %}disabled{% endif %}/>
<label for="example-payload-{{channelName}}-{{msg.uid()}}" class="py-1 px-2 mr-2 mb-4 text-grey-lightest text-sm border rounded focus:outline-none cursor-pointer {% if not msg.payload() %}text-grey-dark border-grey-dark opacity-25{% else %}text-grey-lightest{% endif %}" {% if not msg.payload() %}title="This message does not have a payload"{% endif %}>Payload</label>

<input type="radio" id="example-headers-{{channelName}}-{{msg.uid()}}" name="tab" class="examples-headers-tab hidden" {% if not msg.headers() %}disabled{% endif %} />
<label for="example-headers-{{channelName}}-{{msg.uid()}}"
class="py-1 px-2 mr-2 mb-4 text-sm border rounded focus:outline-none cursor-pointer {% if not msg.headers() %}text-grey-dark border-grey-dark opacity-25{% else %}text-grey-lightest{% endif %}" {% if not msg.headers() %}title="This message does not have headers"{% endif %}>Headers</label>

<div class="payload-examples mt-4">
{% if msg | getPayloadExamples | length %}
{% for ex in msg | getPayloadExamples %}
<h6 class="text-xs font-bold text-grey-darker">Example #{{loop.index}}</h6>
<pre class="hljs mb-4 border border-grey-darkest rounded"><code>{{ex | dump(2) | safe }}</code></pre>
{% endfor %}
{% elif msg.payload() %}
<pre class="hljs mb-4 border border-grey-darkest rounded"><code>{{ msg.payload().json() | generateExample | safe }}</code></pre>
<h6 class="text-xs font-bold text-grey-darker italic">This example has been generated automatically.</h6>
{% endif %}
</div>

<div class="headers-examples mt-4">
{% if msg | getHeadersExamples | length %}
{% for ex in msg | getHeadersExamples %}
<h6 class="text-xs font-bold text-grey-darker">Example #{{loop.index}}</h6>
<pre class="hljs mb-4 border border-grey-darkest rounded"><code>{{ ex | dump(2) | safe }}</code></pre>
{% endfor %}
{% elif msg.headers() %}
<pre class="hljs mb-4 border border-grey-darkest rounded"><code>{{ msg.headers().json() | generateExample | safe }}</code></pre>
<h6 class="text-xs font-bold text-grey-darker italic">This example has been generated automatically.</h6>
{% endif %}
</div>
</form>

{% endmacro %}
32 changes: 32 additions & 0 deletions .partials/introduction.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<div class="center-block p-8">
<a name="introduction"></a>
<h1>{{asyncapi.info().title()}} {{asyncapi.info().version()}}</h1>
<div class="leading-normal mb-4">
{% if asyncapi.info().termsOfService() %}
<a class="border border-solid border-purple-lighter hover:bg-purple-lighter hover:text-purple-dark font-bold no-underline text-purple text-xs uppercase rounded mr-2 px-3 py-1"
href="{{asyncapi.info().termsOfService()}}" target="_blank">Terms of Service</a>
{% endif %}
{% if asyncapi.info().license() %}
<a class="border border-solid border-orange-lighter hover:bg-orange-lighter hover:text-orange-dark font-bold no-underline text-orange text-xs uppercase rounded mr-2 px-3 py-1"
href="{{asyncapi.info().license().url()}}" target="_blank">{{asyncapi.info().license().name()}}</a>
{% endif %}
</div>

<div class="markdown">
{{ asyncapi.info().description() | markdown2html | safe }}
</div>


<div class="leading-normal mb-4">
{% if asyncapi.info().contact() %}
{% if asyncapi.info().contact().url() %}
Contact link: <a class="border border-solid border-purple-lighter hover:bg-purple-lighter hover:text-purple-dark font-bold no-underline text-purple text-xs uppercase rounded mr-2 px-3 py-1"
href="{{asyncapi.info().contact().url()}}" target="_blank">{%- if asyncapi.info().contact().name() %}{{asyncapi.info().contact().name()}}{%- else %}{{asyncapi.info().contact().url()}}{%- endif %}</a>
{% endif %}
{% if asyncapi.info().contact().email() %}
Contact email: <a class="border border-solid border-purple-lighter hover:bg-purple-lighter hover:text-purple-dark font-bold no-underline text-purple text-xs uppercase rounded mr-2 px-3 py-1"
href="{{asyncapi.info().contact().email()}}" target="_blank">{{asyncapi.info().contact().email()}}</a>
{% endif %}
{% endif %}
</div>
</div>
31 changes: 31 additions & 0 deletions .partials/message.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{% from "./schema.html" import schema %}

{% macro message(msg, showIndex=false, index=0, open=false) %}

<div class="bg-grey-lighter rounded p-4 mt-2">
<div class="text-sm text-grey-darker mb-2">
{% if showIndex %}
<span class="text-grey-darker font-bold mr-2">#{{index}}</span>
{% endif %}
{% if msg.title() %}
{{msg.title()}}
{% if msg.name() %}
<span class="border text-orange rounded text-xs ml-3 py-0 px-2">{{msg.name()}}</span>
{% endif %}
{% else %}
{% if msg.name() %}
<span class="border text-orange rounded text-s py-0 px-2">{{msg.name()}}</span>
{% endif %}
{% endif %}
</div>
<p class="text-grey-dark text-sm">{{msg.summary()}}</p>
<div class="mt-4 mb-4 markdown">{{ msg.description() | markdown2html | safe }}</div>
{{ schema(msg.payload(), 'Payload', open=open) }}
{% if msg.headers() %}
<div class="mt-4">
{{ schema(msg.headers(), 'Headers', open=open) }}
</div>
{% endif %}
</div>

{% endmacro %}
81 changes: 81 additions & 0 deletions .partials/operation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{% from "./sliced-string.html" import slicedString %}
{% from "./tags.html" import tags %}
{% from "./message.html" import message %}
{% from "./example.html" import example %}
{% from "./schema-prop.html" import schemaProp %}


{% macro operation(operation, operationType, channelName, channel) %}

<a name="operation-{{operationType}}-{{channelName}}"></a>
<div class="center-block p-8">
<div class="operation pt-8 pb-8">
<h3 class="font-mono text-base">
{% if operationType === 'publish' %}
<span class="font-mono border border-blue text-blue uppercase p-1 rounded" title="Publish">Pub</span>
{% endif %}
{% if operationType === 'subscribe' %}
<span class="font-mono border border-green-dark text-green-dark uppercase p-1 rounded"
title="Subscribe">Sub</span>
{% endif %}
{{ slicedString(channelName) }}
</h3>

<div class="mt-4 mb-4 markdown">{{ channel.description() | markdown2html | safe }}</div>

<p class="text-grey text-sm">{{operation.summary()}}</p>
<div class="mt-4 mb-4 markdown">{{ operation.description() | markdown2html | safe }}</div>



{% if channel.parameters() | length %}
<div class="mt-2">
<div class="is-open">
<div class="js-prop cursor-pointer py-2 flex property">
<div class="pr-4" style="margin-top:-2px; min-width: 25%;">
<span class="text" style="word-break: break-word;">Parameters</span>
<svg class="expand" version="1.1" viewBox="0 0 24 24" x="0" xmlns="http://www.w3.org/2000/svg" y="0">
<polygon points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "></polygon>
</svg>
</div>
</div>
<div class="children bg-grey-lighter p-4 rounded">
<div class="bg-grey-lightest pl-8 pr-8 rounded">
{% for parameterName, parameter in channel.parameters() %}
{{ schemaProp(parameter.schema(), parameterName, odd=false, specialName=false, required=true) }}
{% endfor %}
</div>
</div>
</div>
</div>
{% endif %}


{% if operation.hasMultipleMessages() %}
<p>Accepts <strong>one of</strong> the following messages:</p>
{% for msg in operation.messages() %}
{{ message(msg, showIndex=true, index=loop.index, open=false) }}
{% endfor %}
{% else %}
<p>Accepts the following message:</p>
{{ message(operation.message(0), showIndex=false, open=true) }}
{% endif %}

{{ tags(operation.tags()) }}
</div>
</div>

<div class="right-block p-8">
<h4 class="text-lg text-white mb-4">Examples</h4>

{% if operation.hasMultipleMessages() %}
{% for msg in operation.messages() %}
{{ example(msg, channelName) }}
{% endfor %}
{% else %}
{{ example(operation.message(0), channelName) }}
{% endif %}
</div>


{% endmacro %}
17 changes: 17 additions & 0 deletions .partials/operations.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{% from "./operation.html" import operation %}

<a name="operations"></a>
<h2 class="mb-4 ml-8">Operations</h2>

{% for channelName, channel in asyncapi.channels() %}
<div class="responsive-container">
{% if channel.hasPublish() %}
{{ operation(channel.publish(), 'publish', channelName, channel) }}
{% endif %}
</div>
<div class="responsive-container">
{% if channel.hasSubscribe() %}
{{ operation(channel.subscribe(), 'subscribe', channelName, channel) }}
{% endif %}
</div>
{% endfor %}
Loading

0 comments on commit 48b3129

Please sign in to comment.