@achinet/nestjs-async 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/.eslintrc.js +9 -0
  2. package/.prettierrc +5 -0
  3. package/CHANGELOG.md +7 -0
  4. package/README.md +1 -0
  5. package/dist/asyncapi-document.builder.d.ts +27 -0
  6. package/dist/asyncapi-document.builder.js +110 -0
  7. package/dist/asyncapi-document.builder.js.map +1 -0
  8. package/dist/asyncapi.constants.d.ts +6 -0
  9. package/dist/asyncapi.constants.js +11 -0
  10. package/dist/asyncapi.constants.js.map +1 -0
  11. package/dist/asyncapi.module.d.ts +8 -0
  12. package/dist/asyncapi.module.js +44 -0
  13. package/dist/asyncapi.module.js.map +1 -0
  14. package/dist/binding/asyncapi.amqp.interfaces.d.ts +38 -0
  15. package/dist/binding/asyncapi.amqp.interfaces.js +3 -0
  16. package/dist/binding/asyncapi.amqp.interfaces.js.map +1 -0
  17. package/dist/binding/asyncapi.kafka.interfaces.d.ts +21 -0
  18. package/dist/binding/asyncapi.kafka.interfaces.js +3 -0
  19. package/dist/binding/asyncapi.kafka.interfaces.js.map +1 -0
  20. package/dist/binding/index.d.ts +2 -0
  21. package/dist/binding/index.js +19 -0
  22. package/dist/binding/index.js.map +1 -0
  23. package/dist/decorators/asyncapi-operation-for-meta-key.decorator.d.ts +2 -0
  24. package/dist/decorators/asyncapi-operation-for-meta-key.decorator.js +38 -0
  25. package/dist/decorators/asyncapi-operation-for-meta-key.decorator.js.map +1 -0
  26. package/dist/decorators/asyncapi-operation.decorator.d.ts +2 -0
  27. package/dist/decorators/asyncapi-operation.decorator.js +10 -0
  28. package/dist/decorators/asyncapi-operation.decorator.js.map +1 -0
  29. package/dist/decorators/asyncapi-pub.decorator.d.ts +2 -0
  30. package/dist/decorators/asyncapi-pub.decorator.js +11 -0
  31. package/dist/decorators/asyncapi-pub.decorator.js.map +1 -0
  32. package/dist/decorators/asyncapi-service.decorator.d.ts +1 -0
  33. package/dist/decorators/asyncapi-service.decorator.js +10 -0
  34. package/dist/decorators/asyncapi-service.decorator.js.map +1 -0
  35. package/dist/decorators/asyncapi-sub.decorator.d.ts +2 -0
  36. package/dist/decorators/asyncapi-sub.decorator.js +11 -0
  37. package/dist/decorators/asyncapi-sub.decorator.js.map +1 -0
  38. package/dist/decorators/index.d.ts +4 -0
  39. package/dist/decorators/index.js +21 -0
  40. package/dist/decorators/index.js.map +1 -0
  41. package/dist/explorers/asyncapi-class.explorer.d.ts +5 -0
  42. package/dist/explorers/asyncapi-class.explorer.js +31 -0
  43. package/dist/explorers/asyncapi-class.explorer.js.map +1 -0
  44. package/dist/explorers/asyncapi-operation.explorer.d.ts +17 -0
  45. package/dist/explorers/asyncapi-operation.explorer.js +26 -0
  46. package/dist/explorers/asyncapi-operation.explorer.js.map +1 -0
  47. package/dist/explorers/index.d.ts +2 -0
  48. package/dist/explorers/index.js +19 -0
  49. package/dist/explorers/index.js.map +1 -0
  50. package/dist/index.d.ts +4 -0
  51. package/dist/index.js +21 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/interface/asyncapi-common.interfaces.d.ts +126 -0
  54. package/dist/interface/asyncapi-common.interfaces.js +3 -0
  55. package/dist/interface/asyncapi-common.interfaces.js.map +1 -0
  56. package/dist/interface/asyncapi-document-options.interface.d.ts +3 -0
  57. package/dist/interface/asyncapi-document-options.interface.js +3 -0
  58. package/dist/interface/asyncapi-document-options.interface.js.map +1 -0
  59. package/dist/interface/asyncapi-message.interface.d.ts +8 -0
  60. package/dist/interface/asyncapi-message.interface.js +3 -0
  61. package/dist/interface/asyncapi-message.interface.js.map +1 -0
  62. package/dist/interface/asyncapi-operation-headers.interface.d.ts +6 -0
  63. package/dist/interface/asyncapi-operation-headers.interface.js +3 -0
  64. package/dist/interface/asyncapi-operation-headers.interface.js.map +1 -0
  65. package/dist/interface/asyncapi-operation-options-raw.interface.d.ts +21 -0
  66. package/dist/interface/asyncapi-operation-options-raw.interface.js +3 -0
  67. package/dist/interface/asyncapi-operation-options-raw.interface.js.map +1 -0
  68. package/dist/interface/asyncapi-operation-options.interface.d.ts +8 -0
  69. package/dist/interface/asyncapi-operation-options.interface.js +3 -0
  70. package/dist/interface/asyncapi-operation-options.interface.js.map +1 -0
  71. package/dist/interface/asyncapi-operation-payload.interface.d.ts +2 -0
  72. package/dist/interface/asyncapi-operation-payload.interface.js +3 -0
  73. package/dist/interface/asyncapi-operation-payload.interface.js.map +1 -0
  74. package/dist/interface/asyncapi-server.interface.d.ts +10 -0
  75. package/dist/interface/asyncapi-server.interface.js +3 -0
  76. package/dist/interface/asyncapi-server.interface.js.map +1 -0
  77. package/dist/interface/asyncapi-template-options.interface.d.ts +7 -0
  78. package/dist/interface/asyncapi-template-options.interface.js +3 -0
  79. package/dist/interface/asyncapi-template-options.interface.js.map +1 -0
  80. package/dist/interface/denormalized-doc-resolvers.interface.d.ts +6 -0
  81. package/dist/interface/denormalized-doc-resolvers.interface.js +3 -0
  82. package/dist/interface/denormalized-doc-resolvers.interface.js.map +1 -0
  83. package/dist/interface/denormalized-doc.interface.d.ts +10 -0
  84. package/dist/interface/denormalized-doc.interface.js +3 -0
  85. package/dist/interface/denormalized-doc.interface.js.map +1 -0
  86. package/dist/interface/generator-options.interface.d.ts +22 -0
  87. package/dist/interface/generator-options.interface.js +3 -0
  88. package/dist/interface/generator-options.interface.js.map +1 -0
  89. package/dist/interface/index.d.ts +11 -0
  90. package/dist/interface/index.js +28 -0
  91. package/dist/interface/index.js.map +1 -0
  92. package/dist/services/asyncapi.explorer.d.ts +12 -0
  93. package/dist/services/asyncapi.explorer.js +72 -0
  94. package/dist/services/asyncapi.explorer.js.map +1 -0
  95. package/dist/services/asyncapi.generator.d.ts +7 -0
  96. package/dist/services/asyncapi.generator.js +27 -0
  97. package/dist/services/asyncapi.generator.js.map +1 -0
  98. package/dist/services/asyncapi.scanner.d.ts +14 -0
  99. package/dist/services/asyncapi.scanner.js +82 -0
  100. package/dist/services/asyncapi.scanner.js.map +1 -0
  101. package/dist/services/asyncapi.transformer.d.ts +4 -0
  102. package/dist/services/asyncapi.transformer.js +33 -0
  103. package/dist/services/asyncapi.transformer.js.map +1 -0
  104. package/dist/services/index.d.ts +5 -0
  105. package/dist/services/index.js +22 -0
  106. package/dist/services/index.js.map +1 -0
  107. package/dist/services/operation-object.factory.d.ts +9 -0
  108. package/dist/services/operation-object.factory.js +37 -0
  109. package/dist/services/operation-object.factory.js.map +1 -0
  110. package/dist/tsconfig.tsbuildinfo +1 -0
  111. package/package.json +94 -0
  112. package/src/asyncapi-document.builder.ts +186 -0
  113. package/src/asyncapi.constants.ts +8 -0
  114. package/src/asyncapi.module.ts +76 -0
  115. package/src/binding/asyncapi.amqp.interfaces.ts +48 -0
  116. package/src/binding/asyncapi.kafka.interfaces.ts +35 -0
  117. package/src/binding/index.ts +2 -0
  118. package/src/decorators/asyncapi-operation-for-meta-key.decorator.ts +70 -0
  119. package/src/decorators/asyncapi-operation.decorator.ts +9 -0
  120. package/src/decorators/asyncapi-pub.decorator.ts +18 -0
  121. package/src/decorators/asyncapi-service.decorator.ts +9 -0
  122. package/src/decorators/asyncapi-sub.decorator.ts +18 -0
  123. package/src/decorators/index.ts +4 -0
  124. package/src/explorers/asyncapi-class.explorer.ts +30 -0
  125. package/src/explorers/asyncapi-operation.explorer.ts +49 -0
  126. package/src/explorers/index.ts +2 -0
  127. package/src/index.ts +4 -0
  128. package/src/interface/asyncapi-common.interfaces.ts +172 -0
  129. package/src/interface/asyncapi-document-options.interface.ts +3 -0
  130. package/src/interface/asyncapi-message.interface.ts +10 -0
  131. package/src/interface/asyncapi-operation-headers.interface.ts +6 -0
  132. package/src/interface/asyncapi-operation-options-raw.interface.ts +23 -0
  133. package/src/interface/asyncapi-operation-options.interface.ts +12 -0
  134. package/src/interface/asyncapi-operation-payload.interface.ts +7 -0
  135. package/src/interface/asyncapi-server.interface.ts +14 -0
  136. package/src/interface/asyncapi-template-options.interface.ts +20 -0
  137. package/src/interface/denormalized-doc-resolvers.interface.ts +6 -0
  138. package/src/interface/denormalized-doc.interface.ts +10 -0
  139. package/src/interface/generator-options.interface.ts +21 -0
  140. package/src/interface/index.ts +12 -0
  141. package/src/services/asyncapi.explorer.ts +121 -0
  142. package/src/services/asyncapi.generator.ts +33 -0
  143. package/src/services/asyncapi.scanner.ts +155 -0
  144. package/src/services/asyncapi.transformer.ts +39 -0
  145. package/src/services/index.ts +5 -0
  146. package/src/services/operation-object.factory.ts +56 -0
  147. package/tsconfig.build.json +4 -0
  148. package/tsconfig.json +8 -0
