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

chore: add support for asyncapi 3.0.0 operations and channels #654

Merged
merged 17 commits into from
Oct 26, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"README.md"
],
"scripts": {
"dev": "tsc --watch",
magicmatatjahu marked this conversation as resolved.
Show resolved Hide resolved
"build": "npm run build:esm && npm run build:cjs && npm run build:browser",
"build:esm": "tsc",
"build:cjs": "tsc --project ./tsconfig.cjs.json",
Expand Down
3 changes: 2 additions & 1 deletion src/custom-operations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { Parser } from '../parser';
import type { ParseOptions } from '../parse';
import type { AsyncAPIDocumentInterface } from '../models';
import type { DetailedAsyncAPI } from '../types';
import { v2 } from 'spec-types';
fmvilas marked this conversation as resolved.
Show resolved Hide resolved

export async function customOperations(parser: Parser, document: AsyncAPIDocumentInterface, detailed: DetailedAsyncAPI, options: ParseOptions): Promise<void> {
switch (detailed.semver.major) {
Expand All @@ -20,7 +21,7 @@ async function operationsV2(parser: Parser, document: AsyncAPIDocumentInterface,
anonymousNaming(document);

if (options.applyTraits) {
applyTraitsV2(detailed.parsed);
applyTraitsV2(detailed.parsed as v2.AsyncAPIObject);
Copy link
Member Author

Choose a reason for hiding this comment

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

You'll find a bunch of these. Since now a DetailedAsyncAPI can be either v2 or v3, we need to make it explicit here.

}
if (options.parseSchemas) {
await parseSchemasV2(parser, detailed);
Expand Down
3 changes: 2 additions & 1 deletion src/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import {

import type { AsyncAPIDocumentInterface } from './models';
import type { DetailedAsyncAPI } from './types';
import { v2 } from 'spec-types';

export function createAsyncAPIDocument(asyncapi: DetailedAsyncAPI): AsyncAPIDocumentInterface {
switch (asyncapi.semver.major) {
case 2:
return new AsyncAPIDocumentV2(asyncapi.parsed, { asyncapi, pointer: '/' });
return new AsyncAPIDocumentV2(asyncapi.parsed as v2.AsyncAPIObject, { asyncapi, pointer: '/' });
// case 3:
// return new AsyncAPIDocumentV3(asyncapi.parsed, { asyncapi, pointer: '/' });
default:
Expand Down
4 changes: 2 additions & 2 deletions src/models/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { DetailedAsyncAPI } from '../types';

export interface ModelMetadata {
asyncapi: DetailedAsyncAPI;
pointer: string;
pointer?: string;
Copy link
Member Author

Choose a reason for hiding this comment

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

I found some cases in which pointer will be undefined and had to do this. Happy to hear about alternatives.

Copy link
Member

Choose a reason for hiding this comment

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

In which places? Could you point to them in this PR?

Copy link
Member Author

Choose a reason for hiding this comment

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

}

export abstract class BaseModel<J extends any = any, M extends Record<string, any> = {}> {
Expand All @@ -28,7 +28,7 @@ export abstract class BaseModel<J extends any = any, M extends Record<string, an
return this._meta[key];
}

jsonPath(field?: string | undefined): string {
jsonPath(field?: string | undefined): string | undefined {
if (typeof field !== 'string') {
return this._meta.pointer;
}
Expand Down
10 changes: 8 additions & 2 deletions src/models/channel-parameters.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { Collection } from './collection';
import { Collection } from './collection';
import type { ChannelParameterInterface } from './channel-parameter';

export type ChannelParametersInterface = Collection<ChannelParameterInterface>
export type ChannelParametersInterface = Collection<ChannelParameterInterface>;

export class ChannelParameters extends Collection<ChannelParameterInterface> implements ChannelParametersInterface {
override get(id: string): ChannelParameterInterface | undefined {
return this.collections.find(parameter => parameter.id() === id);
}
}
magicmatatjahu marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion src/models/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { ServersInterface } from './servers';

export interface ChannelInterface extends BaseModel, BindingsMixinInterface, DescriptionMixinInterface, ExtensionsMixinInterface {
id(): string;
address(): string;
address(): string | null | undefined;
servers(): ServersInterface;
operations(): OperationsInterface;
messages(): MessagesInterface;
Expand Down
18 changes: 16 additions & 2 deletions src/models/channels.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
import type { Collection } from './collection';
import { Collection } from './collection';
import type { ChannelInterface } from './channel';

export interface ChannelsInterface extends Collection<ChannelInterface> {
filterBySend(): ChannelInterface[];
filterByReceive(): ChannelInterface[];
}
}

export class Channels extends Collection<ChannelInterface> implements ChannelsInterface {
override get(id: string): ChannelInterface | undefined {
return this.collections.find(channel => channel.id() === id);
}

filterBySend(): ChannelInterface[] {
return this.filterBy(channel => channel.operations().filterBySend().length > 0);
}

filterByReceive(): ChannelInterface[] {
return this.filterBy(channel => channel.operations().filterByReceive().length > 0);
}
}
10 changes: 8 additions & 2 deletions src/models/correlation-ids.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { Collection } from './collection';
import { Collection } from './collection';
import type { CorrelationIdInterface } from './correlation-id';

export type CorrelationIdsInterface = Collection<CorrelationIdInterface>
export type CorrelationIdsInterface = Collection<CorrelationIdInterface>

export class CorrelationIds extends Collection<CorrelationIdInterface> implements CorrelationIdsInterface {
override get(id: string): CorrelationIdInterface | undefined {
return this.collections.find(correlationId => correlationId.meta('id' as any) === id);
}
}
11 changes: 9 additions & 2 deletions src/models/extensions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import type { Collection } from './collection';
import { Collection } from './collection';
import type { ExtensionInterface } from './extension';

export type ExtensionsInterface = Collection<ExtensionInterface>
export type ExtensionsInterface = Collection<ExtensionInterface>

export class Extensions extends Collection<ExtensionInterface> implements ExtensionsInterface {
override get<T = any>(id: string): ExtensionInterface<T> | undefined {
id = id.startsWith('x-') ? id : `x-${id}`;
return this.collections.find(ext => ext.id() === id);
}
}
8 changes: 7 additions & 1 deletion src/models/message-examples.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { Collection } from './collection';
import { Collection } from './collection';
import type { MessageExampleInterface } from './message-example';

export type MessageExamplesInterface = Collection<MessageExampleInterface>

export class MessageExamples extends Collection<MessageExampleInterface> implements MessageExamplesInterface {
override get(name: string): MessageExampleInterface | undefined {
return this.collections.find(example => example.name() === name);
}
}
10 changes: 8 additions & 2 deletions src/models/message-traits.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { Collection } from './collection';
import { Collection } from './collection';
import type { MessageTraitInterface } from './message-trait';

export type MessageTraitsInterface = Collection<MessageTraitInterface>
export type MessageTraitsInterface = Collection<MessageTraitInterface>

export class MessageTraits extends Collection<MessageTraitInterface> implements MessageTraitsInterface {
override get(id: string): MessageTraitInterface | undefined {
return this.collections.find(trait => trait.id() === id);
}
}
18 changes: 16 additions & 2 deletions src/models/messages.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
import type { Collection } from './collection';
import { Collection } from './collection';
import type { MessageInterface } from './message';

export interface MessagesInterface extends Collection<MessageInterface> {
filterBySend(): MessageInterface[];
filterByReceive(): MessageInterface[];
}
}

export class Messages extends Collection<MessageInterface> implements MessagesInterface {
override get(name: string): MessageInterface | undefined {
return this.collections.find(message => message.id() === name);
}

filterBySend(): MessageInterface[] {
return this.filterBy(message => message.operations().filterBySend().length > 0);
}

filterByReceive(): MessageInterface[] {
return this.filterBy(message => message.operations().filterByReceive().length > 0);
}
}
7 changes: 3 additions & 4 deletions src/models/operation-trait.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type { BaseModel } from './base';
import type { BindingsMixinInterface, DescriptionMixinInterface, ExtensionsMixinInterface, ExternalDocumentationMixinInterface, TagsMixinInterface } from './mixins';
import { SecurityRequirements } from './v2/security-requirements';
import { SecurityRequirements } from './security-requirements';

export interface OperationTraitInterface extends BaseModel, BindingsMixinInterface, DescriptionMixinInterface, ExtensionsMixinInterface, ExternalDocumentationMixinInterface, TagsMixinInterface {
id(): string;
hasOperationId(): boolean;
operationId(): string | undefined;
id(): string | undefined;
hasId(): boolean;
Comment on lines -6 to +9
Copy link
Member Author

Choose a reason for hiding this comment

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

I changed this because I think it's confusing. An operation shouldn't have an id and an operationId. The operationId is the id of the operation 😅 So yeah, changed it and changed tests too.

Copy link
Member

Choose a reason for hiding this comment

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

Makes sense 👍 . Would you mind adding such a change into asyncapi/parser-api#71 ?

Copy link
Member

Choose a reason for hiding this comment

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

id was treated as a key in the collection and not as operationId. The fact that in v3 operationId is mandatory (but not for operations in components - here keys are interpreted as a collection's key and not operationId), this remains a problem with v2 and how we filter collections -

return this.collections.find(operation => operation.id() === id);

Copy link
Member

Choose a reason for hiding this comment

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

We have to think how to handle it.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think I understand 😅 Just to clarify, in v3 operationId is not mandatory, it just doesn't exist. The id comes from the key in the operations object.

Copy link
Member

Choose a reason for hiding this comment

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

from v2 spec point of view it makes sense and that's the problem 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think it does, even from v2 point of view. operationId was there to mean the id of the operation. If it's present, it should be the operation identifier. If it's not, ok, we can generate one if you want, but it was never meant to be an "extra" id.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm 🤔 I have a problem in general because in the v3 version people will be able to define operations in components and how to distinguish this identifier in components.operations[x] from operationId - that's why we have these two methods.id() method is more of a method used to filter operations by identifier (used as key in the components.operations[x] and operations[x]), but... we'll see how to figure it out and id() should return operationId. Can we fix it in next PRs? We need also to fix that in parser-api and remove operationId() method.

To clarify: I wrote about components.operations[x] and operations[x], because in parser-api we have method allOperations() which should return all operations defined in given AsyncAPI document.

Copy link
Member

Choose a reason for hiding this comment

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

I realized no one updated the parser-api with any change regarding this.

Copy link
Member

Choose a reason for hiding this comment

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

All is fixed and there is no need to add anything else.

hasSummary(): boolean;
summary(): string | undefined;
security(): SecurityRequirements[];
Expand Down
10 changes: 8 additions & 2 deletions src/models/operation-traits.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { Collection } from './collection';
import { Collection } from './collection';
import type { OperationTraitInterface } from './operation-trait';

export type OperationTraitsInterface = Collection<OperationTraitInterface>
export type OperationTraitsInterface = Collection<OperationTraitInterface>

export class OperationTraits extends Collection<OperationTraitInterface> implements OperationTraitsInterface {
override get(id: string): OperationTraitInterface | undefined {
return this.collections.find(trait => trait.id() === id);
}
}
17 changes: 16 additions & 1 deletion src/models/operations.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import type { Collection } from './collection';
import { Collection } from './collection';

import type { OperationInterface } from './operation';

export interface OperationsInterface extends Collection<OperationInterface> {
filterBySend(): OperationInterface[];
filterByReceive(): OperationInterface[];
}

export class Operations extends Collection<OperationInterface> implements OperationsInterface {
override get(id: string): OperationInterface | undefined {
return this.collections.find(operation => operation.id() === id);
}

filterBySend(): OperationInterface[] {
return this.filterBy(operation => operation.isSend());
}

filterByReceive(): OperationInterface[] {
return this.filterBy(operation => operation.isReceive());
}
}
8 changes: 7 additions & 1 deletion src/models/schemas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { Collection } from './collection';
import { Collection } from './collection';
import type { SchemaInterface } from './schema';

export type SchemasInterface = Collection<SchemaInterface>

export class Schemas extends Collection<SchemaInterface> implements SchemasInterface {
override get(id: string): SchemaInterface | undefined {
return this.collections.find(schema => schema.uid() === id);
}
}
10 changes: 8 additions & 2 deletions src/models/security-requirements.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { Collection} from './collection';
import { Collection} from './collection';
import type { SecurityRequirementInterface } from './security-requirement';

export type SecurityRequirementsInterface = Collection<SecurityRequirementInterface>
export type SecurityRequirementsInterface = Collection<SecurityRequirementInterface>

export class SecurityRequirements extends Collection<SecurityRequirementInterface> implements SecurityRequirementsInterface {
override get(id: string): SecurityRequirementInterface | undefined {
return this.collections.find(securityRequirement => securityRequirement.meta('id' as any) === id);
}
}
8 changes: 7 additions & 1 deletion src/models/security-schemes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { Collection} from './collection';
import { Collection} from './collection';
import type { SecuritySchemeInterface } from './security-scheme';

export type SecuritySchemesInterface = Collection<SecuritySchemeInterface>

export class SecuritySchemes extends Collection<SecuritySchemeInterface> implements SecuritySchemesInterface {
override get(id: string): SecuritySchemeInterface | undefined {
return this.collections.find(securityScheme => securityScheme.id() === id);
}
}
10 changes: 8 additions & 2 deletions src/models/server-variables.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { Collection } from './collection';
import { Collection } from './collection';
import type { ServerVariableInterface } from './server-variable';

export type ServerVariablesInterface = Collection<ServerVariableInterface>
export type ServerVariablesInterface = Collection<ServerVariableInterface>;

export class ServerVariables extends Collection<ServerVariableInterface> implements ServerVariablesInterface {
override get(id: string): ServerVariableInterface | undefined {
return this.collections.find(variable => variable.id() === id);
}
}
16 changes: 15 additions & 1 deletion src/models/servers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
import type { Collection } from './collection';
import { Collection } from './collection';
import type { ServerInterface } from './server';

export interface ServersInterface extends Collection<ServerInterface> {
filterBySend(): ServerInterface[];
filterByReceive(): ServerInterface[];
}

export class Servers extends Collection<ServerInterface> implements ServersInterface {
override get(id: string): ServerInterface | undefined {
return this.collections.find(server => server.id() === id);
}

filterBySend(): ServerInterface[] {
return this.filterBy(server => server.operations().filterBySend().length > 0);
}

filterByReceive(): ServerInterface[] {
return this.filterBy(server => server.operations().filterByReceive().length > 0);
}
}
10 changes: 8 additions & 2 deletions src/models/tags.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { Collection } from './collection';
import { Collection } from './collection';
import type { TagInterface } from './tag';

export type TagsInterface = Collection<TagInterface>
export type TagsInterface = Collection<TagInterface>

export class Tags extends Collection<TagInterface> implements TagsInterface {
override get(name: string): TagInterface | undefined {
return this.collections.find(tag => tag.name() === name);
}
}
12 changes: 6 additions & 6 deletions src/models/v2/asyncapi.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { BaseModel } from '../base';
import { Info } from './info';
import { Channels } from './channels';
import { Channels } from '../channels';
import { Channel } from './channel';
import { Components } from './components';
import { Messages } from './messages';
import { Operations } from './operations';
import { Servers } from './servers';
import { Messages } from '../messages';
import { Operations } from '../operations';
import { Servers } from '../servers';
import { Server } from './server';
import { SecuritySchemes } from './security-schemes';
import { SecuritySchemes } from '../security-schemes';
import { SecurityScheme } from './security-scheme';
import { Schemas } from './schemas';
import { Schemas } from '../schemas';

import { extensions } from './mixins';

Expand Down
2 changes: 1 addition & 1 deletion src/models/v2/bindings.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Collection } from '../collection';
import { Extensions } from './extensions';
import { Extensions } from '../extensions';
import { Extension } from './extension';

import { createModel } from '../utils';
Expand Down
10 changes: 0 additions & 10 deletions src/models/v2/channel-parameters.ts

This file was deleted.

8 changes: 4 additions & 4 deletions src/models/v2/channel.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { BaseModel } from '../base';
import { ChannelParameters } from './channel-parameters';
import { ChannelParameters } from '../channel-parameters';
import { ChannelParameter } from './channel-parameter';
import { Messages } from './messages';
import { Operations } from './operations';
import { Messages } from '../messages';
import { Operations } from '../operations';
import { Operation } from './operation';
import { Servers } from './servers';
import { Servers } from '../servers';
import { Server } from './server';

import { bindings, hasDescription, description, extensions } from './mixins';
Expand Down
Loading