-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathasyncapi.py
238 lines (182 loc) · 6.57 KB
/
asyncapi.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
"""
AsyncAPI 3.0.0
Ref: https://github.com/asyncapi/spec/blob/v3.0.0/spec/asyncapi.md
TODO:
- Only a short subset of components is supported
"""
from typing import Annotated, Any, Literal, Mapping
from fastapi.openapi.models import (
Reference as OpenAPIReference,
)
from fastapi.openapi.models import (
Schema as OpenAPISchema,
)
from pydantic import (
BaseModel,
Field,
RootModel,
field_validator,
model_validator,
)
from ..utils import validate_email_address, validate_url_rfc3986
ASYNCAPI_VERSION = "3.0.0"
SecuritySchemeType = (
Literal["userPassword"]
| Literal["apiKey"]
| Literal["X509"]
| Literal["symmetricEncryption"]
| Literal["asymmetricEncryption"]
| Literal["httpApiKey"]
| Literal["http"]
| Literal["oauth2"]
| Literal["openIdConnect"]
| Literal["plain"]
| Literal["scramSha256"]
| Literal["scramSha512"]
| Literal["gssapi"]
)
class AsyncAPIExternalDocs(BaseModel):
description: str | None = None
url: str
class AsyncAPITag(BaseModel):
name: str
description: str | None = None
externalDocs: AsyncAPIExternalDocs | OpenAPIReference | None = None
class AsyncAPIComponents(BaseModel):
schemas: Mapping[str, OpenAPISchema | OpenAPIReference] | None = None
# ... TODO!
class AsyncAPIMessageExample(BaseModel):
headers: Mapping[str, str] | None = None
payload: Mapping[str, Any] | None = None
name: str | None = None
summary: str | None = None
class AsyncAPIMessage(BaseModel):
headers: OpenAPISchema | OpenAPIReference | None = None
payload: Any | OpenAPIReference | OpenAPIReference | None = None
correlationId: Any | None = None # TODO
contentType: str | None = None
name: str | None = None
title: str | None = None
summary: str | None = None
description: str | None = None
tags: list[AsyncAPITag] | None = None
externalDocs: AsyncAPIExternalDocs | OpenAPIReference | None = None
bindings: OpenAPIReference | None = None # TODO
examples: list[AsyncAPIMessageExample] | None = None
traits: OpenAPIReference | None = None # TODO
class AsyncAPIOneOfMessages(BaseModel):
oneOf: AsyncAPIMessage | OpenAPIReference
class AsyncAPISecuritySchemeOther(BaseModel):
type: SecuritySchemeType
description: str | None = None
class AsyncAPISecuritySchemeHttpApiKey(BaseModel):
type: SecuritySchemeType = "httpApiKey"
name: str
location_in: Annotated[str, Field(alias="in")]
class AsyncAPISecuritySchemeApiKey(BaseModel):
type: SecuritySchemeType = "apiKey"
location_in: Annotated[str, Field(alias="in")]
class AsyncAPISecuritySchemeHttp(BaseModel):
type: SecuritySchemeType = "http"
scheme: str
bearerFormat: str | None = None
class AsyncAPISecuritySchemeOAuth2(BaseModel):
type: SecuritySchemeType = "oauth2"
flows: Any # TODO
scopes: list[str] | None = None
class AsyncAPISecuritySchemeOpenIDConnect(BaseModel):
type: SecuritySchemeType = "openIdConnect"
openIdConnectUrl: str
scopes: list[str] | None = None
AnySecurityScheme = (
AsyncAPISecuritySchemeOther
| AsyncAPISecuritySchemeHttpApiKey
| AsyncAPISecuritySchemeApiKey
| AsyncAPISecuritySchemeHttp
| AsyncAPISecuritySchemeOAuth2
| AsyncAPISecuritySchemeOpenIDConnect
| OpenAPIReference
)
class AsyncAPIOperation(BaseModel):
action: Literal["send"] | Literal["receive"]
channel: OpenAPIReference
title: str | None = None
summary: str | None = None
description: str | None = None
security: list[AnySecurityScheme] | None = None
tags: list[AsyncAPITag] | None = None
externalDocs: AsyncAPIExternalDocs | OpenAPIReference | None = None
bindings: OpenAPIReference | None = None # TODO
traits: OpenAPIReference | None = None # TODO
messages: list[OpenAPIReference] | None = None
reply: OpenAPIReference | None = None # TODO
class AsyncAPIParameter(BaseModel):
description: str | None = None
parameterSchema: Any = Field(..., alias="schema") # TODO
location: str | None = None
class AsyncAPIChannel(BaseModel):
address: str | None = None # TODO: Add explicit `null` value
messages: Mapping[str, AsyncAPIMessage | OpenAPIReference] | None = None
title: str | None = None
summary: str | None = None
description: str | None = None
servers: list[OpenAPIReference] | None = None
parameters: Mapping[str, AsyncAPIParameter] | None = None
tags: list[AsyncAPITag] | None = None
externalDocs: AsyncAPIExternalDocs | OpenAPIReference | None = None
bindings: OpenAPIReference | None = None # TODO
class AsyncAPIServerVariable(BaseModel):
enum: list[str] | None = None
default: str | None = None
description: str | None = None
examples: list[str] | None = None
class AsyncAPIServer(BaseModel):
host: str
protocol: str
protocolVersion: str | None = None
pathname: str | None = None
description: str | None = None
title: str | None = None
summary: str | None = None
variables: Mapping[str, AsyncAPIServerVariable | OpenAPIReference] | None = None
security: list[AnySecurityScheme | OpenAPIReference] | None = None
tags: list[AsyncAPITag] | None = None
externalDocs: AsyncAPIExternalDocs | OpenAPIReference | None = None
bindings: OpenAPIReference | None = None # TODO
class AsyncAPIInfoLicense(BaseModel):
name: str
url: str | None = None
class AsyncAPIInfoContact(BaseModel):
name: str | None = None
url: str | None = None
email: str | None = None
@field_validator("url")
def validate_url(cls, v):
validate_url_rfc3986(v)
@field_validator("email")
def validate_email(cls, v):
validate_email_address(v)
class AsyncAPIInfo(BaseModel):
title: str
version: str
description: str | None = None
termsOfService: str | None = None
contact: AsyncAPIInfoContact | None = None
license: AsyncAPIInfoLicense | None = None
tags: list[AsyncAPITag] | None = None
externalDocs: AsyncAPIExternalDocs | OpenAPIReference | None = None
class AsyncAPIIdentifier(RootModel):
root: str
@model_validator(mode="after")
def validate_is_uri(self):
validate_url_rfc3986(self.root)
return self
class AsyncAPI(BaseModel):
asyncapi: str = ASYNCAPI_VERSION
id: AsyncAPIIdentifier | None = None
info: AsyncAPIInfo
servers: Mapping[str, AsyncAPIServer | OpenAPIReference] | None = None
defaultContentType: str | None = None
channels: Mapping[str, AsyncAPIChannel | OpenAPIReference]
operations: Mapping[str, AsyncAPIOperation]
components: AsyncAPIComponents | None = None