@@ -0,0 +1,172 @@
1
+ import {
2
+ InfoObject,
3
+ ReferenceObject,
4
+ SchemaObject,
5
+ ServerVariableObject,
6
+ } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';
7
+ import {
8
+ AmqpChannelBinding,
9
+ AmqpMessageBinding,
10
+ AmqpOperationBinding,
11
+ AmqpServerBinding,
12
+ KafkaChannelBinding,
13
+ KafkaMessageBinding,
14
+ KafkaOperationBinding,
15
+ KafkaServerBinding,
16
+ } from '../binding';
17
+ import { AsyncOperationPayload } from './asyncapi-operation-payload.interface';
18
+ import { AsyncServerObject } from './asyncapi-server.interface';
19
+
20
+ export interface AsyncApiDocument {
21
+ asyncapi: string;
22
+ id?: string;
23
+ info: InfoObject;
24
+ servers?: Record<string, AsyncServerObject>;
25
+ channels: AsyncChannelsObject;
26
+ components?: AsyncComponentsObject;
27
+ tags?: AsyncTagObject[];
28
+ externalDocs?: ExternalDocumentationObject;
29
+ defaultContentType?: string;
30
+ }
31
+
32
+ export type AsyncChannelsObject = Record<string, AsyncChannelObject>;
33
+ export interface AsyncChannelObject {
34
+ description?: string;
35
+ subscribe?: AsyncOperationObject;
36
+ publish?: AsyncOperationObject;
37
+ parameters?: Record<string, ParameterObject>;
38
+ bindings?: Record<string, KafkaChannelBinding | AmqpChannelBinding>;
39
+ }
40
+
41
+ export interface AsyncServerVariableObject extends ServerVariableObject {
42
+ examples?: string[];
43
+ }
44
+
45
+ export type SecurityObject = Record<string, string[]>;
46
+
47
+ export interface AsyncComponentsObject {
48
+ schemas?: Record<string, SchemaObject>;
49
+ messages?: Record<string, AsyncMessageObject>;
50
+ securitySchemes?: Record<string, AsyncSecuritySchemeObject>;
51
+ parameters?: Record<string, ParameterObject>;
52
+ correlationIds?: Record<string, AsyncCorrelationObject>;
53
+ operationTraits?: Record<string, AsyncOperationTraitObject>;
54
+ messageTraits?: Record<string, AsyncMessageTraitObject>;
55
+ serverBindings?: Record<string, KafkaServerBinding | AmqpServerBinding>;
56
+ channelBindings?: Record<string, KafkaChannelBinding | AmqpChannelBinding>;
57
+ operationBindings?: Record<
58
+ string,
59
+ KafkaOperationBinding | AmqpOperationBinding
60
+ >;
61
+ messageBindings?: Record<string, KafkaMessageBinding | AmqpMessageBinding>;
62
+ }
63
+
64
+ export interface AsyncMessageObject extends AsyncMessageTraitObject {
65
+ payload?: {
66
+ type?: AsyncOperationPayload;
67
+ $ref?: AsyncOperationPayload;
68
+ };
69
+ }
70
+
71
+ export type MessageType = AsyncMessageObject | ReferenceObject;
72
+ export interface OneOfMessageType {
73
+ oneOf: MessageType[];
74
+ }
75
+
76
+ export type AsyncOperationMessage = OneOfMessageType | MessageType;
77
+
78
+ export interface AsyncOperationObject {
79
+ channel: string;
80
+ operationId?: string;
81
+ summary?: string;
82
+ description?: string;
83
+ tags?: AsyncTagObject[];
84
+ externalDocs?: ExternalDocumentationObject;
85
+ bindings?: Record<string, KafkaOperationBinding | AmqpOperationBinding>;
86
+ traits?: Record<string, AsyncOperationTraitObject>;
87
+ message?: AsyncOperationMessage;
88
+ }
89
+
90
+ export interface AsyncOperationTraitObject {
91
+ operationId?: string;
92
+ summary?: string;
93
+ description?: string;
94
+ tags?: AsyncTagObject[];
95
+ externalDocs?: ExternalDocumentationObject;
96
+ bindings?: Record<string, KafkaOperationBinding | AmqpOperationBinding>;
97
+ }
98
+
99
+ export interface AsyncMessageTraitObject {
100
+ headers?: SchemaObject;
101
+ correlationId?: AsyncCorrelationObject;
102
+ schemaFormat?: string;
103
+ contentType?: string;
104
+ name?: string;
105
+ title?: string;
106
+ summary?: string;
107
+ description?: string;
108
+ tags?: AsyncTagObject[];
109
+ externalDocs?: ExternalDocumentationObject;
110
+ bindings?: Record<string, KafkaMessageBinding | AmqpMessageBinding>;
111
+ }
112
+
113
+ export interface AsyncCorrelationObject {
114
+ description?: string;
115
+ location: string;
116
+ }
117
+
118
+ export interface AsyncTagObject {
119
+ name: string;
120
+ description?: string;
121
+ externalDocs?: ExternalDocumentationObject;
122
+ }
123
+
124
+ export interface AsyncSecuritySchemeObject {
125
+ type: SecuritySchemeType;
126
+ description?: string;
127
+ name?: string;
128
+ in?: string;
129
+ scheme?: string;
130
+ bearerFormat?: string;
131
+ flows?: OAuthFlowsObject;
132
+ openIdConnectUrl?: string;
133
+ }
134
+
135
+ export declare type SecuritySchemeType =
136
+ | 'userPassword'
137
+ | 'apiKey'
138
+ | 'X509'
139
+ | 'symmetricEncryption'
140
+ | 'asymmetricEncryption'
141
+ | 'http'
142
+ | 'oauth2'
143
+ | 'openIdConnect';
144
+
145
+ export interface OAuthFlowsObject {
146
+ implicit?: OAuthFlowObject;
147
+ password?: OAuthFlowObject;
148
+ clientCredentials?: OAuthFlowObject;
149
+ authorizationCode?: OAuthFlowObject;
150
+ }
151
+
152
+ export interface OAuthFlowObject {
153
+ authorizationUrl?: string;
154
+ tokenUrl?: string;
155
+ refreshUrl?: string;
156
+ scopes: ScopesObject;
157
+ }
158
+
159
+ export type ScopesObject = Record<string, unknown>;
160
+
161
+ export type ParameterObject = BaseParameterObject;
162
+
163
+ export interface BaseParameterObject {
164
+ description?: string;
165
+ schema?: SchemaObject | ReferenceObject;
166
+ location?: string;
167
+ }
168
+
169
+ export interface ExternalDocumentationObject {
170
+ description?: string;
171
+ url: string;
172
+ }
@@ -0,0 +1,3 @@
1
+ import { SwaggerDocumentOptions } from '@nestjs/swagger';
2
+
3
+ export interface AsyncApiDocumentOptions extends SwaggerDocumentOptions {}
@@ -0,0 +1,10 @@
1
+ import { AsyncApiOperationHeaders } from './asyncapi-operation-headers.interface';
2
+ import { AsyncOperationPayload } from './asyncapi-operation-payload.interface';
3
+
4
+ export interface OneAsyncApiMessage {
5
+ name?: string;
6
+ payload: AsyncOperationPayload;
7
+ headers?: AsyncApiOperationHeaders;
8
+ }
9
+
10
+ export type AsyncApiMessage = OneAsyncApiMessage | OneAsyncApiMessage[];
@@ -0,0 +1,6 @@
1
+ export interface AsyncApiOperationHeaders {
2
+ [key: string]: {
3
+ description: string;
4
+ [key: string]: unknown;
5
+ };
6
+ }
@@ -0,0 +1,23 @@
1
+ import { AsyncOperationObject } from './asyncapi-common.interfaces';
2
+ import { AsyncOperationPayload } from './asyncapi-operation-payload.interface';
3
+
4
+ export interface RawAsyncApiMessage {
5
+ name?: string;
6
+ payload: {
7
+ type: AsyncOperationPayload;
8
+ };
9
+ headers?: {
10
+ type: 'object';
11
+ properties: {
12
+ [key: string]: {
13
+ description: string;
14
+ type: 'string';
15
+ [key: string]: unknown;
16
+ };
17
+ };
18
+ };
19
+ }
20
+
21
+ export interface AsyncApiOperationOptionsRaw extends AsyncOperationObject {
22
+ type: 'sub' | 'pub';
23
+ }
@@ -0,0 +1,12 @@
1
+ import { AsyncOperationObject } from './asyncapi-common.interfaces';
2
+ import { AsyncApiMessage } from './asyncapi-message.interface';
3
+
4
+ export interface AsyncApiSpecificOperationOptions
5
+ extends Omit<AsyncOperationObject, 'message'> {
6
+ message: AsyncApiMessage;
7
+ }
8
+
9
+ export interface AsyncApiOperationOptions
10
+ extends AsyncApiSpecificOperationOptions {
11
+ type: 'pub' | 'sub';
12
+ }
@@ -0,0 +1,7 @@
1
+ import { Type } from '@nestjs/common';
2
+
3
+ export type AsyncOperationPayload =
4
+ | Type<unknown>
5
+ | Function
6
+ | [Function]
7
+ | string;
@@ -0,0 +1,14 @@
1
+ import { ServerObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';
2
+ import { AmqpServerBinding, KafkaServerBinding } from '../binding';
3
+ import {
4
+ AsyncServerVariableObject,
5
+ SecurityObject,
6
+ } from './asyncapi-common.interfaces';
7
+
8
+ export interface AsyncServerObject extends Omit<ServerObject, 'variables'> {
9
+ variables?: Record<string, AsyncServerVariableObject>;
10
+ protocol: string;
11
+ protocolVersion?: string;
12
+ security?: SecurityObject[];
13
+ bindings?: Record<string, KafkaServerBinding | AmqpServerBinding>;
14
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @see https://github.com/asyncapi/html-template#supported-parameters
3
+ **/
4
+ export interface AsyncApiTemplateOptions {
5
+ /** @default byTagsNoRoot **/
6
+ sidebarOrganization?: 'byTags' | 'byTagsNoRoot';
7
+ /**
8
+ * @example /docs
9
+ **/
10
+ baseHref?: string;
11
+ /** @default true **/
12
+ singleFile?: boolean;
13
+ /** @example asyncapi.html **/
14
+ outFilename?: string;
15
+ /**
16
+ * @description Generates output HTML as PDF
17
+ * @default false
18
+ */
19
+ pdf?: boolean;
20
+ }
@@ -0,0 +1,6 @@
1
+ export interface DenormalizedDocResolvers {
2
+ root: Function[];
3
+ security: Function[];
4
+ tags: Function[];
5
+ operations: Function[];
6
+ }
@@ -0,0 +1,10 @@
1
+ import {
2
+ AsyncApiDocument,
3
+ AsyncChannelObject,
4
+ AsyncOperationObject,
5
+ } from './asyncapi-common.interfaces';
6
+
7
+ export interface DenormalizedDoc extends Partial<AsyncApiDocument> {
8
+ root?: { name: string } & AsyncChannelObject;
9
+ operations?: { pub: AsyncOperationObject; sub: AsyncOperationObject };
10
+ }
@@ -0,0 +1,21 @@
1
+ import { AsyncApiDocument } from './asyncapi-common.interfaces';
2
+ import { AsyncApiTemplateOptions } from './asyncapi-template-options.interface';
3
+
4
+ export interface GeneratorOptions {
5
+ templateName: string;
6
+ targetDir: string;
7
+ entrypoint?: string;
8
+ noOverwriteGlobs: string[];
9
+ disabledHooks: { [key: string]: string | boolean | string[] };
10
+ output: 'string' | 'fs';
11
+ forceWrite: boolean;
12
+ debug: boolean;
13
+ install: boolean;
14
+ templateConfig: Record<string, unknown>;
15
+ hooks: Record<string, unknown>;
16
+ templateParams: AsyncApiTemplateOptions;
17
+ generate: (document: AsyncApiDocument) => Promise<void>;
18
+ generateFromURL: (url: string) => Promise<void>;
19
+ generateFromFile: (path: string) => Promise<void>;
20
+ generateFromString: (yaml: string, args?: unknown) => Promise<string>;
21
+ }
@@ -0,0 +1,12 @@
1
+ export * from './asyncapi-common.interfaces';
2
+ export * from './asyncapi-template-options.interface';
3
+ export * from './asyncapi-operation-headers.interface';
4
+ export * from './asyncapi-operation-payload.interface';
5
+ export * from './asyncapi-operation-options.interface';
6
+ export * from './asyncapi-document-options.interface';
7
+ export * from './asyncapi-server.interface';
8
+ export * from './denormalized-doc.interface';
9
+ export * from './denormalized-doc-resolvers.interface';
10
+ export * from './generator-options.interface';
11
+
12
+ export * from './asyncapi-operation-options-raw.interface';
@@ -0,0 +1,121 @@
1
+ import { Type } from '@nestjs/common';
2
+ import { MetadataScanner } from '@nestjs/core';
3
+ import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
4
+ import { SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';
5
+ import { flatten } from 'lodash';
6
+ import {
7
+ asyncApiClassAnnotationLabels,
8
+ exploreAsyncapiClassMetadata,
9
+ exploreAsyncApiOperationMetadata,
10
+ exploreControllerMetadata,
11
+ exploreGatewayMetadata,
12
+ } from '../explorers';
13
+ import { DenormalizedDoc, DenormalizedDocResolvers } from '../interface';
14
+
15
+ export class AsyncApiExplorer {
16
+ private readonly metadataScanner = new MetadataScanner();
17
+ private readonly schemas: SchemaObject[] = [];
18
+ private readonly schemaRefsStack: string[] = [];
19
+
20
+ private operationIdFactory = (controllerKey: string, methodKey: string) =>
21
+ controllerKey ? `${controllerKey}_${methodKey}` : methodKey;
22
+
23
+ public explorerAsyncapiServices(
24
+ wrapper: InstanceWrapper,
25
+ modulePath?: string,
26
+ globalPrefix?: string,
27
+ operationIdFactory?: (controllerKey: string, methodKey: string) => string,
28
+ ) {
29
+ if (operationIdFactory) {
30
+ this.operationIdFactory = operationIdFactory;
31
+ }
32
+
33
+ const { instance, metatype } = wrapper;
34
+ if (
35
+ !instance ||
36
+ !metatype ||
37
+ !Reflect.getMetadataKeys(metatype).find((label) =>
38
+ asyncApiClassAnnotationLabels.includes(label),
39
+ )
40
+ ) {
41
+ return [];
42
+ }
43
+
44
+ const prototype = Object.getPrototypeOf(instance);
45
+ const documentResolvers: DenormalizedDocResolvers = {
46
+ root: [
47
+ exploreAsyncapiClassMetadata,
48
+ exploreControllerMetadata,
49
+ exploreGatewayMetadata,
50
+ ],
51
+ security: [],
52
+ tags: [],
53
+ operations: [exploreAsyncApiOperationMetadata],
54
+ };
55
+
56
+ return this.generateDenormalizedDocument(
57
+ metatype as Type<unknown>,
58
+ prototype,
59
+ instance,
60
+ documentResolvers,
61
+ modulePath,
62
+ globalPrefix,
63
+ );
64
+ }
65
+
66
+ public getSchemas(): Record<string, SchemaObject> {
67
+ const ret = { ...this.schemas } as unknown as Record<string, SchemaObject>;
68
+ return ret;
69
+ }
70
+
71
+ private generateDenormalizedDocument(
72
+ metatype: Type<unknown>,
73
+ prototype: Type<unknown>,
74
+ instance: object,
75
+ documentResolvers: DenormalizedDocResolvers,
76
+ _modulePath?: string,
77
+ _globalPrefix?: string,
78
+ ): DenormalizedDoc[] {
79
+ const denormalizedAsyncapiServices = this.metadataScanner.scanFromPrototype<
80
+ unknown,
81
+ DenormalizedDoc[]
82
+ >(instance, prototype, (name) => {
83
+ const targetCallback = prototype[name];
84
+ const methodMetadata = documentResolvers.root.reduce((_metadata, fn) => {
85
+ const serviceMetadata = fn(metatype);
86
+
87
+ const channels = documentResolvers.operations.reduce(
88
+ (operations, exploreOperationsMeta) => {
89
+ const meta = exploreOperationsMeta(
90
+ this.schemas,
91
+ instance,
92
+ prototype,
93
+ targetCallback,
94
+ );
95
+ if (!meta) {
96
+ return operations;
97
+ }
98
+
99
+ meta.forEach((op) => {
100
+ if (operations.hasOwnProperty(op.channel)) {
101
+ operations[op.channel] = { ...operations[op.channel], ...op };
102
+ } else {
103
+ operations[op.channel] = op;
104
+ }
105
+ });
106
+ return operations;
107
+ },
108
+ {},
109
+ );
110
+
111
+ return Object.keys(channels).map((channel) => ({
112
+ root: { ...serviceMetadata, name: channel },
113
+ operations: channels[channel],
114
+ }));
115
+ }, []);
116
+ return methodMetadata;
117
+ });
118
+
119
+ return flatten(denormalizedAsyncapiServices);
120
+ }
121
+ }
@@ -0,0 +1,33 @@
1
+ import Generator from '@asyncapi/generator';
2
+ import jsyaml from 'js-yaml';
3
+ import os from 'os';
4
+ import {
5
+ AsyncApiDocument,
6
+ AsyncApiTemplateOptions,
7
+ GeneratorOptions,
8
+ } from '../interface';
9
+
10
+ export class AsyncapiGenerator {
11
+ private readonly generator: GeneratorOptions;
12
+
13
+ constructor(readonly templateOptions?: AsyncApiTemplateOptions) {
14
+ this.generator = new Generator('@asyncapi/html-template', os.tmpdir(), {
15
+ forceWrite: true,
16
+ entrypoint: 'index.html',
17
+ output: 'string',
18
+ templateParams: {
19
+ singleFile: true,
20
+ ...templateOptions,
21
+ },
22
+ });
23
+ }
24
+
25
+ public async generate(contract: AsyncApiDocument): Promise<string> {
26
+ const yaml = jsyaml.dump(contract);
27
+ return this.generator.generateFromString(yaml, {
28
+ resolve: {
29
+ file: false,
30
+ },
31
+ });
32
+ }
33
+ }
@@ -0,0 +1,155 @@
1
+ import { INestApplicationContext, Type } from '@nestjs/common';
2
+ import { MODULE_PATH } from '@nestjs/common/constants';
3
+ import { Injectable, InjectionToken } from '@nestjs/common/interfaces';
4
+ import { NestContainer } from '@nestjs/core/injector/container';
5
+ import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
6
+ import { Module } from '@nestjs/core/injector/module';
7
+ import { SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';
8
+ import { ModelPropertiesAccessor } from '@nestjs/swagger/dist/services/model-properties-accessor';
9
+ import { SchemaObjectFactory } from '@nestjs/swagger/dist/services/schema-object-factory';
10
+ import { SwaggerTypesMapper } from '@nestjs/swagger/dist/services/swagger-types-mapper';
11
+ import { stripLastSlash } from '@nestjs/swagger/dist/utils/strip-last-slash.util';
12
+ import { flatten, isEmpty } from 'lodash';
13
+ import {
14
+ AsyncApiDocument,
15
+ AsyncApiDocumentOptions,
16
+ DenormalizedDoc,
17
+ } from '../interface';
18
+ import { AsyncApiExplorer } from './asyncapi.explorer';
19
+ import { AsyncapiTransformer } from './asyncapi.transformer';
20
+
21
+ export class AsyncapiScanner {
22
+ private readonly transformer = new AsyncapiTransformer();
23
+ private readonly explorer = new AsyncApiExplorer();
24
+ private readonly modelPropertiesAccessor = new ModelPropertiesAccessor();
25
+ private readonly swaggerTypesMapper = new SwaggerTypesMapper();
26
+ private readonly schemaObjectFactory = new SchemaObjectFactory(
27
+ this.modelPropertiesAccessor,
28
+ this.swaggerTypesMapper,
29
+ );
30
+
31
+ public scanApplication(
32
+ app: INestApplicationContext,
33
+ options: AsyncApiDocumentOptions,
34
+ ): Omit<AsyncApiDocument, 'asyncapi' | 'info'> {
35
+ const {
36
+ deepScanRoutes,
37
+ include: includedModules = [],
38
+ extraModels = [],
39
+ ignoreGlobalPrefix = false,
40
+ operationIdFactory,
41
+ } = options;
42
+
43
+ const container: NestContainer = (app as any).container;
44
+ const modules: Module[] = this.getModules(
45
+ container.getModules(),
46
+ includedModules,
47
+ );
48
+ const globalPrefix = !ignoreGlobalPrefix
49
+ ? stripLastSlash(this.getGlobalPrefix(app))
50
+ : '';
51
+
52
+ const denormalizedChannels = modules.reduce(
53
+ (channels, { providers, metatype, imports, controllers }) => {
54
+ let allProviders = new Map([...providers, ...controllers]);
55
+
56
+ if (deepScanRoutes) {
57
+ // only load submodules routes if asked
58
+ const isGlobal = (module: Type<any>) =>
59
+ !container.isGlobalModule(module);
60
+
61
+ Array.from(imports.values())
62
+ .filter(isGlobal as any)
63
+ .map(
64
+ ({
65
+ providers: relatedProviders,
66
+ controllers: relatedControllers,
67
+ }) => ({
68
+ relatedProviders,
69
+ relatedControllers,
70
+ }),
71
+ )
72
+ .forEach(({ relatedProviders, relatedControllers }) => {
73
+ allProviders = new Map([
74
+ ...allProviders,
75
+ ...relatedProviders,
76
+ ...relatedControllers,
77
+ ]);
78
+ });
79
+ }
80
+ const path = metatype
81
+ ? Reflect.getMetadata(MODULE_PATH, metatype)
82
+ : undefined;
83
+
84
+ return [
85
+ ...channels,
86
+ ...this.scanModuleProviders(
87
+ allProviders,
88
+ path,
89
+ globalPrefix,
90
+ operationIdFactory,
91
+ ),
92
+ ];
93
+ },
94
+ [],
95
+ );
96
+
97
+ const schemas = this.explorer.getSchemas();
98
+ this.addExtraModels(schemas, extraModels);
99
+ const normalizedChannels = this.transformer.normalizeChannels(
100
+ flatten(denormalizedChannels),
101
+ );
102
+ return {
103
+ ...normalizedChannels,
104
+ components: { schemas },
105
+ };
106
+ }
107
+
108
+ private scanModuleProviders(
109
+ providers: Map<InjectionToken, InstanceWrapper<Injectable>>,
110
+ modulePath?: string,
111
+ globalPrefix?: string,
112
+ operationIdFactory?: (controllerKey: string, methodKey: string) => string,
113
+ ): DenormalizedDoc[] {
114
+ const denormalizedArray = [...providers.values()].reduce(
115
+ (denormalized, prov) => {
116
+ const object = this.explorer.explorerAsyncapiServices(
117
+ prov,
118
+ modulePath,
119
+ globalPrefix,
120
+ operationIdFactory,
121
+ );
122
+ return [...denormalized, ...object];
123
+ },
124
+ [],
125
+ );
126
+
127
+ return flatten(denormalizedArray) as any;
128
+ }
129
+
130
+ private getModules(
131
+ modulesContainer: Map<string, Module>,
132
+ include: Function[],
133
+ ): Module[] {
134
+ if (!include || isEmpty(include)) {
135
+ return [...modulesContainer.values()];
136
+ }
137
+ return [...modulesContainer.values()].filter(({ metatype }) =>
138
+ include.some((item) => item === metatype),
139
+ );
140
+ }
141
+
142
+ private getGlobalPrefix(app: INestApplicationContext): string {
143
+ const internalConfigRef = (app as any).config;
144
+ return internalConfigRef?.getGlobalPrefix() || '';
145
+ }
146
+
147
+ private addExtraModels(
148
+ schemas: Record<string, SchemaObject>,
149
+ extraModels: Function[],
150
+ ) {
151
+ extraModels.forEach((item) => {
152
+ this.schemaObjectFactory.exploreModelSchema(item, schemas);
153
+ });
154
+ }
155
+ }
@@ -0,0 +1,39 @@
1
+ import {
2
+ AsyncChannelObject,
3
+ AsyncChannelsObject,
4
+ DenormalizedDoc,
5
+ } from '../interface';
6
+
7
+ export class AsyncapiTransformer {
8
+ public normalizeChannels(
9
+ denormalizedDocs: DenormalizedDoc[],
10
+ ): Record<'channels', AsyncChannelsObject> {
11
+ const flatChannels = denormalizedDocs.map((doc: DenormalizedDoc) => {
12
+ const key = doc.root?.name;
13
+ const value: AsyncChannelObject = {
14
+ description: doc.root?.description,
15
+ bindings: doc.root?.bindings,
16
+ parameters: doc.root?.parameters,
17
+ subscribe: doc.operations?.sub,
18
+ publish: doc.operations?.pub,
19
+ };
20
+ return { key, value };
21
+ });
22
+
23
+ const channels = flatChannels.reduce((acc, { key, value }) => {
24
+ // skip entries without a channel name
25
+ if (!key) return acc;
26
+
27
+ if (!acc[key]) {
28
+ acc[key] = value;
29
+ }
30
+
31
+ acc[key].publish = acc[key].publish ?? value.publish;
32
+ acc[key].subscribe = acc[key].subscribe ?? value.subscribe;
33
+
34
+ return acc;
35
+ }, {} as AsyncChannelsObject);
36
+
37
+ return { channels };
38
+ }
39
+ }
@@ -0,0 +1,5 @@
1
+ export * from './asyncapi.generator';
2
+ export * from './operation-object.factory';
3
+ export * from './asyncapi.explorer';
4
+ export * from './asyncapi.scanner';
5
+ export * from './asyncapi.scanner';