Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: toc and operation reply #523

Merged
merged 26 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
eb8b9d5
feat: common helpers with tests
korifey91 Apr 1, 2024
c3dd464
chore: update `@asyncapi/cli` and `@asyncapi/parser` versions
korifey91 Apr 1, 2024
7b2f685
fix: show operation reply messages
korifey91 Apr 1, 2024
400b4d0
fix(table of contents): correctly show all operations
korifey91 Apr 1, 2024
deaa2eb
feat(table of contents): use new `CommonHelpers` class for `Operation…
korifey91 Apr 9, 2024
a7e0ffc
fix: adjust `v2` operation types constants
korifey91 Apr 10, 2024
4182d41
feat(test): handle v3 `request` operation type
korifey91 Apr 10, 2024
eea544d
feat: move `isV3` helper into `CommonHelpers`
korifey91 Apr 10, 2024
0cb2e29
feat: show short operation type for v2
korifey91 Apr 10, 2024
2ab0ff8
chore: add comment to v2 operation types
korifey91 Apr 10, 2024
5b9c7f3
feat(test): add `CommonHelpers.isV3` unit tests
korifey91 Apr 10, 2024
9dcdd1e
feat: common helpers with tests
korifey91 Apr 1, 2024
130daed
chore: update `@asyncapi/cli` and `@asyncapi/parser` versions
korifey91 Apr 1, 2024
916ac2a
fix: show operation reply messages
korifey91 Apr 1, 2024
73bf5ed
fix(table of contents): correctly show all operations
korifey91 Apr 1, 2024
3de2a03
feat(table of contents): use new `CommonHelpers` class for `Operation…
korifey91 Apr 9, 2024
3b87982
fix: adjust `v2` operation types constants
korifey91 Apr 10, 2024
9199903
feat(test): handle v3 `request` operation type
korifey91 Apr 10, 2024
0e75817
feat: move `isV3` helper into `CommonHelpers`
korifey91 Apr 10, 2024
414da8a
feat: show short operation type for v2
korifey91 Apr 10, 2024
09da43e
chore: add comment to v2 operation types
korifey91 Apr 10, 2024
2839be0
feat(test): add `CommonHelpers.isV3` unit tests
korifey91 Apr 10, 2024
e5acf7a
Merge remote-tracking branch 'origin/master'
korifey91 Apr 11, 2024
24182cd
fix(test): create asyncapi doc with version using `createAsyncAPIDocu…
korifey91 Apr 11, 2024
0839c64
fix(test): use valid `bindingVersion` in `Operations` test in case `s…
korifey91 Apr 11, 2024
47b462b
fix(test): update snapshots
korifey91 Apr 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 23 additions & 53 deletions components/Operations.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Tags } from './Tags';
import { Header, ListItem, Link } from './common';
import { SchemaHelpers } from '../helpers/schema';
import { FormatHelpers } from '../helpers/format';
import { CommonHelpers } from '../helpers/common';

// eslint-disable-next-line no-unused-vars
import { AsyncAPIDocumentInterface, OperationInterface, ChannelInterface } from '@asyncapi/parser';
Expand All @@ -17,7 +18,7 @@ function isV3({asyncapi}) {
}

