async def test_cancel_subscriptions(app, client, m_influx, values_result): m_influx.query = AsyncMock(return_value=values_result) signal = features.get(app, sse.ShutdownAlert).shutdown_signal async def close_after(delay): await asyncio.sleep(delay) signal.set() await asyncio.gather( client.get('/sse/values', params=urlencode({'measurement': 'm'}, doseq=True)), close_after(0.1))
def __init__(self, app: web.Application): super().__init__(app) self._address = 'simulation:1234' self._data_callbacks = set() self._event_callbacks = set() # an ErrorCode will be returned # a None value will cause no response to be returned self.next_error: list[Union[ErrorCode, None]] = [] self._start_time = datetime.now() self._codec: codec.Codec = features.get(app, key='sim_codec') self._id_counter = count(start=const.USER_NID_START) self._blocks: dict[int, FirmwareBlock] = default_blocks()
async def test_last_values_sse(app, client, m_influx, last_values_result): m_influx.query = AsyncMock(return_value=last_values_result) signal = features.get(app, sse.ShutdownAlert).shutdown_signal expected = [ { 'field': 'val1', 'time': 1556527890131178000, 'value': 0, }, { 'field': 'val2', 'time': 1556527890131178000, 'value': 100, }, { 'field': 'val_none', 'time': None, 'value': None, }, ] expected_len = len(json.dumps(expected)) prefix_len = len('data: ') async with client.get('/sse/last_values', params=urlencode( { 'measurement': 'sparkey', 'fields': ['val1', 'val2', 'val_none'] }, doseq=True)) as resp: resp_values = [] while len(resp_values) < 3: read_val = (await resp.content.read(prefix_len + expected_len)).decode() # Skip new line characters if read_val.rstrip(): resp_values.append(json.loads(read_val[prefix_len:])) assert resp_values == [expected, expected, expected] signal.set() await asyncio.sleep(0.1) assert resp.status == 200
def get_data_writer(app) -> InfluxWriter: return features.get(app, InfluxWriter)
def get_client(app) -> QueryClient: return features.get(app, QueryClient)
def get_savepoints(app: web.Application) -> 'CouchDBConfig': return features.get(app, key='savepoints')
def get_datastore(app: web.Application) -> TwinKeyDict: return features.get(app, CouchDBBlockStore)
def fget(app: web.Application) -> SparkConnection: return features.get(app, SparkConnection)
def get_converter(app: web.Application) -> UnitConverter: return features.get(app, UnitConverter)
async def subscribe_last_values(request: web.Request) -> web.Response: """ --- tags: - History summary: Subscribe to updates of latest value in each field. operationId: history.sse.last_values produces: - application/json parameters: - in: query name: database schema: type: string required: false example: "brewblox" - in: query name: measurement schema: type: string required: true example: "sparkey" - in: query name: fields schema: type: list required: true example: ["actuator-1/value"] - in: query name: duration schema: type: string required: false """ client = influx.get_client(request.app) params = { k: request.query.get(k) for k in [ 'database', 'measurement', 'duration', ] } params['fields'] = request.query.getall('fields') alert: ShutdownAlert = features.get(request.app, ShutdownAlert) poll_interval = request.app['config']['poll_interval'] def check_shutdown(): if alert.shutdown_signal.is_set(): raise asyncio.CancelledError() async with sse_response(request, headers=_cors_headers(request)) as resp: while True: try: check_shutdown() data = await queries.select_last_values(client, **params) await resp.send(json.dumps(data)) check_shutdown() await asyncio.sleep(poll_interval) except asyncio.CancelledError: return resp except Exception as ex: msg = f'Exiting last_values SSE with error: {strex(ex)}' LOGGER.error(msg) break return resp
def get_conduit(app: web.Application) -> 'SparkConduit': return features.get(app, SparkConduit)
def sim_cdc(app) -> Codec: return features.get(app, key='sim_codec')
def get_publisher(app: web.Application): return features.get(app, SSEPublisher)
def fget(app: web.Application) -> MqttApi: return features.get(app, MqttApi)
def fget(app: web.Application): return features.get(app, MQTTDataRelay)
def get_client(app: web.Application) -> HTTPClient: return features.get(app, HTTPClient)
def cmder(app): return features.get(app, commander.SparkCommander)
async def subscribe_values(request: web.Request) -> web.Response: """ --- tags: - History summary: subscribe to InfluxDB updates operationId: history.sse.values produces: - application/json parameters: - in: query name: database schema: type: string required: false example: "brewblox" - in: query name: measurement schema: type: string required: true example: "spark" - in: query name: fields schema: type: list required: false example: ["*"] - in: query name: approx_points schema: type: int required: false example: 100 - in: query name: start schema: type: string required: false - in: query name: duration schema: type: string required: false - in: query name: end schema: type: string required: false """ client = influx.get_client(request.app) params = { k: request.query.get(k) for k in [ 'database', 'measurement', 'approx_points', 'start', 'duration', 'end', ] if k in request.query } if 'fields' in request.query: params['fields'] = request.query.getall('fields') params = await queries.configure_params(client, **params) open_ended = _check_open_ended(params) alert: ShutdownAlert = features.get(request.app, ShutdownAlert) poll_interval = request.app['config']['poll_interval'] def check_shutdown(): if alert.shutdown_signal.is_set(): raise asyncio.CancelledError() async with sse_response(request, headers=_cors_headers(request)) as resp: while True: try: check_shutdown() query = queries.build_query(params) data = await queries.run_query(client, query, params) if data.get('values'): await resp.send(json.dumps(data)) # Reset time frame for subsequent updates params['start'] = data['values'][-1][0] + 1 params.pop('duration', None) if not open_ended: break check_shutdown() await asyncio.sleep(poll_interval) except asyncio.CancelledError: return resp except Exception as ex: msg = f'Exiting values SSE with error: {strex(ex)}' LOGGER.error(msg) break return resp
def get_broadcaster(app: web.Application) -> Broadcaster: return features.get(app, Broadcaster)
def fget(app: web.Application) -> SparkSynchronization: return features.get(app, SparkSynchronization)
def fget(app: web.Application) -> SocketCloser: return features.get(app, SocketCloser)
def fget(app: web.Application) -> SparkCommander: return features.get(app, SparkCommander)
def get_config(app: web.Application) -> 'CouchDBConfig': return features.get(app, key='config')
def get_commander(app: web.Application): return features.get(app, SparkCommander)
def fget(app: web.Application) -> SparkController: return features.get(app, SparkController)
def fget(app: web.Application) -> ServiceStatus: return features.get(app, ServiceStatus)