22import requests
33import os
44import shutil
5- from typing import List
5+ from typing import List , cast
66
7+ from polyapi import schema
78from polyapi .auth import render_auth_function
89from polyapi .client import render_client_function
10+ from polyapi .poly_schemas import generate_schemas
911from polyapi .webhook import render_webhook_handle
1012
11- from .typedefs import PropertySpecification , SpecificationDto , VariableSpecDto
13+ from .typedefs import PropertySpecification , SchemaSpecDto , SpecificationDto , VariableSpecDto
1214from .api import render_api_function
1315from .server import render_server_function
1416from .utils import add_import_to_init , get_auth_headers , init_the_init , to_func_namespace
1820SUPPORTED_FUNCTION_TYPES = {
1921 "apiFunction" ,
2022 "authFunction" ,
21- "customFunction" ,
23+ "customFunction" , # client function - this is badly named in /specs atm
2224 "serverFunction" ,
2325 "webhookHandle" ,
2426}
2527
26- SUPPORTED_TYPES = SUPPORTED_FUNCTION_TYPES | {"serverVariable" }
28+ SUPPORTED_TYPES = SUPPORTED_FUNCTION_TYPES | {"serverVariable" , "schema" , "snippet" }
29+
30+
31+ X_POLY_REF_WARNING = '''"""
32+ x-poly-ref:
33+ path:'''
34+
35+ X_POLY_REF_BETTER_WARNING = '''"""
36+ Unresolved schema, please add the following schema to complete it:
37+ path:'''
2738
2839
2940def get_specs () -> List :
@@ -38,9 +49,56 @@ def get_specs() -> List:
3849 raise NotImplementedError (resp .content )
3950
4051
52+ def build_schema_index (items ):
53+ index = {}
54+ for item in items :
55+ if item .get ("type" ) == "schema" and "contextName" in item :
56+ index [item ["contextName" ]] = {** item .get ("definition" , {}), "name" : item .get ("name" )}
57+ return index
58+
59+
60+ def resolve_poly_refs (obj , schema_index ):
61+ if isinstance (obj , dict ):
62+ if "x-poly-ref" in obj :
63+ ref = obj ["x-poly-ref" ]
64+ if isinstance (ref , dict ) and "path" in ref :
65+ path = ref ["path" ]
66+ if path in schema_index :
67+ return resolve_poly_refs (schema_index [path ], schema_index )
68+ else :
69+ return obj
70+ return {k : resolve_poly_refs (v , schema_index ) for k , v in obj .items ()}
71+ elif isinstance (obj , list ):
72+ return [resolve_poly_refs (item , schema_index ) for item in obj ]
73+ else :
74+ return obj
75+
76+
77+ def replace_poly_refs_in_functions (specs : List [SpecificationDto ], schema_index ):
78+ spec_idxs_to_remove = []
79+ for idx , spec in enumerate (specs ):
80+ if spec .get ("type" ) in ("apiFunction" , "customFunction" , "serverFunction" ):
81+ func = spec .get ("function" )
82+ if func :
83+ try :
84+ spec ["function" ] = resolve_poly_refs (func , schema_index )
85+ except Exception :
86+ print ()
87+ print (f"{ spec ['context' ]} .{ spec ['name' ]} (id: { spec ['id' ]} ) failed to resolve poly refs, skipping!" )
88+ spec_idxs_to_remove .append (idx )
89+
90+ # reverse the list so we pop off later indexes first
91+ spec_idxs_to_remove .reverse ()
92+
93+ for idx in spec_idxs_to_remove :
94+ specs .pop (idx )
95+
96+ return specs
97+
98+
4199def parse_function_specs (
42100 specs : List [SpecificationDto ],
43- limit_ids : List [str ] | None , # optional list of ids to limit to
101+ limit_ids : List [str ] | None = None , # optional list of ids to limit to
44102) -> List [SpecificationDto ]:
45103 functions = []
46104 for spec in specs :
@@ -91,23 +149,14 @@ def read_cached_specs() -> List[SpecificationDto]:
91149 return json .loads (f .read ())
92150
93151
94- def get_functions_and_parse (limit_ids : List [str ] | None = None ) -> List [SpecificationDto ]:
95- specs = get_specs ()
96- cache_specs (specs )
97- return parse_function_specs (specs , limit_ids = limit_ids )
152+ def get_variables () -> List [VariableSpecDto ]:
153+ specs = read_cached_specs ()
154+ return [cast (VariableSpecDto , spec ) for spec in specs if spec ["type" ] == "serverVariable" ]
98155
99156
100- def get_variables () -> List [VariableSpecDto ]:
101- api_key , api_url = get_api_key_and_url ()
102- headers = {"Authorization" : f"Bearer { api_key } " }
103- # TODO do some caching so this and get_functions just do 1 function call
104- url = f"{ api_url } /specs"
105- resp = requests .get (url , headers = headers )
106- if resp .status_code == 200 :
107- specs = resp .json ()
108- return [spec for spec in specs if spec ["type" ] == "serverVariable" ]
109- else :
110- raise NotImplementedError (resp .content )
157+ def get_schemas () -> List [SchemaSpecDto ]:
158+ specs = read_cached_specs ()
159+ return [cast (SchemaSpecDto , spec ) for spec in specs if spec ["type" ] == "schema" ]
111160
112161
113162def remove_old_library ():
@@ -120,12 +169,28 @@ def remove_old_library():
120169 if os .path .exists (path ):
121170 shutil .rmtree (path )
122171
172+ path = os .path .join (currdir , "schemas" )
173+ if os .path .exists (path ):
174+ shutil .rmtree (path )
175+
123176
124177def generate () -> None :
125178
126179 remove_old_library ()
127180
128- functions = get_functions_and_parse ()
181+ limit_ids : List [str ] = [] # useful for narrowing down generation to a single function to debug
182+
183+ specs = get_specs ()
184+ cache_specs (specs )
185+ functions = parse_function_specs (specs , limit_ids = limit_ids )
186+
187+ schemas = get_schemas ()
188+ if schemas :
189+ generate_schemas (schemas )
190+
191+ schema_index = build_schema_index (schemas )
192+ functions = replace_poly_refs_in_functions (functions , schema_index )
193+
129194 if functions :
130195 generate_functions (functions )
131196 else :
@@ -138,6 +203,7 @@ def generate() -> None:
138203 if variables :
139204 generate_variables (variables )
140205
206+
141207 # indicator to vscode extension that this is a polyapi-python project
142208 file_path = os .path .join (os .getcwd (), ".polyapi-python" )
143209 open (file_path , "w" ).close ()
@@ -214,6 +280,12 @@ def render_spec(spec: SpecificationDto):
214280 arguments ,
215281 return_type ,
216282 )
283+
284+ if X_POLY_REF_WARNING in func_type_defs :
285+ # this indicates that jsonschema_gentypes has detected an x-poly-ref
286+ # let's add a more user friendly error explaining what is going on
287+ func_type_defs = func_type_defs .replace (X_POLY_REF_WARNING , X_POLY_REF_BETTER_WARNING )
288+
217289 return func_str , func_type_defs
218290
219291
0 commit comments