/**
* @param {{asyncapi: AsyncAPIDocumentInterface}} param0
* @param {{asyncapi: AsyncAPIDocumentInterface}} param0
*/
export function Operations({ asyncapi }) {
const channels = asyncapi.channels();
Expand All @@ -30,20 +31,8 @@ export function Operations({ asyncapi }) {
const channelName = channel.address();
const operations = channel.operations().all();
operations.map(operation => {
let type;
if (operation.isSend()) {
if (operation.reply() !== undefined) {
type = 'request';
} else {
type = 'send';
}
} else if (operation.isReceive()) {
if (operation.reply() !== undefined) {
type = 'reply';
} else {
type = 'receive';
}
}
const type = CommonHelpers.getOperationType(operation, asyncapi);

operationsList.push(
<Operation
key={`${operation.action()}-${channelName}`}
Expand All @@ -66,33 +55,8 @@ export function Operations({ asyncapi }) {
</>
);
}
function getRenderedTypeForOperation({asyncapi, type}) {
const isv3 = isV3({asyncapi});
if (isv3) {
switch (type) {
case 'request':
return 'REQUEST';
case 'send':
return 'SEND';
case 'reply':
return 'REPLY';
case 'receive':
return 'RECEIVE';
}
}
// For v2, we render the application view still
// Meaning the when you use publish operation it means other publish to your application because your application is subscribing to it.
switch (type) {
case 'send': // This is the publish operation
return 'SUB';
case 'receive': // This is the subscribe operation
return 'PUB';
}
// This case should never happen, if it does this function needs to be changed
return 'UNKNOWN';
}
/**
* @param {{asyncapi: AsyncAPIDocumentInterface, type: string, operation: OperationInterface, channelName: string, channel: ChannelInterface}} param0
* @param {{asyncapi: AsyncAPIDocumentInterface, type: string, operation: OperationInterface, channelName: string, channel: ChannelInterface}} param0
*/
function Operation({ asyncapi, type, operation, channelName, channel }) { // NOSONAR
if (!operation || !channel) {
Expand All @@ -104,14 +68,13 @@ function Operation({ asyncapi, type, operation, channelName, channel }) { // NOS
const applyToAllServers = asyncapi.servers().all().length === channel.servers().all().length;
const servers = applyToAllServers ? [] : channel.servers().all();
const security = operation.security();
const renderedType = getRenderedTypeForOperation({asyncapi, type});

const showInfoList = operationId || (servers && servers.length);

return (
<Text>
<Header type={3}>
{`${renderedType} \`${channelName}\` Operation`}
{`${type.toUpperCase()} \`${channelName}\` Operation`}
</Header>

{operation.summary() && (
Expand Down Expand Up @@ -177,13 +140,13 @@ function Operation({ asyncapi, type, operation, channelName, channel }) { // NOS

<OperationMessages operation={operation} asyncapi={asyncapi} type={type} />

<OperationReply operation={operation} />
<OperationReply operation={operation} type={type} />
</Text>
);
}

/**
* @param {{channel: ChannelInterface}} param0
* @param {{channel: ChannelInterface}} param0
*/
function OperationParameters({ channel }) {
const parameters = SchemaHelpers.parametersToSchema(channel.parameters().all());
Expand Down Expand Up @@ -214,7 +177,7 @@ function getOperationMessageText({asyncapi, type}) {
return messagesText;
}
/**
* @param {{operation: OperationInterface, asyncapi: AsyncAPIDocumentInterface, type: string}} param0
* @param {{operation: OperationInterface, asyncapi: AsyncAPIDocumentInterface, type: string}} param0
*/
function OperationMessages({ asyncapi, operation, type }) {
const messages = operation.messages().all();
Expand All @@ -238,13 +201,18 @@ function OperationMessages({ asyncapi, operation, type }) {
}

/**
* @param {{operation: OperationInterface}} param0
* @param {{operation: OperationInterface}} param0
*/
function OperationReply({ operation, type }) {
const reply = operation.reply();
if (reply === undefined) {
return null;
}

const replyMessages = reply.messages().length
? reply.messages()
: reply.channel().messages();

const explicitChannel = reply.channel();

let typeText;
Expand All @@ -267,17 +235,19 @@ function OperationReply({ operation, type }) {
{`${typeText} information`}
</Header>

{explicitChannel && <ListItem>{type} should be done to channel: `{explicitChannel.address()}`</ListItem>}

<OperationReplyAddress name="Operation reply address" reply={reply} />
{explicitChannel?.address() ? (
<ListItem>{type} should be done to channel: `{explicitChannel.address()}`</ListItem>
) : (
<OperationReplyAddress name="Operation reply address" reply={reply} />
)}

<>
{reply.messages().length > 1 && (
{replyMessages.length > 1 && (
<Text newLines={2}>
{messagesText}
</Text>
)}
{reply.messages().length > 1 && reply.messages().map((msg, idx) => (
{replyMessages.all().map((msg, idx) => (
<Message message={msg} key={`message-${idx}`} />
))}
</>
Expand All @@ -287,7 +257,7 @@ function OperationReply({ operation, type }) {
}

/**
* @param {{reply: OperationReplyInterface}} param0
* @param {{reply: OperationReplyInterface}} param0
*/
function OperationReplyAddress({ reply }) {
const address = reply.address();
Expand Down
86 changes: 45 additions & 41 deletions components/TableOfContents.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,68 @@
import { Text, Indent, IndentationTypes } from '@asyncapi/generator-react-sdk';

import { Header, Link, ListItem } from '../components/common';

import { FormatHelpers } from '../helpers/format';
import { CommonHelpers } from '../helpers/common';

export function TableOfContents({ asyncapi }) {
const serversList = asyncapi.servers().all().map(server => {
const serverName = server.id();
return (
<Indent size={2} type={IndentationTypes.SPACES} key={serverName}>
<ListItem>
<Link href={`#${FormatHelpers.slugify(serverName)}-server`}>{serverName}</Link>
</ListItem>
</Indent>
);
});
import { Header, Link, ListItem } from './common';

const operationsList = [];
asyncapi.channels().all().map(channel => {
const channelName = channel.address();
channel.operations().all().forEach(operation => {
if (operation.action() === 'publish') {
operationsList.push(
<Indent size={2} type={IndentationTypes.SPACES} key={`pub-${channelName}`}>
<ListItem>
<Link href={`#pub-${FormatHelpers.slugify(channelName)}-operation`}>PUB {channelName}</Link>
</ListItem>
</Indent>
);
}
if (operation.action() === 'subscribe') {
operationsList.push(
<Indent size={2} type={IndentationTypes.SPACES} key={`sub-${channelName}`}>
<ListItem>
<Link href={`#sub-${FormatHelpers.slugify(channelName)}-operation`}>SUB {channelName}</Link>
</ListItem>
</Indent>
);
}
});
});
export function TableOfContents({asyncapi}) {
const servers = asyncapi.servers().all();
const operations = asyncapi.operations().all();

return (
<>
<Header type={2}>Table of Contents</Header>
<Text>
{serversList.length > 0 && (
{servers.length > 0 && (
<>
<ListItem>
<Link href="#servers">Servers</Link>
</ListItem>
{serversList}
{servers.map((server) => {
const serverName = server.id();
return (
<Indent
size={2}
type={IndentationTypes.SPACES}
key={serverName}
>
<ListItem>
<Link href={`#${FormatHelpers.slugify(
serverName)}-server`}>
{serverName}
</Link>
</ListItem>
</Indent>
);
})}
</>
)}
{operationsList.length > 0 && (
{operations.length > 0 && (
<>
<ListItem>
<Link href="#operations">Operations</Link>
</ListItem>
{operationsList}
{operations.map((operation) => {
const channel = operation.channels().all()[0];
const channelAddress = channel?.address() ?? '';
const type = CommonHelpers.getOperationType(operation, asyncapi);
return (
<Indent
size={2}
type={IndentationTypes.SPACES}
key={`${type}-${channelAddress}`}
>
<ListItem>
<Link
href={`#${type}-${FormatHelpers.slugify(
channelAddress)}-operation`}
>
{type.toUpperCase()} {channelAddress}
</Link>
</ListItem>
</Indent>
);
})}
</>
)}
</Text>
Expand Down
37 changes: 37 additions & 0 deletions helpers/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const OPERATION_TYPES = {
V3: {
REQUEST: 'request',
SEND: 'send',
REPLY: 'reply',
RECEIVE: 'receive',
},
V2: {
REQUEST: 'request',
SEND: 'publish',
REPLY: 'reply',
RECEIVE: 'subscribe',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v2 did not support request/reply pattern, so we really need to stick to publish/subscribe always, no request/reply

what I mean is that in case of v2 it is either publish or subscribe and I believe operation.reply() is anyway always undefined in v2

not sure if you are familiar in v2

so in v2 spec, we had publish/subscribe from perspective of the user of the API. So if AsyncAPI document for service A had publish operation - then it meant that end user can send a message, and service A in fact subscribes to a message. Same around, if service A had subscribe operation, it meant that user can subscribe so the service A was in fact publishing. Yeah, confusing, thus we changed it

in v3 we say what API does, so send means API sends and receive means API receives - as simple as that.

REQUEST: 'request' should be publish
REPLY: 'request' should be subscribe

any anyway, they will never be use as in v2, reply is always undefined

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for detailed comment! I appreciate it.
I'll fix types.

}
};

const getOperationTypesByVersion = (version) => {
const [majorVersion] = version.split('.');

return OPERATION_TYPES[`V${majorVersion}`];
};

export class CommonHelpers {
static getOperationType(operation, asyncApiDoc) {
const operationsTypes = getOperationTypesByVersion(asyncApiDoc.version());

if (operation.isSend()) {
if (operation.reply() !== undefined) {
return operationsTypes.REQUEST;
}
return operationsTypes.SEND;
}
if (operation.isReceive() && operation.reply() !== undefined) {
return operationsTypes.REPLY;
}
return operationsTypes.RECEIVE;
}
}
Loading
Loading