Skip to content

Commit

Permalink
feat: render missed parts of server object (#189)
Browse files Browse the repository at this point in the history
  • Loading branch information
magicmatatjahu authored Oct 12, 2021
1 parent b64f2a4 commit f1e2ab5
Show file tree
Hide file tree
Showing 10 changed files with 1,049 additions and 121 deletions.
4 changes: 3 additions & 1 deletion .sonarcloud.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# Disable specific file since it would introduce more complexity to reduce it - mainly code complexity and complex template literals
sonar.exclusions=components/Schema.js,helpers/schema.js
sonar.exclusions=components/Schema.js,helpers/schema.js
# Disable duplicate code in tests since it would introduce more complexity to reduce it.
sonar.cpd.exclusions=test/**
4 changes: 2 additions & 2 deletions components/Info.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Text } from "@asyncapi/generator-react-sdk";

import { Tags } from "./Tags";
import { Header, Link, Image, List } from "./common";
import { Header, Link, Image, List, NewLine } from "./common";

export function Info({ asyncapi, params = {} }) {
const info = asyncapi.info();
Expand Down Expand Up @@ -91,7 +91,7 @@ export function Info({ asyncapi, params = {} }) {
{infoList.length && (
<>
<List list={infoList} />
<Text />
<NewLine />
</>
)}

Expand Down
302 changes: 196 additions & 106 deletions components/Servers.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { Text } from "@asyncapi/generator-react-sdk";
import { IndentationTypes, Text } from "@asyncapi/generator-react-sdk";

import { Header, Table } from "./common";
import { Header, ListItem, Link, Table, NewLine } from "./common";
import { ServerHelpers } from "../helpers/server";
import { FormatHelpers } from "../helpers/format";

export function Servers({ asyncapi }) {
if (!asyncapi.hasServers()) {
return null;
}
const servers = Object.entries(asyncapi.servers()).map(([serverName, server]) => (
<Server serverName={serverName} server={server} asyncapi={asyncapi} />
<Server serverName={serverName} server={server} asyncapi={asyncapi} key={serverName} />
));

return (
Expand All @@ -21,41 +23,45 @@ export function Servers({ asyncapi }) {
}

function Server({ serverName, server, asyncapi }) {
const headers = ['URL', 'Protocol', 'Description'];
const rowRenderer = (entry) => [
entry.url(),
`${server.protocol()}${server.protocolVersion() ? ` ${server.protocolVersion()}` : ''}`,
entry.description() || '-',
];

return (
<>
<Text>
<Header type={3}>{`**${serverName}** Server`}</Header>
<Table headers={headers} rowRenderer={rowRenderer} data={[server]} />
</Text>
<Header type={3}>{`\`${serverName}\` Server`}</Header>
<ServerInfo server={server} />
<ServerVariables variables={server.variables()} />
<ServerSecurity protocol={server.protocol()} security={server.security()} asyncapi={asyncapi} />
</>
);
}

function ServerInfo({ server }) {
return (
<Text>
<Text>
<ListItem>URL: `{server.url()}`</ListItem>
<ListItem>Protocol: `{server.protocol()}{server.protocolVersion() ? ` ${server.protocolVersion()}` : ''}`</ListItem>
</Text>
{server.hasDescription() && (
<Text>
{server.description()}
</Text>
)}
</Text>
);
}

function ServerVariables({ variables }) {
if (!variables || !Object.keys(variables).length) {
return null;
}

const variableHeader = ['Name', 'Default value', 'Possible values', 'Description'];
const variableRenderer = (variable) => [
variable.name || '-',
variable.hasDefaultValue() ? variable.defaultValue() : '*None*',
variable.hasAllowedValues() ? variable.allowedValues().join(', ') : 'Any',
const variableHeader = ['Name', 'Description', 'Default value', 'Allowed values'];
const variableRenderer = ([variableName, variable]) => [
variableName || '-',
variable.description() || '-',
variable.hasDefaultValue() ? FormatHelpers.inlineCode(variable.defaultValue()) : '_None_',
variable.hasAllowedValues() ? `${variable.allowedValues().map(FormatHelpers.inlineCode).join(', ')}` : '_Any_',
];
const variablesData = Object.entries(variables).map(([variableName, variable]) => {
variable.name = variableName;
return variable;
});
const variablesData = Object.entries(variables);

return (
<Text>
Expand All @@ -66,105 +72,189 @@ function ServerVariables({ variables }) {
}

function ServerSecurity({ protocol, security, asyncapi }) {
if (protocol === 'kafka' || protocol === 'kafka-secure') {
return <KafkaServerSecurity protocol={protocol} security={security} asyncapi={asyncapi} />
const securitySchemes =
asyncapi.hasComponents() && asyncapi.components().securitySchemes();

let renderedRequirements;
if (
!security ||
!security.length ||
!securitySchemes ||
!Object.keys(securitySchemes).length
) {
if (protocol === 'kafka' || protocol === 'kafka-secure') {
renderedRequirements = (
<SecurityRequirementItem protocol={protocol} requirement={null} />
);
}
} else {
renderedRequirements = security
.map((requirement, idx) => (
<SecurityRequirementItem protocol={protocol} requirement={requirement} securitySchemes={securitySchemes} index={idx} key={idx} />
))
.filter(Boolean);

if (renderedRequirements.length === 0) {
return null;
}
}
if (!security) {

if (!renderedRequirements) {
return null;
}

const securityHeader = ['Type', 'In', 'Name', 'Scheme', 'Format', 'Description'];
const securityRenderer = (entry) => [
entry.type() || '-',
entry.in() || '-',
entry.name() || '-',
entry.scheme() || '-',
entry.bearerFormat() || '-',
entry.description() || '-',
];
return (
<Text>
<Header type={4}>Security</Header>
<Text>
{renderedRequirements}
</Text>
</Text>
);
}

const components = asyncapi.components();
const securityData = security.map(s => {
const key = Object.keys(s.json())[0];
return components.securityScheme(key);
});
function SecurityRequirementItem({ protocol, requirement, securitySchemes, index = 0 }) {
let renderedServerSecurities;
if (requirement === null && (protocol === 'kafka' || protocol === 'kafka-secure')) {
renderedServerSecurities = (
<ServerSecurityItem protocol={protocol} securitySchema={null} />
);
} else if (requirement) {
renderedServerSecurities = Object.entries(requirement.json())
.map(([requiredKey, requiredScopes]) => {
const securitySchema = securitySchemes[requiredKey];
if (!securitySchema) {
return;
}
return (
<ServerSecurityItem
protocol={protocol}
securitySchema={securitySchema}
requiredScopes={requiredScopes}
key={securitySchema.type() || protocol}
/>
);
})
.filter(Boolean);

if (!renderedServerSecurities.length) {
return null;
}
}

if (!renderedServerSecurities) {
return null;
}

return (
<Text>
<Header type={4}>Security Requirements</Header>
<Table headers={securityHeader} rowRenderer={securityRenderer} data={securityData} />
<Header type={5}>Security Requirement {`${index + 1}`}</Header>
<Text>
{renderedServerSecurities}
</Text>
</Text>
);
}

function KafkaServerSecurity({ protocol, security, asyncapi }) {
let securityData;
if (security) {
const components = asyncapi.components();
securityData = security.map(s => {
const key = Object.keys(s.json())[0];
return components.securityScheme(key);
});
}
else {
securityData = [null];
}
function ServerSecurityItem({ protocol, securitySchema, requiredScopes = [] }) {
let schemas = [];
renderServerSecuritySchemasBasic({ securitySchema, schemas });
renderServerSecuritySchemasKafka({ protocol, securitySchema, schemas });
renderServerSecuritySchemasFlows({ securitySchema, requiredScopes, schemas })
schemas = schemas.filter(Boolean);

const securityHeader = ['Type', 'Description', 'security.protocol', 'sasl.mechanism'];

const securityRenderer = (entry) => {
let securityProtocol, saslMechanism;
if (protocol === 'kafka') {
if (entry) {
securityProtocol = 'SASL_PLAINTEXT';
}
else {
securityProtocol = 'PLAINTEXT';
}
const type = securitySchema && securitySchema.type() && ServerHelpers.securityType(securitySchema.type());
return (
<Text>
{type && <ListItem>Type: `{type}`</ListItem>}
{schemas.length && (
<Text indent={2} type={IndentationTypes.SPACES}>
{schemas}
</Text>
)}
{securitySchema && securitySchema.hasDescription() && (
<Text indent={2} type={IndentationTypes.SPACES}>
{securitySchema.description()}
</Text>
)}
</Text>
);
}

function renderServerSecuritySchemasBasic({ securitySchema, schemas }) {
if (securitySchema) {
if (securitySchema.name()) {
schemas.push(<ListItem key='name'>Name: {securitySchema.name()}</ListItem>);
}
if (securitySchema.in()) {
schemas.push(<ListItem key='in'>In: {securitySchema.in()}</ListItem>);
}
if (securitySchema.scheme()) {
schemas.push(<ListItem key='scheme'>Scheme: {securitySchema.scheme()}</ListItem>);
}
else {
if (entry) {
securityProtocol = 'SASL_SSL';
}
else {
securityProtocol = 'SSL';
}
if (securitySchema.bearerFormat()) {
schemas.push(<ListItem key='bearerFormat'>Bearer format: {securitySchema.bearerFormat()}</ListItem>);
}
if (entry) {
switch (entry.type()) {
case 'plain':
saslMechanism = 'PLAIN';
break;
case 'scramSha256':
saslMechanism = 'SCRAM-SHA-256';
break;
case 'scramSha512':
saslMechanism = 'SCRAM-SHA-512';
break;
case 'oauth2':
saslMechanism = 'OAUTHBEARER';
break;
case 'gssapi':
saslMechanism = 'GSSAPI';
break;
case 'X509':
securityProtocol = 'SSL';
break;
}
if (securitySchema.openIdConnectUrl()) {
schemas.push(
<ListItem key='openIdConnectUrl'>
OpenID Connect URL:{' '}
<Link href={securitySchema.openIdConnectUrl()}>
{securitySchema.openIdConnectUrl()}
</Link>
</ListItem>
);
}
}
}

function renderServerSecuritySchemasKafka({ protocol, securitySchema, schemas }) {
const isKafkaProtocol = protocol === 'kafka' || protocol === 'kafka-secure';
if (!isKafkaProtocol) {
return;
}

return [
(entry && entry.type()) || '-',
(entry && entry.description()) || '-',
securityProtocol || '-',
saslMechanism || '-'
];
};
const { securityProtocol, saslMechanism } = ServerHelpers.getKafkaSecurity(
protocol,
securitySchema,
);

return (
<Text>
<Header type={4}>Security Requirements</Header>
<Table headers={securityHeader} rowRenderer={securityRenderer} data={securityData} />
</Text>
if (securityProtocol) {
schemas.push(<ListItem key='security.protocol'>security.protocol: {securityProtocol}</ListItem>);
}
if (saslMechanism) {
schemas.push(<ListItem key='sasl.mechanism'>sasl.mechanism: {saslMechanism}</ListItem>);
}
}

function renderServerSecuritySchemasFlows({ securitySchema, requiredScopes, schemas }) {
const hasFlows = securitySchema && securitySchema.flows() && Object.keys(securitySchema.flows()).length;
if (!hasFlows) {
return;
}

const flowsHeader = ['Flow', 'Auth URL', 'Token URL', 'Refresh URL', 'Scopes'];
const flowsRenderer = ([flowName, flow]) => [
ServerHelpers.flowName(flowName) || '-',
flow.authorizationUrl() ? `[${flow.authorizationUrl()}](${flow.authorizationUrl()})` : '-',
flow.tokenUrl() ? `[${flow.tokenUrl()}](${flow.tokenUrl()})` : '-',
flow.refreshUrl() ? `[${flow.refreshUrl()}](${flow.refreshUrl()})` : '-',
Object.keys(flow.scopes()).length ? Object.keys(flow.scopes()).map(v => `\`${v}\``).join(', ') : '-',
];
const flowsData = Object.entries(securitySchema.flows());

schemas.push(
<ListItem key='flows'>
Flows:
<NewLine numbers={2} />
{requiredScopes.length && (
<Text indent={2} type={IndentationTypes.SPACES} newLines={2}>
Required scopes: {requiredScopes.map(v => `\`${v}\``).join(', ')}
</Text>
)}
<Text indent={2} type={IndentationTypes.SPACES}>
<Table headers={flowsHeader} rowRenderer={flowsRenderer} data={flowsData} />
</Text>
</ListItem>
);
}
}
Loading

0 comments on commit f1e2ab5

Please sign in to comment.