hat.event.manager.rest
1import collections 2import functools 3 4import aiohttp.web 5 6from hat import aio 7from hat import json 8from hat import juggler 9from hat.drivers import net 10 11from hat.event import eventer 12from hat.event.manager import common 13 14 15async def run(addr: net.StreamAddress, 16 client_name: str, 17 client_token: str | None, 18 http_addr: net.TcpAddress): 19 async_group = aio.Group() 20 21 try: 22 client = await eventer.connect(addr=addr, 23 client_name=client_name, 24 client_token=client_token) 25 await _bind_resource(async_group, client) 26 27 server = await _create_web_server(http_addr=http_addr, 28 client=client) 29 await _bind_resource(async_group, server) 30 31 await async_group.wait_closing() 32 33 finally: 34 await aio.uncancellable(async_group.async_close()) 35 36 37async def _create_web_server(http_addr, client): 38 query_routes = [ 39 aiohttp.web.get( 40 f'/query/{path}', 41 functools.partial(_process_get_query, parse_params, client)) 42 for path, parse_params in [('latest', _parse_latest_params), 43 ('timeseries', _parse_timeseries_params), 44 ('server', _parse_server_params)]] 45 46 return await juggler.listen( 47 host=http_addr.host, 48 port=http_addr.port, 49 ws_path=None, 50 index_path=None, 51 additional_routes=query_routes) 52 53 54async def _process_get_query(parse_params, client, req): 55 try: 56 body = await req.json() 57 params = parse_params(body) 58 59 except Exception as e: 60 raise aiohttp.web.HTTPBadRequest(text=f"error reading params: {e}") 61 62 try: 63 result = await client.query(params) 64 65 return aiohttp.web.Response( 66 content_type='application/json', 67 text=json.encode(common.query_result_to_json(result))) 68 69 except Exception as e: 70 raise aiohttp.web.HTTPInternalServerError(text=f"query error: {e}") 71 72 73def _parse_latest_params(params): 74 event_types = (_parse_event_types(params['event_types']) 75 if 'event_types' in params else None) 76 77 return common.QueryLatestParams(event_types=event_types) 78 79 80def _parse_timeseries_params(params): 81 event_types = (_parse_event_types(params['event_types']) 82 if 'event_types' in params else None) 83 t_from = _parse_timestamp(params['t_from']) if 't_from' in params else None 84 t_to = _parse_timestamp(params['t_to']) if 't_to' in params else None 85 source_t_from = (_parse_timestamp(params['source_t_from']) 86 if 'source_t_from' in params else None) 87 source_t_to = (_parse_timestamp(params['source_t_to']) 88 if 'source_t_to' in params else None) 89 order = common.Order(params.get('order', 'descending')) 90 order_by = common.OrderBy(params.get('order_by', 'timestamp')) 91 max_results = (_parse_max_results(params['max_results']) 92 if 'max_results' in params else None) 93 last_event_id = (_parse_event_id(params['last_event_id']) 94 if 'last_event_id' in params else None) 95 96 return common.QueryTimeseriesParams(event_types=event_types, 97 t_from=t_from, 98 t_to=t_to, 99 source_t_from=source_t_from, 100 source_t_to=source_t_to, 101 order=order, 102 order_by=order_by, 103 max_results=max_results, 104 last_event_id=last_event_id) 105 106 107def _parse_server_params(params): 108 server_id = _parse_server_id(params['server_id']) 109 persisted = (_parse_persisted(params['persisted']) 110 if 'persisted' in params else False) 111 max_results = (_parse_max_results(params['max_results']) 112 if 'max_results' in params else None) 113 last_event_id = (_parse_event_id(params['last_event_id']) 114 if 'last_event_id' in params else None) 115 116 return common.QueryServerParams(server_id=server_id, 117 persisted=persisted, 118 max_results=max_results, 119 last_event_id=last_event_id) 120 121 122def _parse_event_types(event_types): 123 result = collections.deque() 124 125 for event_type in event_types: 126 event_type = tuple(event_type) 127 128 for segment in event_type: 129 if not isinstance(segment, str): 130 raise Exception('invalid event type') 131 132 result.append(event_type) 133 134 return result 135 136 137def _parse_server_id(server_id): 138 if not isinstance(server_id, int): 139 raise Exception('invalid server id') 140 141 return int(server_id) 142 143 144def _parse_max_results(max_results): 145 if not isinstance(max_results, int): 146 raise Exception('invalid max results') 147 148 return int(max_results) 149 150 151def _parse_persisted(persisted): 152 if not isinstance(persisted, bool): 153 raise Exception('invalid persisted') 154 155 return persisted 156 157 158def _parse_timestamp(t): 159 if not isinstance(t['s'], int): 160 raise Exception('invalid timestamp seconds') 161 162 if not isinstance(t['us'], int) or not (0 <= t['us'] < 1_000_000): 163 raise Exception('invalid timestamp micro seconds') 164 165 return common.Timestamp(s=int(t['s']), 166 us=int(t['us'])) 167 168 169def _parse_event_id(event_id): 170 if (not isinstance(event_id['server'], int) or 171 not isinstance(event_id['session'], int) or 172 not isinstance(event_id['instance'], int)): 173 raise Exception('invalid event id') 174 175 return common.EventId(server=int(event_id['server']), 176 session=int(event_id['session']), 177 instance=int(event_id['instance'])) 178 179 180async def _bind_resource(async_group, resource): 181 try: 182 async_group.spawn(aio.call_on_cancel, resource.async_close) 183 async_group.spawn(aio.call_on_done, resource.wait_closing(), 184 async_group.close) 185 186 except Exception: 187 await aio.uncancellable(resource.async_close()) 188 raise
async def
run( addr: hat.drivers.net.stream.TcpAddress | pathlib.Path, client_name: str, client_token: str | None, http_addr: hat.drivers.net.stream.TcpAddress):
16async def run(addr: net.StreamAddress, 17 client_name: str, 18 client_token: str | None, 19 http_addr: net.TcpAddress): 20 async_group = aio.Group() 21 22 try: 23 client = await eventer.connect(addr=addr, 24 client_name=client_name, 25 client_token=client_token) 26 await _bind_resource(async_group, client) 27 28 server = await _create_web_server(http_addr=http_addr, 29 client=client) 30 await _bind_resource(async_group, server) 31 32 await async_group.wait_closing() 33 34 finally: 35 await aio.uncancellable(async_group.async_close())