diff --git a/lyrebird/config/keywords.py b/lyrebird/config/keywords.py index b6dfa0117..ada234323 100644 --- a/lyrebird/config/keywords.py +++ b/lyrebird/config/keywords.py @@ -3,4 +3,5 @@ CONFIG_MOCK_REQUEST_SSR_MOCK_IN_BODY = 'mock.request.ssr.mock_in_body' CONFIG_MOCK_REQUEST_SSR_MOCK_BODY_KEY = 'mock.request.ssr.mock_body_key' CONFIG_MOCK_REQUEST_SSR_MOCK_IN_BODY_PARAM = 'mock.request.ssr.mock_in_body.param' +CONFIG_MOCK_REQUEST_SSR_MOCK_DATA_JSONPATH = 'mock.request.ssr.mock_data_jsonpath' diff --git a/lyrebird/event.py b/lyrebird/event.py index 16db7eb11..755c03611 100644 --- a/lyrebird/event.py +++ b/lyrebird/event.py @@ -159,11 +159,12 @@ def publish(self, channel, message, state=False, *args, **kwargs): context.application.socket_io.emit('event', {'id': event_id, 'channel': channel}) # Send report - application.reporter.report({ - 'action': 'event', - 'channel': channel, - 'event': message - }) + if application.reporter: + application.reporter.report({ + 'action': 'event', + 'channel': channel, + 'event': message + }) logger.debug(f'channel={channel} state={state}\nmessage:\n-----------\n{message}\n-----------\n') diff --git a/lyrebird/mock/dm/__init__.py b/lyrebird/mock/dm/__init__.py index c1bddc531..b4a9550e8 100644 --- a/lyrebird/mock/dm/__init__.py +++ b/lyrebird/mock/dm/__init__.py @@ -589,6 +589,14 @@ def add_data(self, parent_id, raw_data, **kwargs): return _data_id + def _flow_str_2_data(self, data_str): + if not isinstance(data_str, str): + return data_str + try: + return json.loads(data_str) + except Exception: + return data_str + def _flow_data_2_str(self, data): if isinstance(data, str): return data diff --git a/lyrebird/mock/dm/jsonpath.py b/lyrebird/mock/dm/jsonpath.py index 928fd8fbf..1efbd875f 100644 --- a/lyrebird/mock/dm/jsonpath.py +++ b/lyrebird/mock/dm/jsonpath.py @@ -9,7 +9,7 @@ class JSONPath: @staticmethod - def search(root, path:str): + def search(root, path:str, find_one=False): """ Find JSON object in object (a ``list`` or ``dict`` JSON object) file by JSONPath ``root`` or an object containing data @@ -44,6 +44,8 @@ def search(root, path:str): result = [] JSONPath._search_iterator(root, keys, result) + if find_one: + return result[0].node if result else None return result @staticmethod diff --git a/lyrebird/mock/extra_mock_server/server.py b/lyrebird/mock/extra_mock_server/server.py index 42752f631..76b31fe2a 100644 --- a/lyrebird/mock/extra_mock_server/server.py +++ b/lyrebird/mock/extra_mock_server/server.py @@ -47,6 +47,7 @@ async def send_request(context: LyrebirdProxyContext, target_url): headers = {k: v for k, v in request.headers.items() if k.lower() not in [ 'cache-control', 'host', 'transfer-encoding']} headers['Proxy-Raw-Headers'] = make_raw_headers_line(request) + headers['Lyrebird-Client-Address'] = request.remote request_body = None if request.body_exists: request_body = request.content diff --git a/lyrebird/mock/handlers/handler_context.py b/lyrebird/mock/handlers/handler_context.py index 2573dabeb..d127834c7 100644 --- a/lyrebird/mock/handlers/handler_context.py +++ b/lyrebird/mock/handlers/handler_context.py @@ -92,8 +92,10 @@ def _parse_request(self): DataHelper.origin2flow(self.request, output=_request, chain=self.request_chain) if self.request.headers.get('Lyrebird-Client-Address'): + # from extra mock server and mitmproxy self.client_address = self.request.headers.get('Lyrebird-Client-Address') else: + # from origin mock server self.client_address = self.request.remote_addr self.flow['client_address'] = self.client_address diff --git a/lyrebird/mock/handlers/mock_handler.py b/lyrebird/mock/handlers/mock_handler.py index 1c8787e10..4492e31e0 100644 --- a/lyrebird/mock/handlers/mock_handler.py +++ b/lyrebird/mock/handlers/mock_handler.py @@ -1,9 +1,11 @@ from pathlib import Path +from lyrebird.mock.dm.jsonpath import jsonpath +import json from lyrebird.mock import context from lyrebird.log import get_logger from .mock_data_helper import MockDataHelper from lyrebird.application import config -from lyrebird.config import CONFIG_MOCK_REQUEST_SSR_MOCK_IN_BODY, CONFIG_MOCK_REQUEST_SSR_MOCK_IN_BODY_PARAM, CONFIG_MOCK_REQUEST_SSR_MOCK_BODY_KEY +from lyrebird.config import CONFIG_MOCK_REQUEST_SSR_MOCK_IN_BODY, CONFIG_MOCK_REQUEST_SSR_MOCK_IN_BODY_PARAM, CONFIG_MOCK_REQUEST_SSR_MOCK_BODY_KEY, CONFIG_MOCK_REQUEST_SSR_MOCK_DATA_JSONPATH logger = get_logger() @@ -15,6 +17,22 @@ class MockHandler: """ + @staticmethod + def is_handle_ssr(handler_context): + if not config.get(CONFIG_MOCK_REQUEST_SSR_MOCK_IN_BODY): + return False + + key = config.get(CONFIG_MOCK_REQUEST_SSR_MOCK_IN_BODY_PARAM, 'is_ssr') + is_open_ssr = handler_context.flow['request']['query'].get(key) + if not is_open_ssr: + return False + + if 'data' in handler_context.flow['request'] and not isinstance(handler_context.flow['request']['data'], dict): + logger.warning('ssr but request body type is not Object') + return False + + return True + def handle(self, handler_context): hit_datas = context.application.data_manager.get_matched_data(handler_context.flow) if len(hit_datas) <= 0: @@ -25,31 +43,40 @@ def handle(self, handler_context): activated_groups = context.application.data_manager.activated_group activated_group = list(activated_groups.values())[0] - logger.info( - f' Hit Group:{activated_group.get("name")} - Data:{hit_data["name"]} \nURL: {handler_context.flow["request"]["url"]}\nGroupID:{activated_group["id"]} DataID:{hit_data["id"]}') - if config.get(CONFIG_MOCK_REQUEST_SSR_MOCK_IN_BODY): - key = config.get(CONFIG_MOCK_REQUEST_SSR_MOCK_IN_BODY_PARAM, 'is_ssr') - is_open_ssr = handler_context.flow['request']['query'].get(key) - if is_open_ssr: - if 'data' not in handler_context.flow['request']: - handler_context.flow['request']['data'] = {} + is_ssr = MockHandler.is_handle_ssr(handler_context) + if is_ssr: + logger.info( + f' Hit Group:{activated_group.get("name")} - Data:{hit_data["name"]} \nURL: {handler_context.flow["request"]["url"]}\nGroupID:{activated_group["id"]} DataID:{hit_data["id"]}') - resp_key = config.get(CONFIG_MOCK_REQUEST_SSR_MOCK_BODY_KEY, 'lyrebird_mock_response') + request_data_update = {} - handler_context.flow['request']['data'][resp_key] = hit_data['response'].get('data', '') + resp_key = config.get(CONFIG_MOCK_REQUEST_SSR_MOCK_BODY_KEY, 'lyrebird_mock_response') + request_data_update[resp_key] = hit_data['response'].get('data', '') - handler_context.set_request_edited() - handler_context.flow['request']['headers']['lyrebird'] = 'mock' + resp_jsonpath = config.get(CONFIG_MOCK_REQUEST_SSR_MOCK_DATA_JSONPATH) + if resp_jsonpath: + request_data_update[resp_key] = context.application.data_manager._flow_str_2_data(request_data_update[resp_key]) + target_response_data = jsonpath.search(request_data_update[resp_key], resp_jsonpath, find_one=True) + request_data_update[resp_key] = context.application.data_manager._flow_data_2_str(target_response_data) - handler_context.add_flow_action({ - 'id': 'mock_ssr', - 'name': Path(__file__).name, - 'group_id': activated_group['id'], - 'mock_id': hit_data['id'] - }) - return + if 'data' not in handler_context.flow['request']: + handler_context.flow['request']['data'] = {} + handler_context.flow['request']['data'].update(request_data_update) + handler_context.set_request_edited() + handler_context.flow['request']['headers']['lyrebird'] = 'mock' + + handler_context.add_flow_action({ + 'id': 'mock_ssr', + 'name': Path(__file__).name, + 'group_id': activated_group['id'], + 'mock_id': hit_data['id'] + }) + return + + logger.info( + f' Hit Group:{activated_group.get("name")} - Data:{hit_data["name"]} \nURL: {handler_context.flow["request"]["url"]}\nGroupID:{activated_group["id"]} DataID:{hit_data["id"]}') handler_context.flow['response']['code'] = hit_data['response']['code'] handler_context.flow['response']['headers'] = {k:v for k,v in hit_data['response']['headers'].items()} handler_context.flow['response']['data'] = hit_data['response'].get('data', '') diff --git a/lyrebird/plugins/plugin_loader.py b/lyrebird/plugins/plugin_loader.py index 4fe276493..df11fb57c 100644 --- a/lyrebird/plugins/plugin_loader.py +++ b/lyrebird/plugins/plugin_loader.py @@ -100,6 +100,7 @@ def load_all_from_ep(): logger.error(f'Load plugin {ep.name} failed: {ep.dist}\n{e}\n{traceback.format_exc()}') continue plugins[plugin.project_name] = plugin + logger.info(f'Load plugin from ep success: {plugin.project_name}') return plugins diff --git a/lyrebird/plugins/plugin_manager.py b/lyrebird/plugins/plugin_manager.py index ddae59e34..e68f352b8 100644 --- a/lyrebird/plugins/plugin_manager.py +++ b/lyrebird/plugins/plugin_manager.py @@ -21,6 +21,7 @@ def reload(self): for plugin_path in self.plugin_path_list: try: plugin = plugin_loader.load_from_path(plugin_path) + logger.info(f'Load plugin from path success: {plugin.project_name}') except: logger.error(f'Load plugin failed. Skip plugin : {plugin_path}\n{traceback.format_exc()}') continue