4
4
5
5
import logging
6
6
import os
7
- from pathlib import Path
8
7
9
8
from openapi_core import OpenAPI
10
9
from openapi_core .contrib .werkzeug import WerkzeugOpenAPIRequest , WerkzeugOpenAPIResponse
13
12
RequestValidationError ,
14
13
)
15
14
from openapi_core .validation .response .exceptions import ResponseValidationError
15
+ from plux import PluginManager
16
16
17
17
from localstack import config
18
18
from localstack .aws .api import RequestContext
19
19
from localstack .aws .chain import Handler , HandlerChain
20
20
from localstack .constants import INTERNAL_RESOURCE_PATH
21
21
from localstack .http import Response
22
+ from localstack .plugins import OASPlugin
22
23
23
24
LOG = logging .getLogger (__name__ )
24
25
25
26
26
- # TODO: replace with from importlib.resources.files when https://github.com/python/importlib_resources/issues/311 is
27
- # resolved. Import from a namespace package is broken when installing in editable mode.
28
- oas_path = os .path .join (os .path .dirname (__file__ ), ".." , ".." , "openapi.yaml" )
27
+ class CoreOASPlugin (OASPlugin ):
28
+ name = "localstack.core"
29
+
30
+ def __init__ (self ):
31
+ path = os .path .join (os .path .dirname (__file__ ), ".." , ".." , "openapi.yaml" )
32
+ super ().__init__ (path )
29
33
30
34
31
35
class OpenAPIValidator (Handler ):
32
- openapi : "OpenAPI"
36
+ open_apis : list [ "OpenAPI" ]
33
37
34
38
def __init__ (self ) -> None :
35
- path = Path (oas_path )
36
- assert path .exists ()
37
- self .openapi = OpenAPI .from_path (path )
39
+ specs = PluginManager ("localstack.openapi.spec" ).load_all ()
40
+ self .open_apis = []
41
+ for spec in specs :
42
+ self .open_apis .append (OpenAPI .from_path (spec .spec_path ))
38
43
39
44
40
45
class OpenAPIRequestValidator (OpenAPIValidator ):
@@ -51,7 +56,8 @@ def __call__(self, chain: HandlerChain, context: RequestContext, response: Respo
51
56
52
57
if path .startswith (f"{ INTERNAL_RESOURCE_PATH } /" ) or path .startswith ("/_aws/" ):
53
58
try :
54
- self .openapi .validate_request (WerkzeugOpenAPIRequest (context .request ))
59
+ for openapi in self .open_apis :
60
+ openapi .validate_request (WerkzeugOpenAPIRequest (context .request ))
55
61
except RequestValidationError as e :
56
62
# Note: in this handler we only check validation errors, e.g., wrong body, missing required in the body.
57
63
response .status_code = 400
@@ -77,10 +83,11 @@ def __call__(self, chain: HandlerChain, context: RequestContext, response: Respo
77
83
78
84
if path .startswith (f"{ INTERNAL_RESOURCE_PATH } /" ) or path .startswith ("/_aws/" ):
79
85
try :
80
- self .openapi .validate_response (
81
- WerkzeugOpenAPIRequest (context .request ),
82
- WerkzeugOpenAPIResponse (response ),
83
- )
86
+ for openapi in self .open_apis :
87
+ openapi .validate_response (
88
+ WerkzeugOpenAPIRequest (context .request ),
89
+ WerkzeugOpenAPIResponse (response ),
90
+ )
84
91
except ResponseValidationError as exc :
85
92
LOG .error ("Response validation failed for %s: $s" , path , exc )
86
93
response .status_code = 500
0 commit comments