7
7
from azure_devtools .ci_tools .git_tools import get_add_diff_file_list
8
8
from pathlib import Path
9
9
from subprocess import check_call
10
- from typing import List
10
+ from typing import List , Dict , Any
11
+ from glob import glob
12
+ import yaml
13
+
14
+ from .swaggertosdk .autorest_tools import build_autorest_options , generate_code
15
+ from .swaggertosdk .SwaggerToSdkCore import CONFIG_FILE_DPG , read_config
11
16
12
- from .swaggertosdk .autorest_tools import build_autorest_options
13
17
14
18
_LOGGER = logging .getLogger (__name__ )
15
19
_SDK_FOLDER_RE = re .compile (r"^(sdk/[\w-]+)/(azure[\w-]+)/" , re .ASCII )
16
20
17
21
DEFAULT_DEST_FOLDER = "./dist"
18
-
22
+ _DPG_README = "README.md"
19
23
20
24
def get_package_names (sdk_folder ):
21
25
files = get_add_diff_file_list (sdk_folder )
@@ -42,9 +46,13 @@ def update_servicemetadata(sdk_folder, data, config, folder_name, package_name,
42
46
43
47
readme_file = str (Path (spec_folder , input_readme ))
44
48
global_conf = config ["meta" ]
45
- local_conf = config [ "projects" ][ readme_file ]
49
+ local_conf = config . get ( "projects" , {}). get ( readme_file , {})
46
50
47
- cmd = ["autorest" , input_readme ]
51
+ if "resource-manager" in input_readme :
52
+ cmd = ["autorest" , input_readme ]
53
+ else :
54
+ # autorest for DPG will be executed in package folder like: sdk/deviceupdate/azure-iot-deviceupdate/swagger
55
+ cmd = ["autorest" , _DPG_README ]
48
56
cmd += build_autorest_options (global_conf , local_conf )
49
57
50
58
# metadata
@@ -71,23 +79,24 @@ def update_servicemetadata(sdk_folder, data, config, folder_name, package_name,
71
79
_LOGGER .info (f"Saved metadata to { metadata_file_path } " )
72
80
73
81
# Check whether MANIFEST.in includes _meta.json
74
- require_meta = "include _meta.json\n "
75
- manifest_file = os .path .join (package_folder , "MANIFEST.in" )
76
- if not os .path .exists (manifest_file ):
77
- _LOGGER .info (f"MANIFEST.in doesn't exist: { manifest_file } " )
78
- return
79
-
80
- includes = []
81
- write_flag = False
82
- with open (manifest_file , "r" ) as f :
83
- includes = f .readlines ()
84
- if require_meta not in includes :
85
- includes = [require_meta ] + includes
86
- write_flag = True
87
-
88
- if write_flag :
89
- with open (manifest_file , "w" ) as f :
90
- f .write ("" .join (includes ))
82
+ if "resource-manager" in input_readme :
83
+ require_meta = "include _meta.json\n "
84
+ manifest_file = os .path .join (package_folder , "MANIFEST.in" )
85
+ if not os .path .exists (manifest_file ):
86
+ _LOGGER .info (f"MANIFEST.in doesn't exist: { manifest_file } " )
87
+ return
88
+
89
+ includes = []
90
+ write_flag = False
91
+ with open (manifest_file , "r" ) as f :
92
+ includes = f .readlines ()
93
+ if require_meta not in includes :
94
+ includes = [require_meta ] + includes
95
+ write_flag = True
96
+
97
+ if write_flag :
98
+ with open (manifest_file , "w" ) as f :
99
+ f .write ("" .join (includes ))
91
100
92
101
93
102
# find all the files of one folder, including files in subdirectory
@@ -129,6 +138,154 @@ def judge_tag_preview(path: str) -> bool:
129
138
return 'preview' in api_version
130
139
131
140
141
+ def extract_yaml_content (autorest_config : str ) -> str :
142
+ num = []
143
+ content = autorest_config .split ('\r \n ' )
144
+ for i in range (len (content )):
145
+ if "```" in content [i ]:
146
+ num .append (i )
147
+ if len (num ) != 2 :
148
+ raise Exception (f"autorestConfig content is not valid: { autorest_config } " )
149
+ return '\n ' .join (content [num [0 ] + 1 : num [1 ]])
150
+
151
+
152
+ def add_config_title (content : str ) -> str :
153
+ return f"# autorest configuration for Python\n \n { content } "
154
+
155
+
156
+ def yaml_block (content : str , annotation : str = "" , tag : str = "" ) -> str :
157
+ annotation = f"{ annotation } \n \n " if annotation else annotation
158
+ return f"{ annotation } " + f"``` yaml { tag } \n " + content + "```\n "
159
+
160
+
161
+ def gen_package_name (origin_config : Dict [str , Any ]) -> str :
162
+ return Path (origin_config ["output-folder" ]).parts [- 1 ]
163
+
164
+
165
+ def gen_basic_config (origin_config : Dict [str , Any ]) -> Dict [str , Any ]:
166
+ return {
167
+ "package-name" : gen_package_name (origin_config ),
168
+ "license-header" : "MICROSOFT_MIT_NO_VERSION" ,
169
+ "package-version" : origin_config .get ("package-version" , "1.0.0b1" ),
170
+ "require" : ["../../../../../azure-rest-api-specs/" + line for line in origin_config ["require" ]],
171
+ "package-mode" : "dataplane" ,
172
+ "output-folder" : "../" ,
173
+ }
174
+
175
+
176
+ def gen_general_namespace (package_name : str ) -> str :
177
+ return package_name .replace ('-' , '.' )
178
+
179
+
180
+ def gen_dpg_config_single_client (origin_config : Dict [str , Any ]) -> str :
181
+ package_name = Path (origin_config ["output-folder" ]).parts [- 1 ]
182
+ readme_config = gen_basic_config (origin_config )
183
+ readme_config .update ({
184
+ "namespace" : gen_general_namespace (package_name ),
185
+ })
186
+ readme_content = yaml_block (yaml .safe_dump (readme_config ), "### Settings" )
187
+ return add_config_title (readme_content )
188
+
189
+
190
+ def gen_tag_config (origin_config : Dict [str , Any ]) -> Dict [str , Any ]:
191
+ tag_config = {}
192
+ package_name = gen_package_name (origin_config )
193
+ for tag in origin_config ["batch" ]:
194
+ tag_name = tag ["tag" ]
195
+ extra_part = tag_name .split ("-" )[- 1 ]
196
+ tag_config [tag_name ] = {
197
+ "namespace" : gen_general_namespace (package_name ) + f".{ extra_part } " ,
198
+ }
199
+
200
+ return tag_config
201
+
202
+
203
+ def gen_batch_config (origin_config : Dict [str , Any ]) -> Dict [str , Any ]:
204
+ batch_config = []
205
+ for item in origin_config ["batch" ]:
206
+ for _ , value in item .items ():
207
+ batch_config .append ({value : True })
208
+ return {"batch" : batch_config }
209
+
210
+
211
+ def gen_dpg_config_multi_client (origin_config : Dict [str , Any ]) -> str :
212
+ # generate config
213
+ basic_config = gen_basic_config (origin_config )
214
+ batch_config = gen_batch_config (origin_config )
215
+ tag_config = gen_tag_config (origin_config )
216
+
217
+ # convert to string
218
+ readme_content = yaml_block (yaml .dump (basic_config ), "### Settings" )
219
+ readme_content += yaml_block (yaml .dump (batch_config ), "\n ### Python multi-client" )
220
+ for tag , value in tag_config .items ():
221
+ readme_content += yaml_block (
222
+ yaml .dump (value ),
223
+ f"\n ### Tag: { tag } " ,
224
+ f"$({ tag } )" ,
225
+ )
226
+
227
+ return add_config_title (readme_content )
228
+
229
+
230
+ # generate swagger/README.md and return relative path based on SDK repo root path
231
+ def gen_dpg_config (autorest_config : str ) -> str :
232
+ # remove useless lines
233
+ autorest_config = extract_yaml_content (autorest_config )
234
+ _LOGGER .info (f"autorestConfig after remove useless lines:\n { autorest_config } " )
235
+
236
+ # make dir if not exist
237
+ origin_config = yaml .safe_load (autorest_config )
238
+ _LOGGER .info (f"autorestConfig: { origin_config } " )
239
+ swagger_folder = str (Path (origin_config ["output-folder" ], "swagger" ))
240
+ if not os .path .exists (swagger_folder ):
241
+ os .makedirs (swagger_folder )
242
+
243
+ # generate autorest configuration
244
+ if "batch:" in autorest_config :
245
+ readme_content = gen_dpg_config_multi_client (origin_config )
246
+ else :
247
+ readme_content = gen_dpg_config_single_client (origin_config )
248
+
249
+ # output autorest configuration
250
+ swagger_readme = str (Path (swagger_folder , _DPG_README ))
251
+ with open (swagger_readme , "w" ) as file :
252
+ file .write (readme_content )
253
+ return swagger_readme
254
+
255
+
256
+ def lookup_swagger_readme (rest_readme_path : str ) -> str :
257
+ all_swagger_readme = glob (str (Path (f'sdk/*/*/swagger/{ _DPG_README } ' )))
258
+ for readme in all_swagger_readme :
259
+ with open (readme , 'r' ) as file :
260
+ content = file .read ()
261
+ if rest_readme_path in content :
262
+ _LOGGER .info (f"find swagger readme: { readme } " )
263
+ return readme
264
+ _LOGGER .info (f"do not find swagger readme which contains { rest_readme_path } " )
265
+ return ""
266
+
267
+
268
+ def gen_dpg (rest_readme_path : str , autorest_config : str ) -> Dict [str , Any ]:
269
+ # generate or find swagger/README.md
270
+ if autorest_config :
271
+ swagger_readme = gen_dpg_config (autorest_config )
272
+ else :
273
+ swagger_readme = lookup_swagger_readme (rest_readme_path )
274
+ if not swagger_readme :
275
+ return
276
+
277
+ # extract global config
278
+ global_config = read_config ('.' , CONFIG_FILE_DPG )
279
+
280
+ # generate code
281
+ current_path = os .getcwd ()
282
+ os .chdir (Path (swagger_readme ).parent )
283
+ generate_code (_DPG_README , global_config ["meta" ], {})
284
+ os .chdir (current_path )
285
+
286
+ return global_config
287
+
288
+
132
289
def format_samples (sdk_code_path ) -> None :
133
290
generate_sample_path = Path (sdk_code_path ) / 'generated_samples'
134
291
if not generate_sample_path .exists ():
0 commit comments