tools/azure-rest-api-specs-examples-automation/java/main.py (264 lines of code) (raw):

import sys import os from os import path import json import argparse import logging import dataclasses from typing import List import importlib.util from modules import JavaExample, JavaFormatResult from package import MavenPackage from format import JavaFormat spec_location = ( "./directory/examples_dir.py" if path.exists("./directory/examples_dir.py") else "../directory/examples_dir.py" ) spec = importlib.util.spec_from_file_location("examples_dir", spec_location) examples_dir = importlib.util.module_from_spec(spec) spec.loader.exec_module(examples_dir) script_path: str = "." tmp_path: str specs_path: str sdk_package_path: str namespace = "com.azure.resourcemanager" original_file_key = "* x-ms-original-file:" @dataclasses.dataclass(eq=True, frozen=True) class Release: tag: str package: str version: str sdk_name: str @dataclasses.dataclass(eq=True) class JavaExampleMethodContent: example_relative_path: str = None content: List[str] = None line_start: int = None line_end: int = None def is_valid(self) -> bool: return self.example_relative_path is not None @dataclasses.dataclass(eq=True) class AggregatedJavaExample: methods: List[JavaExampleMethodContent] class_opening: List[str] = None class_closing: List[str] = None def _set_paths(new_specs_path: str, new_sdk_package_path: str): # for test global specs_path global sdk_package_path specs_path = new_specs_path sdk_package_path = new_sdk_package_path def get_sdk_name_from_package(package: str) -> str: if package == "azure-resourcemanager": return "resourcemanager" else: return package[len("azure-resourcemanager-") :] def is_aggregated_java_example(lines: List[str]) -> bool: # check metadata to see if the sample Java is a candidate for example extraction for line in lines: if line.strip().startswith(original_file_key): return True return False def get_java_example_method(lines: List[str], start: int) -> JavaExampleMethodContent: # extract one example method, start from certain line number original_file = None java_example_method = JavaExampleMethodContent() for index in range(len(lines)): if index < start: continue line = lines[index] if line.strip().startswith(original_file_key): original_file = line.strip()[len(original_file_key) :].strip() # merge rest of the lines peek_index = index + 1 while peek_index < len(lines): peek_line = lines[peek_index] if peek_line.strip() == "*/": # end of comment block break else: # content of original_file breaks into this line of comment original_file = original_file + peek_line.strip()[len("*") :].strip() peek_index += 1 elif line.startswith(" public static void"): # begin of method java_example_method.example_relative_path = original_file java_example_method.line_start = index elif line.startswith(" }"): # end of method java_example_method.line_end = index + 1 break index += 1 if java_example_method.is_valid(): # backtrace to include javadoc and comments before the method declaration for index in range(java_example_method.line_start - 1, start - 1, -1): line = lines[index] if ( line.strip().startswith("*") or line.strip().startswith("/*") or line.strip().startswith("*/") or line.strip().startswith("//") ): java_example_method.line_start = index else: break java_example_method.content = lines[java_example_method.line_start : java_example_method.line_end] return java_example_method def break_down_aggregated_java_example(lines: List[str]) -> AggregatedJavaExample: # break down sample Java to multiple examples aggregated_java_example = AggregatedJavaExample([]) java_example_method = get_java_example_method(lines, 0) line_start = java_example_method.line_start line_end = java_example_method.line_end while java_example_method.is_valid(): aggregated_java_example.methods.append(java_example_method) line_end = java_example_method.line_end java_example_method = get_java_example_method(lines, java_example_method.line_end) aggregated_java_example.class_opening = lines[0:line_start] aggregated_java_example.class_closing = lines[line_end:] return aggregated_java_example def format_java(lines: List[str], old_class_name: str, new_class_name: str) -> List[str]: # format example as Java code new_lines = [] skip_head = True for line in lines: if not skip_head: # use new class name line = line.replace("class " + old_class_name + " {", "class " + new_class_name + " {", 1) new_lines.append(line) else: # remove package if line.startswith("package "): skip_head = False return new_lines def process_java_example(filepath: str) -> List[JavaExample]: # process aggregated Java sample to examples filename = path.basename(filepath) logging.info(f"Processing Java aggregated sample: {filename}") with open(filepath, encoding="utf-8") as f: lines = f.readlines() class_name = filename.split(".")[0] return process_java_example_content(lines, class_name) def process_java_example_content(lines: List[str], class_name: str) -> List[JavaExample]: java_examples = [] if is_aggregated_java_example(lines): aggregated_java_example = break_down_aggregated_java_example(lines) for java_example_method in aggregated_java_example.methods: if java_example_method.is_valid(): logging.info(f"Processing java example: {java_example_method.example_relative_path}") # re-construct the example class, from example method example_lines = ( aggregated_java_example.class_opening + java_example_method.content + aggregated_java_example.class_closing ) example_filepath = java_example_method.example_relative_path example_dir, example_filename = path.split(example_filepath) try: example_dir = examples_dir.try_find_resource_manager_example( specs_path, sdk_package_path, example_dir, example_filename ) except NameError: pass # use Main as class name old_class_name = class_name new_class_name = "Main" example_lines = format_java(example_lines, old_class_name, new_class_name) filename = example_filename.split(".")[0] # use the examples-java folder for Java example md_dir = ( (example_dir + "-java") if example_dir.endswith("/examples") else example_dir.replace("/examples/", "/examples-java/") ) java_example = JavaExample(filename, md_dir, "".join(example_lines)) java_examples.append(java_example) return java_examples def validate_java_examples(release: Release, java_examples: List[JavaExample]) -> JavaFormatResult: # batch validate Java examples java_format = JavaFormat(tmp_path, path.join(script_path, "javaformat")) java_format_result = java_format.format(java_examples) if java_format_result.succeeded: maven_package = MavenPackage(tmp_path, release.package, release.version) succeeded = maven_package.compile(java_examples) if not succeeded: return JavaFormatResult(False, java_format_result.examples) return java_format_result def generate_examples(release: Release, sdk_examples_path: str, java_examples: List[JavaExample]) -> List[str]: # generate code and metadata from Java examples files = [] for java_example in java_examples: doc_link = ( f"https://github.com/Azure/azure-sdk-for-java/blob/{release.tag}/sdk/" f"{release.sdk_name}/{release.package}/README.md" ) files.extend( write_code_to_file( sdk_examples_path, java_example.target_dir, java_example.target_filename, ".java", java_example.content, doc_link, ) ) return files def write_code_to_file( sdk_examples_path: str, target_dir: str, filename_root: str, filename_ext: str, code_content: str, sdk_url: str ) -> List[str]: # write code file and metadata file code_filename = filename_root + filename_ext metadata_filename = filename_root + ".json" metadata_json = {"sdkUrl": sdk_url} target_dir_path = path.join(sdk_examples_path, target_dir) os.makedirs(target_dir_path, exist_ok=True) code_file_path = path.join(target_dir_path, code_filename) with open(code_file_path, "w", encoding="utf-8") as f: f.write(code_content) logging.info(f"Code written to file: {code_file_path}") metadata_file_path = path.join(target_dir_path, metadata_filename) with open(metadata_file_path, "w", encoding="utf-8") as f: json.dump(metadata_json, f) logging.info(f"Metadata written to file: {metadata_file_path}") return [path.join(target_dir, code_filename), path.join(target_dir, metadata_filename)] def create_java_examples(release: Release, sdk_examples_path: str, java_examples_path: str) -> (bool, List[str]): logging.info("Preparing tools and thread pool") logging.info(f"Processing SDK examples: {release.sdk_name}") java_examples = [] java_paths = [] for root, dirs, files in os.walk(java_examples_path): for name in files: filepath = path.join(root, name) if path.splitext(filepath)[1] == ".java": java_paths.append(filepath) for filepath in java_paths: java_examples += process_java_example(filepath) files = [] if java_examples: logging.info("Validating SDK examples") java_build_result = validate_java_examples(release, java_examples) if java_build_result.succeeded: files = generate_examples(release, sdk_examples_path, java_build_result.examples) else: logging.error("Validation failed") return java_build_result.succeeded, files else: logging.info("SDK examples not found") return True, files def main(): global script_path global tmp_path global specs_path global sdk_package_path logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %X") script_path = path.abspath(path.dirname(sys.argv[0])) parser = argparse.ArgumentParser(description='Requires 2 arguments, path of "input.json" and "output.json".') parser.add_argument("paths", metavar="path", type=str, nargs=2, help='path of "input.json" or "output.json"') args = parser.parse_args() input_json_path = args.paths[0] output_json_path = args.paths[1] with open(input_json_path, "r", encoding="utf-8") as f_in: config = json.load(f_in) specs_path = config["specsPath"] sdk_path = config["sdkPath"] sdk_examples_path = config["sdkExamplesPath"] tmp_path = config["tempPath"] release = Release( config["release"]["tag"], config["release"]["package"], config["release"]["version"], get_sdk_name_from_package(config["release"]["package"]), ) java_examples_relative_path = path.join("sdk", release.sdk_name, release.package, "src", "samples") java_examples_path = path.join(sdk_path, java_examples_relative_path) sdk_package_path = path.join(sdk_path, "sdk", release.sdk_name, release.package) succeeded, files = create_java_examples(release, sdk_examples_path, java_examples_path) with open(output_json_path, "w", encoding="utf-8") as f_out: group = "com.azure.resourcemanager" output = { "status": "succeeded" if succeeded else "failed", "name": f"{group}:{release.package}:{release.version}", "files": files, } json.dump(output, f_out, indent=2) if __name__ == "__main__": main()