class EnvironmentsListPresenter: def __init__(self, parent_view=None): self.parent_view = parent_view self.app_state_interactor = AppStateInteractor() app_settings.app_data_writer.signals.environment_added.connect( self.refresh) app_settings.app_data_writer.signals.environment_removed.connect( self.refresh) app_settings.app_data_writer.signals.environment_renamed.connect( self.refresh) def __get_combox_box(self): toolbar_actions = self.parent_view.tool_bar.actions() tags_list_action = next(act for act in toolbar_actions if act.text() == "Environmnents") return tags_list_action.defaultWidget() def on_env_changed(self, new_env): if not new_env: return self.app_state_interactor.update_selected_environment(new_env) def refresh(self): all_envs = app_settings.app_data_cache.get_environments() envs_list_field = self.__get_combox_box() selected_env = app_settings.app_data_cache.get_appstate_environment() envs_list_field.clear() for env in all_envs: envs_list_field.addItem(env.name) envs_list_field.setCurrentText(selected_env)
def __init__(self, parent_view=None): self.parent_view = parent_view self.app_state_interactor = AppStateInteractor() app_settings.app_data_writer.signals.environment_added.connect( self.refresh) app_settings.app_data_writer.signals.environment_removed.connect( self.refresh) app_settings.app_data_writer.signals.environment_renamed.connect( self.refresh)
class TagsListPresenter: def __init__(self, parent_view=None): self.parent_view = parent_view self.app_state_interactor = AppStateInteractor() app_settings.app_data_writer.signals.project_info_updated.connect(self.refresh) def __get_combox_box(self): toolbar_actions = self.parent_view.tool_bar.actions() tags_list_action = next(act for act in toolbar_actions if act.text() == 'Tags') return tags_list_action.defaultWidget() def on_tag_changed(self, tag_name): self.app_state_interactor.update_selected_tag(tag_name) def refresh(self): project_info = app_settings.app_data_reader.get_or_create_project_info() tags_list_field = self.__get_combox_box() selected_tag = tags_list_field.currentText() tags_list_field.clear() tags_list_field.addItem(DEFAULT_TAG) for tag in project_info.tags: tags_list_field.addItem(tag.name) tags_list_field.setCurrentText(selected_tag)
class CurlImporter: name: str = "Curl" input_type: str = "text" app_state_interactor = AppStateInteractor() def import_data(self, curl_command): try: ctx: ParsedContext = uncurl.parse_context(curl_command) api_call = self.__extract_api_call(ctx) return None, [api_call] except BaseException: raise SyntaxError("Unable to parse curl command") def __extract_api_call(self, ctx): url, qs = split_url_qs(ctx.url.strip()) return ApiCall( title="Curl command", description=f"Imported from {url}", http_url=url, http_method=ctx.method, http_request_body=self.__extract_request_body(ctx), form_params=self.__extract_form_data(ctx), http_params=qs, http_headers=self.__extract_header_data(ctx.headers), sequence_number=self.app_state_interactor.update_sequence_number(), ) def __extract_header_data(self, headers): return {hk: DynamicStringData(value=hv) for hk, hv in headers.items()} def __extract_form_data(self, ctx: ParsedContext): form_content_type = (ctx.headers.get( "Content-Type", None) == "application/x-www-form-urlencoded") if form_content_type: return { fk: DynamicStringData(value=",".join(fv)) for fk, fv in parse.parse_qs(ctx.data).items() } else: return {} def __extract_request_body(self, ctx: ParsedContext): form_content_type = (ctx.headers.get( "Content-Type", None) == "application/x-www-form-urlencoded") if not form_content_type: return ctx.data or "" else: return ""
class PostmanCollectionImporter: name: str = "Postman Collections v2" input_type: str = "file" var_selector = r"({{(\w+)}})+" app_state_interactor = AppStateInteractor() def import_data(self, file_path): self.__validate_file(file_path) json_data = json.loads(Path(file_path).read_text()) postman_data: PostmanDataModel = cattr.structure( json_data, PostmanDataModel) return None, [ self.__extract_api_call(item, sub_item) for item in postman_data.item for sub_item in item.item ] def __extract_api_call(self, item: PostmanItem, sub_item: PostmanSubItem): api_call = ApiCall( tags=[item.name], title=sub_item.name, description=sub_item.name, http_url=self.__internal_variables( sub_item.request.url.get('raw')), http_method=sub_item.request.method, http_request_body=self.__internal_variables( sub_item.request.body.get('raw')), http_headers={ k: DynamicStringData(display_text=self.__internal_variables(v)) for k, v in kv_list_to_dict(sub_item.request.header).items() }, sequence_number=self.app_state_interactor.update_sequence_number()) return api_call def __internal_variables(self, input_str): return re.sub(self.var_selector, r"${\2}", input_str, count=0) def __validate_file(self, file_path): if not (Path(file_path).exists() and Path(file_path).is_file()): raise FileNotFoundError( f"Path {file_path} should be a valid file path")
class OpenApiV3Importer: name: str = "OpenApi(v3)" input_type: str = "file" app_state_interactor = AppStateInteractor() def import_data(self, file_path): openapi_spec: ResolvingParser = self.__load_swagger_spec(file_path) project_info = self.__extract_project_info(openapi_spec) base_path = project_info.servers[0] if project_info.servers else "" api_calls = self.__extract_api_calls(base_path, openapi_spec) return project_info, api_calls def __load_swagger_spec(self, file_path): return ResolvingParser(url=file_path) def __extract_project_info(self, openapi_spec): openapi_info = openapi_spec.specification["info"] openapi_tags = openapi_spec.specification["tags"] openapi_servers = openapi_spec.specification["servers"] info = ProjectInfo( info=openapi_info["description"].strip(), title=openapi_info["title"].strip(), version=openapi_info["version"].strip(), contact_email=openapi_info.get("contact", {}).get("email", "").strip(), contact_name=openapi_info.get("contact", {}).get("name", "").strip(), tags=[ TagInfo(t["name"].strip(), t["description"].strip()) for t in openapi_tags ], servers=[server.get("url", "") for server in openapi_servers]) return info def __extract_api_calls(self, base_path, openapi_spec): openapi_paths = openapi_spec.specification["paths"] return [ self.__convert_to_api_call(base_path, path, path_spec, api_method, api_method_spec, content_type, schema) for path, path_spec in openapi_paths.items() for api_method, api_method_spec in path_spec.items() for content_type, schema in self.__request_content_types( api_method_spec).items() if api_method.strip().lower() in VALID_METHODS_OPENAPI_V3 ] def __request_content_types(self, api_method_spec): has_request_body = type(api_method_spec) is dict \ and api_method_spec.get("requestBody", False) \ and api_method_spec.get("requestBody").get("content", False) if not has_request_body: return {None: None} else: return api_method_spec["requestBody"]["content"] def __process_parameters(self, path_spec, api_method_spec): path_params = path_spec.get("parameters", []) method_params = api_method_spec.get("parameters", []) all_params = path_params + method_params data = sorted(all_params, key=itemgetter("in")) grouped_data = groupby(data, key=itemgetter("in")) def params(pv): return {p["name"]: DynamicStringData(display_text="") for p in pv} gp = {k: params(data) for k, data in grouped_data} return gp.get("header", {}), gp.get("query", {}), gp.get("form", {}) def __convert_to_api_call(self, base_path, path, path_spec, api_method, api_method_spec, content_type, schema): logging.info(f"Converting {api_method} - {content_type} - {path}") headers_params, query_params, form_params = self.__process_parameters( path_spec, api_method_spec) if content_type: headers_params["Content-Type"] = DynamicStringData( display_text=content_type) return ApiCall( tags=[t for t in api_method_spec.get("tags", [])], http_url=f"{base_path}{path}", http_method=api_method.upper(), title=api_method_spec.get("summary", ""), http_request_body=self.__extract_request_body( content_type, schema), description=api_method_spec.get("description", ""), http_headers=headers_params, http_params=query_params, form_params=form_params, sequence_number=self.app_state_interactor.update_sequence_number()) def __extract_request_body(self, content_type, schema): if schema: return json.dumps(json_from_schema(schema.get("schema"))) return ""
def __init__(self, parent_view=None): self.parent_view = parent_view self.app_state_interactor = AppStateInteractor() app_settings.app_data_writer.signals.project_info_updated.connect(self.refresh)
def __init__(self, parent_view): self.view = parent_view self.app_state_interactor = AppStateInteractor() self.view.btn_add_request.pressed.connect(self.on_btn_add_request)
class EmptyFramePresenter: def __init__(self, parent_view): self.view = parent_view self.app_state_interactor = AppStateInteractor() self.view.btn_add_request.pressed.connect(self.on_btn_add_request) def on_btn_add_request(self): api_call = ApiCall() api_call.http_url = "http://127.0.0.1:8000/get" api_call.http_method = "GET" api_call.title = "Get httpbin" api_call.description = "Httpbin call to get request data" api_call.sequence_number = self.app_state_interactor.update_sequence_number( ) api_call_interactor.add_api_call(api_call) self.focus_http_url() def on_btn_add_separator(self): api_call = ApiCall() api_call.title = "Separator" api_call.is_separator = True api_call.sequence_number = self.app_state_interactor.update_sequence_number( ) api_call_interactor.add_api_call(api_call) def focus_http_url(self): self.__select_text(self.view.txt_http_url) def focus_headers(self): self.view.tabWidget.setCurrentIndex(1) def focus_query_params(self): self.view.tabWidget.setCurrentIndex(2) def focus_form_params(self): self.view.tabWidget.setCurrentIndex(3) def focus_request_body(self): self.view.tabWidget.setCurrentIndex(4) def focus_mocked_response(self): self.view.tabWidget.setCurrentIndex(5) def focus_http_method(self): self.view.cmb_http_method.setFocus(True) self.view.cmb_http_method.showPopup() def focus_description(self): self.view.tabWidget.setCurrentIndex(0) self.__select_text(self.view.txt_api_title) def __select_text(self, txt_field): txt_field.setFocus(True) txt_field_value = txt_field.text() txt_field.setSelection(0, len(txt_field_value)) def display(self): """Called when all API calls are removed from list""" self.view.empty_frame.show()