| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  | #!/usr/bin/env python | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  | """Generates template code and response body for specified boto3's operation.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  | To execute: | 
					
						
							|  |  |  |     cd moto  # top-level directory; script will not work from scripts dir | 
					
						
							|  |  |  |     ./scripts/scaffold.py | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  | When prompted, select the service and operation that you want to add. | 
					
						
							|  |  |  | This script will look at the botocore's definition file for the selected | 
					
						
							|  |  |  | service and operation, then auto-generate the code and responses. | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  | Almost all services are supported, as long as the service's protocol is | 
					
						
							|  |  |  | `query`, `json` or `rest-json`.  Even if aws adds new services, this script | 
					
						
							|  |  |  | will work if the protocol is known. | 
					
						
							| 
									
										
										
										
											2017-09-23 17:03:42 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | TODO: | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |   - This script doesn't generate functions in `responses.py` for | 
					
						
							|  |  |  |     `rest-json`.  That logic needs to be added. | 
					
						
							|  |  |  |   - Some services's operations might cause this script to crash. If that | 
					
						
							|  |  |  |     should happen, please create an issue for the problem. | 
					
						
							| 
									
										
										
										
											2017-09-23 17:03:42 +09:00
										 |  |  | """
 | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  | import inspect | 
					
						
							|  |  |  | import importlib | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  | from lxml import etree | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | import click | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  | import jinja2 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  | from prompt_toolkit import prompt | 
					
						
							| 
									
										
										
										
											2020-01-07 10:12:50 -05:00
										 |  |  | from prompt_toolkit.completion import WordCompleter | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | from botocore import xform_name | 
					
						
							|  |  |  | from botocore.session import Session | 
					
						
							|  |  |  | import boto3 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  | from moto.core.responses import BaseResponse | 
					
						
							|  |  |  | from moto.core import BaseBackend | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  | from inflection import singularize | 
					
						
							| 
									
										
										
										
											2021-10-21 15:13:43 +00:00
										 |  |  | from implementation_coverage import get_moto_implementation | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  | TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), "./template") | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  | INPUT_IGNORED_IN_BACKEND = ["Marker", "PageSize"] | 
					
						
							|  |  |  | OUTPUT_IGNORED_IN_BACKEND = ["NextMarker"] | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | def print_progress(title, body, color): | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     """Prints a color-code message describing current state of progress.""" | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |     click.secho(f"\t{title}\t", fg=color, nl=False) | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  |     click.echo(body) | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def select_service_and_operation(): | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     """Prompt user to select service and operation.""" | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  |     service_names = Session().get_available_services() | 
					
						
							|  |  |  |     service_completer = WordCompleter(service_names) | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |     service_name = prompt("Select service: ", completer=service_completer) | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  |     if service_name not in service_names: | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |         click.secho(f"{service_name} is not valid service", fg="red") | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  |         raise click.Abort() | 
					
						
							|  |  |  |     moto_client = get_moto_implementation(service_name) | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     real_client = boto3.client(service_name, region_name="us-east-1") | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  |     implemented = [] | 
					
						
							|  |  |  |     not_implemented = [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     operation_names = [ | 
					
						
							|  |  |  |         xform_name(op) for op in real_client.meta.service_model.operation_names | 
					
						
							|  |  |  |     ] | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |     for operation in operation_names: | 
					
						
							|  |  |  |         if moto_client and operation in dir(moto_client): | 
					
						
							|  |  |  |             implemented.append(operation) | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |             not_implemented.append(operation) | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  |     operation_completer = WordCompleter(operation_names) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     click.echo("==Current Implementation Status==") | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  |     for operation_name in operation_names: | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         check = "X" if operation_name in implemented else " " | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |         click.secho(f"[{check}] {operation_name}") | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     click.echo("=================================") | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |     operation_name = prompt("Select Operation: ", completer=operation_completer) | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if operation_name not in operation_names: | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |         click.secho(f"{operation_name} is not valid operation", fg="red") | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  |         raise click.Abort() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if operation_name in implemented: | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |         click.secho(f"{operation_name} is already implemented", fg="red") | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  |         raise click.Abort() | 
					
						
							|  |  |  |     return service_name, operation_name | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  | def get_escaped_service(service): | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     """Remove dashes from the service name.""" | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     return service.replace("-", "") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  | def get_lib_dir(service): | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     """Return moto path for the location of the code supporting the service.""" | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     return os.path.join("moto", get_escaped_service(service)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | def get_test_dir(service): | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     """Return moto path for the test directory for the service.""" | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |     return os.path.join("tests", f"test_{get_escaped_service(service)}") | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-22 14:03:12 +09:00
										 |  |  | def render_template(tmpl_dir, tmpl_filename, context, service, alt_filename=None): | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     """Create specified files from Jinja templates for specified service.""" | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |     is_test = "test" in tmpl_dir | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     rendered = ( | 
					
						
							|  |  |  |         jinja2.Environment(loader=jinja2.FileSystemLoader(tmpl_dir)) | 
					
						
							|  |  |  |         .get_template(tmpl_filename) | 
					
						
							|  |  |  |         .render(context) | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     dirname = get_test_dir(service) if is_test else get_lib_dir(service) | 
					
						
							|  |  |  |     filename = alt_filename or os.path.splitext(tmpl_filename)[0] | 
					
						
							|  |  |  |     filepath = os.path.join(dirname, filename) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if os.path.exists(filepath): | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         print_progress("skip creating", filepath, "yellow") | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         print_progress("creating", filepath, "green") | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |         with open(filepath, "w", encoding="utf-8") as fhandle: | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |             fhandle.write(rendered) | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  | def append_mock_to_init_py(service): | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     """Update __init_.py to add line to load the mock service.""" | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     path = os.path.join(os.path.dirname(__file__), "..", "moto", "__init__.py") | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     with open(path, encoding="utf-8") as fhandle: | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |         lines = [_.replace("\n", "") for _ in fhandle.readlines()] | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |     if any(_ for _ in lines if re.match(f"^mock_{service}.*lazy_load(.*)$", _)): | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  |         return | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     filtered_lines = [_ for _ in lines if re.match("^mock_.*lazy_load(.*)$", _)] | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  |     last_import_line_index = lines.index(filtered_lines[-1]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |     escaped_service = get_escaped_service(service) | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     new_line = ( | 
					
						
							|  |  |  |         f"mock_{escaped_service} = lazy_load(" | 
					
						
							|  |  |  |         f'".{escaped_service}", "mock_{escaped_service}", boto3_name="{service}")' | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  |     lines.insert(last_import_line_index + 1, new_line) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     body = "\n".join(lines) + "\n" | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     with open(path, "w", encoding="utf-8") as fhandle: | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |         fhandle.write(body) | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-30 13:00:02 -04:00
										 |  |  | def initialize_service(service, api_protocol): | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     """Create lib and test dirs if they don't exist.""" | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  |     lib_dir = get_lib_dir(service) | 
					
						
							|  |  |  |     test_dir = get_test_dir(service) | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     print_progress("Initializing service", service, "green") | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  |     client = boto3.client(service) | 
					
						
							|  |  |  |     service_class = client.__class__.__name__ | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     endpoint_prefix = ( | 
					
						
							|  |  |  |         # pylint: disable=protected-access | 
					
						
							|  |  |  |         client._service_model.endpoint_prefix | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  |     tmpl_context = { | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         "service": service, | 
					
						
							|  |  |  |         "service_class": service_class, | 
					
						
							|  |  |  |         "endpoint_prefix": endpoint_prefix, | 
					
						
							|  |  |  |         "api_protocol": api_protocol, | 
					
						
							|  |  |  |         "escaped_service": get_escaped_service(service), | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  |     # initialize service directory | 
					
						
							|  |  |  |     if os.path.exists(lib_dir): | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         print_progress("skip creating", lib_dir, "yellow") | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         print_progress("creating", lib_dir, "green") | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  |         os.makedirs(lib_dir) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     tmpl_dir = os.path.join(TEMPLATE_DIR, "lib") | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  |     for tmpl_filename in os.listdir(tmpl_dir): | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         render_template(tmpl_dir, tmpl_filename, tmpl_context, service) | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # initialize test directory | 
					
						
							|  |  |  |     if os.path.exists(test_dir): | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         print_progress("skip creating", test_dir, "yellow") | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         print_progress("creating", test_dir, "green") | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  |         os.makedirs(test_dir) | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     tmpl_dir = os.path.join(TEMPLATE_DIR, "test") | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  |     for tmpl_filename in os.listdir(tmpl_dir): | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         alt_filename = ( | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |             f"test_{get_escaped_service(service)}.py" | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |             if tmpl_filename == "test_service.py.j2" | 
					
						
							|  |  |  |             else None | 
					
						
							| 
									
										
										
										
											2017-09-20 03:14:14 +09:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         render_template(tmpl_dir, tmpl_filename, tmpl_context, service, alt_filename) | 
					
						
							| 
									
										
										
										
											2021-09-30 13:00:02 -04:00
										 |  |  |     # append mock to initi files | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  |     append_mock_to_init_py(service) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  | def to_upper_camel_case(string): | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     """Convert snake case to camel case.""" | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |     return "".join([_.title() for _ in string.split("_")]) | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  | def to_lower_camel_case(string): | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     """Convert snake to camel case, but start string with lowercase letter.""" | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |     words = string.split("_") | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     return "".join(words[:1] + [_.title() for _ in words[1:]]) | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  | def to_snake_case(string): | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     """Convert camel case to snake case.""" | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |     new_string = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", string) | 
					
						
							|  |  |  |     return re.sub("([a-z0-9])([A-Z])", r"\1_\2", new_string).lower() | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-01 19:44:13 +09:00
										 |  |  | def get_operation_name_in_keys(operation_name, operation_keys): | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     """Return AWS operation name (service) found in list of client services.""" | 
					
						
							| 
									
										
										
										
											2020-09-01 19:44:13 +09:00
										 |  |  |     index = [_.lower() for _ in operation_keys].index(operation_name.lower()) | 
					
						
							|  |  |  |     return operation_keys[index] | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  | def get_function_in_responses( | 
					
						
							|  |  |  |     service, operation, protocol | 
					
						
							|  |  |  | ):  # pylint: disable=too-many-locals | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     """refers to definition of API in botocore, and autogenerates function
 | 
					
						
							|  |  |  |     You can see example of elbv2 from link below. | 
					
						
							|  |  |  |       https://github.com/boto/botocore/blob/develop/botocore/data/elbv2/2015-12-01/service-2.json | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |     escaped_service = get_escaped_service(service) | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     client = boto3.client(service) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     # pylint: disable=protected-access | 
					
						
							| 
									
										
										
										
											2020-09-01 19:44:13 +09:00
										 |  |  |     aws_operation_name = get_operation_name_in_keys( | 
					
						
							|  |  |  |         to_upper_camel_case(operation), | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         list(client._service_model._service_description["operations"].keys()), | 
					
						
							| 
									
										
										
										
											2020-09-01 19:44:13 +09:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     op_model = client._service_model.operation_model(aws_operation_name) | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     if not hasattr(op_model.output_shape, "members"): | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  |         outputs = {} | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         outputs = op_model.output_shape.members | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     inputs = op_model.input_shape.members | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     input_names = [ | 
					
						
							|  |  |  |         to_snake_case(_) for _ in inputs.keys() if _ not in INPUT_IGNORED_IN_BACKEND | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  |     output_names = [ | 
					
						
							|  |  |  |         to_snake_case(_) for _ in outputs.keys() if _ not in OUTPUT_IGNORED_IN_BACKEND | 
					
						
							|  |  |  |     ] | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |     body = f"\ndef {operation}(self):\n" | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for input_name, input_type in inputs.items(): | 
					
						
							|  |  |  |         type_name = input_type.type_name | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         if type_name == "integer": | 
					
						
							| 
									
										
										
										
											2021-09-14 05:39:39 -04:00
										 |  |  |             arg_line_tmpl = '    {}=self._get_int_param("{}")\n' | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         elif type_name == "list": | 
					
						
							| 
									
										
										
										
											2021-09-14 05:39:39 -04:00
										 |  |  |             arg_line_tmpl = '    {}=self._get_list_prefix("{}.member")\n' | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2021-09-14 05:39:39 -04:00
										 |  |  |             arg_line_tmpl = '    {}=self._get_param("{}")\n' | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |         body += arg_line_tmpl.format(to_snake_case(input_name), input_name) | 
					
						
							|  |  |  |     if output_names: | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |         body += f"    {', '.join(output_names)} = self.{escaped_service}_backend.{operation}(\n" | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |         body += f"    self.{escaped_service}_backend.{operation}(\n" | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     for input_name in input_names: | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |         body += f"        {input_name}={input_name},\n" | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     body += "    )\n" | 
					
						
							|  |  |  |     if protocol == "query": | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |         body += f"    template = self.response_template({operation.upper()}_TEMPLATE)\n" | 
					
						
							|  |  |  |         names = ", ".join([f"{n}={n}" for n in output_names]) | 
					
						
							|  |  |  |         body += f"    return template.render({names})\n" | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     elif protocol in ["json", "rest-json"]: | 
					
						
							|  |  |  |         body += "    # TODO: adjust response\n" | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |         names = ", ".join([f"{to_lower_camel_case(_)}={_}" for _ in output_names]) | 
					
						
							|  |  |  |         body += f"    return json.dumps(dict({names}))\n" | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     return body | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_function_in_models(service, operation): | 
					
						
							|  |  |  |     """refers to definition of API in botocore, and autogenerates function
 | 
					
						
							|  |  |  |     You can see example of elbv2 from link below. | 
					
						
							|  |  |  |       https://github.com/boto/botocore/blob/develop/botocore/data/elbv2/2015-12-01/service-2.json | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     client = boto3.client(service) | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # pylint: disable=protected-access | 
					
						
							| 
									
										
										
										
											2020-09-01 19:44:13 +09:00
										 |  |  |     aws_operation_name = get_operation_name_in_keys( | 
					
						
							|  |  |  |         to_upper_camel_case(operation), | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         list(client._service_model._service_description["operations"].keys()), | 
					
						
							| 
									
										
										
										
											2020-09-01 19:44:13 +09:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     op_model = client._service_model.operation_model(aws_operation_name) | 
					
						
							|  |  |  |     inputs = op_model.input_shape.members | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     if not hasattr(op_model.output_shape, "members"): | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  |         outputs = {} | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         outputs = op_model.output_shape.members | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     input_names = [ | 
					
						
							|  |  |  |         to_snake_case(_) for _ in inputs.keys() if _ not in INPUT_IGNORED_IN_BACKEND | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  |     output_names = [ | 
					
						
							|  |  |  |         to_snake_case(_) for _ in outputs.keys() if _ not in OUTPUT_IGNORED_IN_BACKEND | 
					
						
							|  |  |  |     ] | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     if input_names: | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |         body = f"def {operation}(self, {', '.join(input_names)}):\n" | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         body = "def {}(self)\n" | 
					
						
							|  |  |  |     body += "    # implement here\n" | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |     body += f"    return {', '.join(output_names)}\n\n" | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return body | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  | def _get_subtree(name, shape, replace_list, name_prefix=None): | 
					
						
							|  |  |  |     if not name_prefix: | 
					
						
							|  |  |  |         name_prefix = [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     class_name = shape.__class__.__name__ | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     if class_name in ("StringShape", "Shape"): | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |         tree = etree.Element(name)  # pylint: disable=c-extension-no-member | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |         if name_prefix: | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |             tree.text = f"{{{{ {name_prefix[-1]}.{to_snake_case(name)} }}}}" | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |             tree.text = f"{{{{ {to_snake_case(name)} }}}}" | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |         return tree | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if class_name in ("ListShape",): | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |         # pylint: disable=c-extension-no-member | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |         replace_list.append((name, name_prefix)) | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |         tree = etree.Element(name) | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         t_member = etree.Element("member") | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |         tree.append(t_member) | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |         for nested_name, nested_shape in shape.member.members.items(): | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |             t_member.append( | 
					
						
							|  |  |  |                 _get_subtree( | 
					
						
							|  |  |  |                     nested_name, | 
					
						
							|  |  |  |                     nested_shape, | 
					
						
							|  |  |  |                     replace_list, | 
					
						
							|  |  |  |                     name_prefix + [singularize(name.lower())], | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |         return tree | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     raise ValueError("Not supported Shape") | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  | def get_response_query_template(service, operation):  # pylint: disable=too-many-locals | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     """refers to definition of API in botocore, and autogenerates template
 | 
					
						
							| 
									
										
										
										
											2017-09-21 21:23:13 +09:00
										 |  |  |     Assume that response format is xml when protocol is query | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     You can see example of elbv2 from link below. | 
					
						
							|  |  |  |       https://github.com/boto/botocore/blob/develop/botocore/data/elbv2/2015-12-01/service-2.json | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     client = boto3.client(service) | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # pylint: disable=protected-access | 
					
						
							| 
									
										
										
										
											2020-09-01 19:44:13 +09:00
										 |  |  |     aws_operation_name = get_operation_name_in_keys( | 
					
						
							|  |  |  |         to_upper_camel_case(operation), | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         list(client._service_model._service_description["operations"].keys()), | 
					
						
							| 
									
										
										
										
											2020-09-01 19:44:13 +09:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     op_model = client._service_model.operation_model(aws_operation_name) | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     result_wrapper = op_model.output_shape.serialization["resultWrapper"] | 
					
						
							|  |  |  |     response_wrapper = result_wrapper.replace("Result", "Response") | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     metadata = op_model.metadata | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     xml_namespace = metadata["xmlNamespace"] | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # build xml tree | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |     # pylint: disable=c-extension-no-member | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     t_root = etree.Element(response_wrapper, xmlns=xml_namespace) | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # build metadata | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     t_metadata = etree.Element("ResponseMetadata") | 
					
						
							|  |  |  |     t_request_id = etree.Element("RequestId") | 
					
						
							|  |  |  |     t_request_id.text = "1549581b-12b7-11e3-895e-1334aEXAMPLE" | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     t_metadata.append(t_request_id) | 
					
						
							|  |  |  |     t_root.append(t_metadata) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # build result | 
					
						
							|  |  |  |     t_result = etree.Element(result_wrapper) | 
					
						
							|  |  |  |     outputs = op_model.output_shape.members | 
					
						
							|  |  |  |     replace_list = [] | 
					
						
							|  |  |  |     for output_name, output_shape in outputs.items(): | 
					
						
							|  |  |  |         t_result.append(_get_subtree(output_name, output_shape, replace_list)) | 
					
						
							|  |  |  |     t_root.append(t_result) | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     xml_body = etree.tostring(t_root, pretty_print=True).decode("utf-8") | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  |     xml_body_lines = xml_body.splitlines() | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  |     for replace in replace_list: | 
					
						
							|  |  |  |         name = replace[0] | 
					
						
							|  |  |  |         prefix = replace[1] | 
					
						
							|  |  |  |         singular_name = singularize(name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |         start_tag = f"<{name}>" | 
					
						
							|  |  |  |         iter_name = f"{prefix[-1]}.{name.lower()}" if prefix else name.lower() | 
					
						
							|  |  |  |         loop_start = f"{{%% for {singular_name.lower()} in {iter_name} %%}}" | 
					
						
							|  |  |  |         end_tag = f"</{name}>" | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         loop_end = "{{ endfor }}" | 
					
						
							| 
									
										
										
										
											2017-09-21 21:23:13 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  |         start_tag_indexes = [i for i, l in enumerate(xml_body_lines) if start_tag in l] | 
					
						
							| 
									
										
										
										
											2017-09-21 21:23:13 +09:00
										 |  |  |         if len(start_tag_indexes) != 1: | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |             raise Exception(f"tag {start_tag} not found in response body") | 
					
						
							| 
									
										
										
										
											2017-09-21 21:23:13 +09:00
										 |  |  |         start_tag_index = start_tag_indexes[0] | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  |         xml_body_lines.insert(start_tag_index + 1, loop_start) | 
					
						
							| 
									
										
										
										
											2017-09-21 21:23:13 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  |         end_tag_indexes = [i for i, l in enumerate(xml_body_lines) if end_tag in l] | 
					
						
							| 
									
										
										
										
											2017-09-21 21:23:13 +09:00
										 |  |  |         if len(end_tag_indexes) != 1: | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |             raise Exception(f"tag {end_tag} not found in response body") | 
					
						
							| 
									
										
										
										
											2017-09-21 21:23:13 +09:00
										 |  |  |         end_tag_index = end_tag_indexes[0] | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  |         xml_body_lines.insert(end_tag_index, loop_end) | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     xml_body = "\n".join(xml_body_lines) | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |     body = f'\n{operation.upper()}_TEMPLATE = """{xml_body}"""' | 
					
						
							| 
									
										
										
										
											2017-09-21 21:23:13 +09:00
										 |  |  |     return body | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | def insert_code_to_class(path, base_class, new_code): | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     """Add code for class handling service's response or backend.""" | 
					
						
							|  |  |  |     with open(path, encoding="utf-8") as fhandle: | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |         lines = [_.replace("\n", "") for _ in fhandle.readlines()] | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     mod_path = os.path.splitext(path)[0].replace("/", ".") | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  |     mod = importlib.import_module(mod_path) | 
					
						
							|  |  |  |     clsmembers = inspect.getmembers(mod, inspect.isclass) | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     _response_cls = [ | 
					
						
							|  |  |  |         _[1] for _ in clsmembers if issubclass(_[1], base_class) and _[1] != base_class | 
					
						
							|  |  |  |     ] | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  |     if len(_response_cls) != 1: | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         raise Exception("unknown error, number of clsmembers is not 1") | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  |     response_cls = _response_cls[0] | 
					
						
							|  |  |  |     code_lines, line_no = inspect.getsourcelines(response_cls) | 
					
						
							|  |  |  |     end_line_no = line_no + len(code_lines) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     func_lines = [" " * 4 + _ for _ in new_code.splitlines()] | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     lines = lines[:end_line_no] + func_lines + lines[end_line_no:] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     body = "\n".join(lines) + "\n" | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     with open(path, "w", encoding="utf-8") as fhandle: | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |         fhandle.write(body) | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  | def insert_url(service, operation, api_protocol):  # pylint: disable=too-many-locals | 
					
						
							|  |  |  |     """Create urls.py with appropriate URL bases and paths.""" | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  |     client = boto3.client(service) | 
					
						
							|  |  |  |     service_class = client.__class__.__name__ | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # pylint: disable=protected-access | 
					
						
							| 
									
										
										
										
											2020-09-01 19:44:13 +09:00
										 |  |  |     aws_operation_name = get_operation_name_in_keys( | 
					
						
							|  |  |  |         to_upper_camel_case(operation), | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         list(client._service_model._service_description["operations"].keys()), | 
					
						
							| 
									
										
										
										
											2020-09-01 19:44:13 +09:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     uri = client._service_model.operation_model(aws_operation_name).http["requestUri"] | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     path = os.path.join( | 
					
						
							|  |  |  |         os.path.dirname(__file__), "..", "moto", get_escaped_service(service), "urls.py" | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     with open(path, encoding="utf-8") as fhandle: | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |         lines = [_.replace("\n", "") for _ in fhandle.readlines()] | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if any(_ for _ in lines if re.match(uri, _)): | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     url_paths_found = False | 
					
						
							|  |  |  |     last_elem_line_index = -1 | 
					
						
							|  |  |  |     for i, line in enumerate(lines): | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         if line.startswith("url_paths"): | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  |             url_paths_found = True | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         if url_paths_found and line.startswith("}"): | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  |             last_elem_line_index = i - 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     prev_line = lines[last_elem_line_index] | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     if not prev_line.endswith("{") and not prev_line.endswith(","): | 
					
						
							|  |  |  |         lines[last_elem_line_index] += "," | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  |     # generate url pattern | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     if api_protocol == "rest-json": | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |         new_line = '    "{0}/.*$": response.dispatch,' | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |         new_line = f'    "{{0}}{uri}$": {service_class}Response.dispatch,' | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  |     if new_line in lines: | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  |     lines.insert(last_elem_line_index + 1, new_line) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     body = "\n".join(lines) + "\n" | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     with open(path, "w", encoding="utf-8") as fhandle: | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |         fhandle.write(body) | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  | def insert_codes(service, operation, api_protocol): | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     """Create the responses.py and models.py for the service and operation.""" | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |     escaped_service = get_escaped_service(service) | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  |     func_in_responses = get_function_in_responses(service, operation, api_protocol) | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  |     func_in_models = get_function_in_models(service, operation) | 
					
						
							|  |  |  |     # edit responses.py | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |     responses_path = f"moto/{escaped_service}/responses.py" | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     print_progress("inserting code", responses_path, "green") | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  |     insert_code_to_class(responses_path, BaseResponse, func_in_responses) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # insert template | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     if api_protocol == "query": | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  |         template = get_response_query_template(service, operation) | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |         with open(responses_path, encoding="utf-8") as fhandle: | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |             lines = [_[:-1] for _ in fhandle.readlines()] | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  |         lines += template.splitlines() | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |         with open(responses_path, "w", encoding="utf-8") as fhandle: | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |             fhandle.write("\n".join(lines)) | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # edit models.py | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |     models_path = f"moto/{escaped_service}/models.py" | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     print_progress("inserting code", models_path, "green") | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  |     insert_code_to_class(models_path, BaseBackend, func_in_models) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  |     # edit urls.py | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  |     insert_url(service, operation, api_protocol) | 
					
						
							| 
									
										
										
										
											2017-10-02 07:17:02 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  | @click.command() | 
					
						
							|  |  |  | def main(): | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  |     """Create basic files needed for the user's choice of service and op.""" | 
					
						
							| 
									
										
										
										
											2017-09-20 02:10:10 +09:00
										 |  |  |     service, operation = select_service_and_operation() | 
					
						
							| 
									
										
										
										
											2021-10-30 18:18:44 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # pylint: disable=protected-access | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     api_protocol = boto3.client(service)._service_model.metadata["protocol"] | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |     initialize_service(service, api_protocol) | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |     if api_protocol in ["query", "json", "rest-json"]: | 
					
						
							| 
									
										
										
										
											2017-10-25 03:45:39 +09:00
										 |  |  |         insert_codes(service, operation, api_protocol) | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |         print_progress( | 
					
						
							|  |  |  |             "skip inserting code", | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |             f'api protocol "{api_protocol}" is not supported', | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  |             "yellow", | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2017-09-20 04:36:11 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |     click.echo( | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |         "Remaining setup:\n" | 
					
						
							| 
									
										
										
										
											2021-09-30 13:00:02 -04:00
										 |  |  |         '- Add the mock into "docs/index.rst",\n' | 
					
						
							|  |  |  |         '- Add the mock into "IMPLEMENTATION_COVERAGE.md",\n' | 
					
						
							| 
									
										
										
										
											2021-10-30 06:09:44 -04:00
										 |  |  |         "- Run scripts/update_backend_index.py." | 
					
						
							| 
									
										
										
										
											2021-08-24 11:49:45 -04:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2017-09-26 17:33:19 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 08:46:05 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							| 
									
										
										
										
											2017-09-21 21:54:14 +09:00
										 |  |  |     main() |