def render_GET(self, request: Request) -> bytes: """ GET request for /thin_wallet/address_history/ Expects 'addresses[]' as request args 'addresses[]' is an array of address Returns an array of WalletIndex for each address :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') wallet_index = self.manager.tx_storage.wallet_index if not wallet_index: request.setResponseCode(503) return json.dumps({'success': False}, indent=4).encode('utf-8') addresses = request.args[b'addresses[]'] history = [] seen: Set[bytes] = set() for address_to_decode in addresses: address = address_to_decode.decode('utf-8') for tx_hash in wallet_index.get_from_address(address): tx = self.manager.tx_storage.get_transaction(tx_hash) if tx_hash not in seen: seen.add(tx_hash) history.append(tx.to_json_extended()) data = {'history': history} return json.dumps(data, indent=4).encode('utf-8')
def textReply(request: Request, status: int, message: str) -> bytes: """Replies to an HTTP request with a plain text message. The reply body is returned. """ request.setResponseCode(status) request.setHeader(b'Content-Type', b'text/plain; charset=UTF-8') return message.encode()
def emptyReply(request: Request) -> bytes: """Replies to an HTTP request with a no-body response. An empty bytes sequence is returned. """ request.setResponseCode(204) request.setHeader(b'Content-Length', b'0') return b''
def render_POST(self, request: Request) -> bytes: """ POST request for /thin_wallet/address_history/ It has the same behaviour as the GET request but when using the GET we have a limit of addresses to put as query param, otherwise we end up reaching the HTTP content length limit """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'POST') if not self.manager.tx_storage.indexes.addresses: request.setResponseCode(503) return json.dumps({'success': False}, indent=4).encode('utf-8') raw_body_bytes = request.content.read() or b'' raw_body_str = raw_body_bytes.decode('utf-8') try: post_data = json.loads(raw_body_str) except json.JSONDecodeError: return get_missing_params_msg('invalid json') if 'addresses' not in post_data: return get_missing_params_msg('addresses') return self.get_address_history(post_data.get('addresses'), post_data.get('hash'))
def render_GET(self, request: Request) -> bytes: """ GET request for /thin_wallet/token/ Expects 'id' (hash) as GET parameter of the queried token :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') if not self.manager.tx_storage.tokens_index: request.setResponseCode(503) return json.dumps({'success': False}).encode('utf-8') if b'id' in request.args: try: token_uid_str = request.args[b'id'][0].decode('utf-8') token_uid = bytes.fromhex(token_uid_str) except (ValueError, AttributeError): return json.dumps({ 'success': False, 'message': 'Invalid token id' }).encode('utf-8') data = self.get_one_token_data(token_uid) else: data = self.get_list_token_data() return json.dumps(data).encode('utf-8')
def response_get_process(self, session: Dict, session_id: str, request: Request): request.setResponseCode(OK) if session is None: session = {'auth': False} yield self.cache_interface.set(session_id, session) request.write(ujson.dumps(session).encode('utf-8')) request.finish() return None
def render_GET(self, request: Request) -> bytes: """ GET request for /thin_wallet/token/ Expects 'id' (hash) as GET parameter of the queried token :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') if not self.manager.tx_storage.tokens_index: request.setResponseCode(503) return json.dumps({'success': False}).encode('utf-8') if b'id' not in request.args: return get_missing_params_msg('id') try: token_uid_str = request.args[b'id'][0].decode('utf-8') token_uid = bytes.fromhex(token_uid_str) except (ValueError, AttributeError): return json.dumps({ 'success': False, 'message': 'Invalid token id' }).encode('utf-8') try: token_info = self.manager.tx_storage.tokens_index.get_token_info( token_uid) except KeyError: return json.dumps({ 'success': False, 'message': 'Unknown token' }).encode('utf-8') mint = [] melt = [] transactions_count = self.manager.tx_storage.tokens_index.get_transactions_count( token_uid) for tx_hash, index in token_info.mint: mint.append({'tx_id': tx_hash.hex(), 'index': index}) for tx_hash, index in token_info.melt: melt.append({'tx_id': tx_hash.hex(), 'index': index}) data = { 'name': token_info.name, 'symbol': token_info.symbol, 'success': True, 'mint': mint, 'melt': melt, 'total': token_info.total, 'transactions_count': transactions_count, } return json.dumps(data).encode('utf-8')
def read_json_body(self, request: Request): try: result = ujson.loads(request.content.getvalue()) except ValueError: request.setResponseCode(BAD_REQUEST) request.write(ujson.dumps({'status': 'error', 'message': 'Invalid JSON'}).encode('utf-8')) request.finish() result = None return result
def response_delete_process(self, session: Dict, session_id: str, request: Request): if session is None or session.get('auth', False) is not True: request.setResponseCode(UNAUTHORIZED) request.write(ujson.dumps({'status': 'error', 'message': 'Unauthorized'}).encode('utf-8')) else: yield self.cache_interface.set(session_id, {'auth': False}) request.setResponseCode(OK) request.write(ujson.dumps({'status': 'success'}).encode('utf-8')) request.finish() return None
def render(self, request: Request): request.setHeader(b'Content-Type', b'application/json') if self.config.get('set_cors'): request.setHeader(b'Access-Control-Allow-Origin', b'*') request.setHeader(b'Access-Control-Allow-Methods', b'GET,POST,PUT,DELETE') request.setHeader(b'Access-Control-Allow-Headers', b'x-prototype-version,x-requested-with') request.setHeader(b'Access-Control-Max-Age', b'2520') callback_name = 'response_%s_process' % request.method.lower().decode('ascii') callback = getattr(self, callback_name, None) if not callback: request.setResponseCode(NOT_ALLOWED) return b'Method not allowed' session_id = request.getSession().uid d = self.cache_interface.get(session_id) d.addCallback(callback, session_id, request) return NOT_DONE_YET
def response_post_process(self, session: Dict, session_id: str, request: Request): body = self.read_json_body(request) if session and session.get('auth', False) is True: request.setResponseCode(OK) request.write(ujson.dumps({'status': 'success'}).encode('utf-8')) elif body and body.get('login') and body.get('password'): password_hash = hashlib.md5(body.get('password').encode('utf-8')).hexdigest() selector = Selector(Target.user, {'login': body.get('login'), 'password_hash': password_hash}, 1) database_request = DataBasePackage(Method.select, selector=selector) response = yield self.db_interface(database_request) result = DataBaseResponse.deserialize(response) if result.length == 0: request.setResponseCode(UNAUTHORIZED) request.write(ujson.dumps({'status': 'error', 'message': 'No match'}).encode('utf-8')) else: user = result.objects[0] session = {'login': user.login, 'auth': True} yield self.cache_interface.set(session_id, session) request.write(ujson.dumps({'status': 'success'}).encode('utf-8')) else: request.setResponseCode(BAD_REQUEST) request.write(ujson.dumps({'status': 'error', 'message': 'Invalid request'}).encode('utf-8')) request.finish() return None
def render_GET(self, request: Request) -> bytes: """ GET request for /thin_wallet/token_history/ Expects as GET parameter of the queried token: - 'id': token uid the history is being requested - 'count': int, to indicate the quantity of elements we should return - 'hash': string, the hash reference we are in the pagination - 'timestamp': int, the timestamp reference we are in the pagination - 'page': 'previous' or 'next', to indicate if the user wants after or before the hash reference :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') if not self.manager.tx_storage.tokens_index: request.setResponseCode(503) return json.dumps({'success': False}).encode('utf-8') if b'id' not in request.args: return get_missing_params_msg('id') try: token_uid_str = request.args[b'id'][0].decode('utf-8') token_uid = bytes.fromhex(token_uid_str) except (ValueError, AttributeError): return json.dumps({ 'success': False, 'message': 'Invalid token id' }).encode('utf-8') if b'count' not in request.args: return get_missing_params_msg('count') count = min(int(request.args[b'count'][0]), settings.MAX_TX_COUNT) if b'hash' in request.args: if b'timestamp' not in request.args: return get_missing_params_msg('timestamp') if b'page' not in request.args: return get_missing_params_msg('page') ref_hash = request.args[b'hash'][0].decode('utf-8') ref_timestamp = int(request.args[b'timestamp'][0].decode('utf-8')) page = request.args[b'page'][0].decode('utf-8') if page == 'previous': elements, has_more = self.manager.tx_storage.tokens_index.get_newer_transactions( token_uid, ref_timestamp, bytes.fromhex(ref_hash), count) else: elements, has_more = self.manager.tx_storage.tokens_index.get_older_transactions( token_uid, ref_timestamp, bytes.fromhex(ref_hash), count) else: elements, has_more = self.manager.tx_storage.tokens_index.get_newest_transactions( token_uid, count) transactions = [ self.manager.tx_storage.get_transaction(element) for element in elements ] serialized = [tx.to_json_extended() for tx in transactions] data = { 'success': True, 'transactions': serialized, 'has_more': has_more, } return json.dumps(data).encode('utf-8')
def render_GET(self, request: Request) -> bytes: """ GET request for /thin_wallet/address_balance/ Expects 'address' as request args :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') wallet_index = self.manager.tx_storage.wallet_index tokens_index = self.manager.tx_storage.tokens_index if not wallet_index or not tokens_index: request.setResponseCode(503) return json.dumps({'success': False}, indent=4).encode('utf-8') if b'address' in request.args: requested_address = request.args[b'address'][0].decode('utf-8') else: return get_missing_params_msg('address') try: # Check if address is valid decode_address(requested_address) except InvalidAddress: return json.dumps({ 'success': False, 'message': 'Invalid \'address\' parameter' }).encode('utf-8') tokens_data: Dict[bytes, TokenData] = defaultdict(TokenData) tx_hashes = wallet_index.get_from_address(requested_address) for tx_hash in tx_hashes: tx = self.manager.tx_storage.get_transaction(tx_hash) meta = tx.get_metadata(force_reload=True) if not meta.voided_by: # We consider the spent/received values only if is not voided by for tx_input in tx.inputs: tx2 = self.manager.tx_storage.get_transaction( tx_input.tx_id) tx2_output = tx2.outputs[tx_input.index] if self.has_address(tx2_output, requested_address): # We just consider the address that was requested token_uid = tx2.get_token_uid( tx2_output.get_token_index()) tokens_data[token_uid].spent += tx2_output.value for tx_output in tx.outputs: if self.has_address(tx_output, requested_address): # We just consider the address that was requested token_uid = tx.get_token_uid( tx_output.get_token_index()) tokens_data[token_uid].received += tx_output.value return_tokens_data: Dict[str, Dict[str, Any]] = {} for token_uid in tokens_data.keys(): if token_uid == settings.HATHOR_TOKEN_UID: tokens_data[token_uid].name = settings.HATHOR_TOKEN_NAME tokens_data[token_uid].symbol = settings.HATHOR_TOKEN_SYMBOL else: try: token_info = tokens_index.get_token_info(token_uid) tokens_data[token_uid].name = token_info.name tokens_data[token_uid].symbol = token_info.symbol except KeyError: # Should never get here because this token appears in our wallet index # But better than get a 500 error tokens_data[ token_uid].name = '- (unable to fetch token information)' tokens_data[ token_uid].symbol = '- (unable to fetch token information)' return_tokens_data[ token_uid.hex()] = tokens_data[token_uid].to_dict() data = { 'success': True, 'total_transactions': len(tx_hashes), 'tokens_data': return_tokens_data } return json.dumps(data, indent=4).encode('utf-8')
def abort(self, request: Request, code: int, message: str): request.setResponseCode(code) error = {"code": code, "message": message} return json.dumps(error).encode()
def response_get_process(self, session: Dict, session_id: str, request: Request): print(rerequest.path) request.setResponseCode(OK) request.write(b'test') request.finish() return None
async def _handle_dispatch( self, root_span: Span, request: Request, log: NotificationLoggerAdapter, notif: Notification, context: NotificationContext, ) -> None: """ Actually handle the dispatch of notifications to devices, sequentially for simplicity. root_span: the OpenTracing span request: the Twisted Web Request log: the logger to use notif: the notification to dispatch context: the context of the notification """ try: rejected = [] for d in notif.devices: NOTIFS_RECEIVED_DEVICE_PUSH_COUNTER.inc() appid = d.app_id found_pushkins = self.find_pushkins(appid) if len(found_pushkins) == 0: log.warning("Got notification for unknown app ID %s", appid) rejected.append(d.pushkey) continue if len(found_pushkins) > 1: log.warning("Got notification for an ambiguous app ID %s", appid) rejected.append(d.pushkey) continue pushkin = found_pushkins[0] log.debug("Sending push to pushkin %s for app ID %s", pushkin.name, appid) NOTIFS_BY_PUSHKIN.labels(pushkin.name).inc() result = await pushkin.dispatch_notification(notif, d, context) if not isinstance(result, list): raise TypeError("Pushkin should return list.") rejected += result request.write(json.dumps({"rejected": rejected}).encode()) if rejected: log.info( "Successfully delivered notifications with %d rejected pushkeys", len(rejected), ) except NotificationDispatchException: request.setResponseCode(502) log.warning("Failed to dispatch notification.", exc_info=True) except Exception: request.setResponseCode(500) log.error("Exception whilst dispatching notification.", exc_info=True) finally: if not request._disconnected: request.finish() PUSHGATEWAY_HTTP_RESPONSES_COUNTER.labels(code=request.code).inc() root_span.set_tag(tags.HTTP_STATUS_CODE, request.code) req_time = time.perf_counter() - context.start_time if req_time > 0: # can be negative as perf_counter() may not be monotonic NOTIFY_HANDLE_HISTOGRAM.labels( code=request.code).observe(req_time) if not 200 <= request.code < 300: root_span.set_tag(tags.ERROR, True) root_span.finish()
def render_GET(self, request: Request) -> bytes: """ GET request for /thin_wallet/address_history/ If 'paginate' parameter exists, it calls the new resource method otherwise, it will call the old and deprecated one because it's a request from a wallet still in an older version Expects 'addresses[]' as request args, and 'hash' as optional args to be used in pagination 'addresses[]' is an array of address 'hash' is the hash of the first tx of the pagination to start the history Returns an array of WalletIndex for each address until the maximum number E.g. request: addresses: ['WYxpdgz11cGGPSdmQPcJVwnLsUu7w5hgjw', 'WSo6BtjdxSSs7FpSzXYgEXwKZ3643K5iSQ'] In the case where address 'WYxpdgz11cGGPSdmQPcJVwnLsUu7w5hgjw' has 3 txs [tx_id1, tx_id2, tx_id3] and address 'WSo6BtjdxSSs7FpSzXYgEXwKZ3643K5iSQ' also has 3 txs [tx_id4, tx_id5, tx_id6]. Return: { 'history': [array with 3 txs from first address and 2 txs from second address], 'has_more': True, indicating that there are more txs for this request 'first_address': 'WSo6BtjdxSSs7FpSzXYgEXwKZ3643K5iSQ', indicating that the next request should start with this address as first element of addresses array 'first_hash': tx_id6, indicating that the next request should start with this transaction } So we need to execute one more request to finish getting all transactions. Request: addresses: ['WSo6BtjdxSSs7FpSzXYgEXwKZ3643K5iSQ'] hash: tx_id6 Important note: different requests may return the same transaction for different addresses. We just validate if a transactions was already added in the same request, so e.g. the following case: 1. tx1 has outputs for addr1 and addr2; 2. Request to get [addr1, addr2]; 3. First response return txs only for addr1 including tx1; 4. New request to get the remaining txs for addr1 and the txs for addr2 (including tx1) In this case we would return tx1 for both requests because we don't have the txs returned previously. We could send in all requests the txs already returned but it does not make much difference now. :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') if not self.manager.tx_storage.wallet_index: request.setResponseCode(503) return json.dumps({'success': False}, indent=4).encode('utf-8') paginate = b'paginate' in request.args and request.args[b'paginate'][ 0].decode('utf-8') == 'true' if paginate: # New resource if b'addresses[]' not in request.args: return get_missing_params_msg('addresses[]') addresses = request.args[b'addresses[]'] ref_hash = None if b'hash' in request.args: # If hash parameter is in the request, it must be a valid hex ref_hash = request.args[b'hash'][0].decode('utf-8') return self.get_address_history( [address.decode('utf-8') for address in addresses], ref_hash) else: # Old and deprecated resource return self.deprecated_resource(request)
async def get(self, request: Request) -> None: try: if self.data is None: await asyncio.wait_for(self.read_task, timeout=30) if self.data is None: request.setResponseCode(503) request.write(b'Service temporarily unavailable') return raw_query = urlparse(request.uri).query query = parse_qs(raw_query) # Check for etag matching if self.etag_base: hasher = hashlib.sha256() hasher.update(self.etag_base) hasher.update(raw_query) if request.setETag(hasher.hexdigest().encode('utf-8')): # Etag matched; do not send a body return # decode query parameter releases = None if b'releases' in query: releases = set( self.release_aliases.get(release) for release in sanitize_query_list(query[b'releases'])) releases &= self.releases components = None if b'components' in query: components = sanitize_query_list(query[b'components']) components &= self.components architectures = None if b'architectures' in query: architectures = sanitize_query_list(query[b'architectures']) architectures.add('all') architectures &= self.architectures # generate filtered results def transform(item): result = item.copy() if releases is not None: result['packages'] = [ package for package in result['packages'] if package['release'] in releases ] if components is not None: result['packages'] = [ package for package in result['packages'] if package['component'] in components ] if architectures is not None: result['packages'] = [ package for package in result['packages'] if package['architecture'] in architectures ] return result, bool(result['packages']) result = [ value for value, pred in map(transform, self.data) if pred ] # deliver results request.setHeader(b'content-type', b'application/json; charset=utf-8') request.write(simplejson.dumps(result).encode('utf-8')) except Exception as e: log.err( "An exception occurred while handling request ({})".format(e)) request.setResponseCode(400) request.write('Bad request'.encode('utf-8')) finally: request.finish()
def handle_redirect(request: http.Request) -> None: request.setResponseCode(HTTPStatus.FOUND) request.setHeader("location", to) request.finish()
def render_GET(self, request: Request) -> bytes: """ GET request for /thin_wallet/token_history/ Expects as GET parameter of the queried token: - 'id': uid of token whose history is being requested - 'count': int, to indicate the quantity of elements we should return - 'hash': string, the hash reference we are in the pagination - 'timestamp': int, the timestamp reference we are in the pagination - 'page': 'previous' or 'next', to indicate if the user wants after or before the hash reference :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') if not self.manager.tx_storage.tokens_index: request.setResponseCode(503) return json.dumps({'success': False}).encode('utf-8') parsed = parse_get_arguments(request.args, ARGS) if not parsed['success']: return get_missing_params_msg(parsed['missing']) if b'id' not in request.args: return get_missing_params_msg('id') try: token_uid = bytes.fromhex(parsed['args']['id']) except (ValueError, AttributeError): return json.dumps({'success': False, 'message': 'Invalid token id'}).encode('utf-8') try: count = min(int(parsed['args']['count']), settings.MAX_TX_COUNT) except ValueError: return json.dumps({ 'success': False, 'message': 'Invalid \'count\' parameter, expected an int' }).encode('utf-8') if b'hash' in request.args: parsed = parse_get_arguments(request.args, ['timestamp', 'page', 'hash']) if not parsed['success']: return get_missing_params_msg(parsed['missing']) try: hash_bytes = bytes.fromhex(parsed['args']['hash']) except ValueError: return json.dumps({ 'success': False, 'message': 'Invalid \'hash\' parameter, could not decode hexadecimal' }).encode('utf-8') page = parsed['args']['page'] if page != 'previous' and page != 'next': return json.dumps({ 'success': False, 'message': 'Invalid \'page\' parameter, expected \'previous\' or \'next\'' }).encode('utf-8') try: ref_timestamp = int(parsed['args']['timestamp']) except ValueError: return json.dumps({ 'success': False, 'message': 'Invalid \'timestamp\' parameter, expected an int' }).encode('utf-8') if page == 'previous': elements, has_more = self.manager.tx_storage.tokens_index.get_newer_transactions( token_uid, ref_timestamp, hash_bytes, count) else: elements, has_more = self.manager.tx_storage.tokens_index.get_older_transactions( token_uid, ref_timestamp, hash_bytes, count) else: elements, has_more = self.manager.tx_storage.tokens_index.get_newest_transactions(token_uid, count) transactions = [self.manager.tx_storage.get_transaction(element) for element in elements] serialized = [tx.to_json_extended() for tx in transactions] data = { 'success': True, 'transactions': serialized, 'has_more': has_more, } return json.dumps(data).encode('utf-8')
def render_GET(self, request: Request) -> bytes: """ GET request for /thin_wallet/address_search/ Expects 'address' and 'count' as required request args 'token' is an optional parameter to define if the search wants an specific token only 'hash' and 'page' are optional args to be used in pagination 'address' is a base58 address string 'count' is an integer indicating the quantity of elements to be returned 'token' is the token uid in hex to be searched 'hash' is the first address of the pagination to start the history 'page' is either 'previous' or 'next' to indicate the page clicked Returns an array of WalletIndex until the count limit and the hash parameter for the next request, if has more """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') wallet_index = self.manager.tx_storage.wallet_index if not wallet_index: request.setResponseCode(503) return json.dumps({'success': False}, indent=4).encode('utf-8') if b'address' not in request.args: return get_missing_params_msg('address') if b'count' not in request.args: return get_missing_params_msg('count') try: address = request.args[b'address'][0].decode('utf-8') # Check if address is valid decode_address(address) except InvalidAddress: return json.dumps({ 'success': False, 'message': 'Invalid \'address\' parameter' }).encode('utf-8') try: count = min(int(request.args[b'count'][0]), settings.MAX_TX_COUNT) except ValueError: return json.dumps({ 'success': False, 'message': 'Invalid \'count\' parameter, expected an int' }).encode('utf-8') token_uid_bytes = None if b'token' in request.args: # It's an optional parameter, we just check if it's a valid hex token_uid = request.args[b'token'][0].decode('utf-8') try: token_uid_bytes = bytes.fromhex(token_uid) except ValueError: return json.dumps({ 'success': False, 'message': 'Token uid is not a valid hexadecimal value.' }).encode('utf-8') hashes = wallet_index.get_from_address(address) # XXX To do a timestamp sorting, so the pagination works better # we must get all transactions and sort them # This is not optimal for performance transactions = [] for tx_hash in hashes: tx = self.manager.tx_storage.get_transaction(tx_hash) if token_uid_bytes and not self.has_token_and_address( tx, address, token_uid_bytes): # Request wants to filter by token but tx does not have this token # so we don't add it to the transactions array continue transactions.append(tx.to_json_extended()) sorted_transactions = sorted(transactions, key=lambda tx: tx['timestamp'], reverse=True) if b'hash' in request.args: # It's a paginated request, so 'page' must also be in request.args if b'page' not in request.args: return get_missing_params_msg('page') page = request.args[b'page'][0].decode('utf-8') if page != 'previous' and page != 'next': # Invalid value for page parameter return json.dumps( { 'success': False, 'message': 'Invalid value for \'page\' parameter', }, indent=4).encode('utf-8') ref_hash = request.args[b'hash'][0].decode('utf-8') # Index where the reference hash is for ref_index, tx in enumerate(sorted_transactions): if tx['tx_id'] == ref_hash: break else: # ref_hash is not in the list return json.dumps( { 'success': False, 'message': 'Invalid hash {}'.format(ref_hash) }, indent=4).encode('utf-8') if page == 'next': # User clicked on 'Next' button, so the ref_hash is the last hash of the list # So I need to get the transactions after the ref start_index = ref_index + 1 end_index = start_index + count ret_transactions = sorted_transactions[start_index:end_index] # If has more transactions after this has_more = len(sorted_transactions) > end_index else: # User clicked on 'Previous' button, so the ref_hash is the first hash of the list # So I need to get the transactions before the ref end_index = ref_index start_index = max(end_index - count, 0) ret_transactions = sorted_transactions[start_index:end_index] # If has more transactions before this has_more = start_index > 0 else: ret_transactions = sorted_transactions[:count] has_more = len(sorted_transactions) > count data = { 'success': True, 'transactions': ret_transactions, 'has_more': has_more, 'total': len(sorted_transactions), } return json.dumps(data, indent=4).encode('utf-8')
def _handle_request(self, request: Request) -> Union[int, bytes]: """ Actually handle the request. Args: request: The request, corresponding to a POST request. Returns: Either a str instance or NOT_DONE_YET. """ request_id = self._make_request_id() header_dict = { k.decode(): v[0].decode() for k, v in request.requestHeaders.getAllRawHeaders() } # extract OpenTracing scope from the HTTP headers span_ctx = self.sygnal.tracer.extract(Format.HTTP_HEADERS, header_dict) span_tags = { tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER, "request_id": request_id, } root_span = self.sygnal.tracer.start_span("pushgateway_v1_notify", child_of=span_ctx, tags=span_tags) # if this is True, we will not close the root_span at the end of this # function. root_span_accounted_for = False try: context = NotificationContext(request_id, root_span, time.perf_counter()) log = NotificationLoggerAdapter(logger, {"request_id": request_id}) try: body = json_decoder.decode( request.content.read().decode("utf-8")) except Exception as exc: msg = "Expected JSON request body" log.warning(msg, exc_info=exc) root_span.log_kv({logs.EVENT: "error", "error.object": exc}) request.setResponseCode(400) return msg.encode() if "notification" not in body or not isinstance( body["notification"], dict): msg = "Invalid notification: expecting object in 'notification' key" log.warning(msg) root_span.log_kv({logs.EVENT: "error", "message": msg}) request.setResponseCode(400) return msg.encode() try: notif = Notification(body["notification"]) except InvalidNotificationException as e: log.exception("Invalid notification") request.setResponseCode(400) root_span.log_kv({logs.EVENT: "error", "error.object": e}) return str(e).encode() if notif.event_id is not None: root_span.set_tag("event_id", notif.event_id) # track whether the notification was passed with content root_span.set_tag("has_content", notif.content is not None) NOTIFS_RECEIVED_COUNTER.inc() if len(notif.devices) == 0: msg = "No devices in notification" log.warning(msg) request.setResponseCode(400) return msg.encode() root_span_accounted_for = True async def cb(): with REQUESTS_IN_FLIGHT_GUAGE.labels( self.__class__.__name__).track_inprogress(): await self._handle_dispatch(root_span, request, log, notif, context) ensureDeferred(cb()) # we have to try and send the notifications first, # so we can find out which ones to reject return NOT_DONE_YET except Exception as exc_val: root_span.set_tag(tags.ERROR, True) # [2] corresponds to the traceback trace = traceback.format_tb(sys.exc_info()[2]) root_span.log_kv({ logs.EVENT: tags.ERROR, logs.MESSAGE: str(exc_val), logs.ERROR_OBJECT: exc_val, logs.ERROR_KIND: type(exc_val), logs.STACK: trace, }) raise finally: if not root_span_accounted_for: root_span.finish()