def availability(zip_code): beeline.add_context_field('zip code', zip_code) with beeline.tracer(name='load stores in zip code'): store_response = requests.get( 'https://www.riteaid.com/services/ext/v2/stores/getStores', params={ 'address': zip_code, 'attrFilter': 'PREF-112', 'fetchMechanismVersion': '2', 'radius': '50', }, ).json()['Data']['stores'] stores = [] threads = [] with beeline.tracer(name='get availability in stores'): for store_data in store_response: store_id = store_data['storeNumber'] t = threading.Thread(target=beeline.traced_thread(get_store_data_thread), args=(store_id, store_data, stores)) threads.append(t) t.start() for t in threads: t.join() beeline.add_context_field('possible_availability', sum(1 for i in stores if i['possible_availability'])) return jsonify(stores)
def fetch_google_token(): sub = session.get("profile", {}).get("user_id") or getattr(g, "sub", None) assert sub with beeline.tracer("db connection"): with beeline.tracer("open db connection"): connection = psycopg2.connect(os.environ["POSTGRES_DSN"]) try: with trace_cm(connection, "get google token transaction"): with trace_cm(connection.cursor(), "cursor") as cursor: with beeline.tracer("get google token query"): cursor.execute( """ SELECT token FROM google, link WHERE link.sub = %s AND link.link_name = 'google' AND link.issuer_sub = google.issuer_sub """, (sub,), ) if not cursor.rowcount: raise LookupError return json.loads(next(cursor)[0]) finally: connection.close()
def save(self): if not self.to_save: return pipeline = self.redis.pipeline(transaction=False) for location, vehicle in self.to_save: pipeline.geoadd('vehicle_location_locations', location.latlong.x, location.latlong.y, vehicle.id) redis_json = location.get_redis_json(vehicle) redis_json = json.dumps(redis_json, cls=DjangoJSONEncoder) pipeline.set(f'vehicle{vehicle.id}', redis_json, ex=900) with beeline.tracer(name="pipeline"): try: pipeline.execute() except redis.exceptions.ConnectionError: pass pipeline = self.redis.pipeline(transaction=False) for location, vehicle in self.to_save: pipeline.rpush(*location.get_appendage()) with beeline.tracer(name="pipeline"): try: pipeline.execute() except redis.exceptions.ConnectionError: pass self.to_save = []
def googlecheck(): sub = session.get("profile", {}).get("user_id") assert sub with beeline.tracer("db connection"): with beeline.tracer("open db connection"): connection = psycopg2.connect(os.environ["POSTGRES_DSN"]) try: with trace_cm(connection, "lookup google transaction"): with trace_cm(connection.cursor(), "cursor") as cursor: with beeline.tracer("lookup google query"): cursor.execute( """ SELECT google.token FROM link, google WHERE link.sub = %s AND link.issuer_sub = google.issuer_sub """, (sub,), ) has_google = cursor.rowcount == 1 finally: connection.close() return {"hasGoogle": has_google}
def telegramchats(): sub = session.get("profile", {}).get("user_id") assert sub with beeline.tracer("db connection"): with beeline.tracer("open db connection"): connection = psycopg2.connect(os.environ["POSTGRES_DSN"]) try: with trace_cm(connection, "lookup chats transaction"): with trace_cm(connection.cursor(), "cursor") as cursor: with beeline.tracer("lookup chats query"): cursor.execute( """ SELECT telegram.issuer_sub, chat_title FROM link, telegram WHERE link.sub = %s AND link.issuer_sub = telegram.issuer_sub """, (sub, ), ) return { "telegramGroups": [{ "issuerSub": issuer_sub, "chatTitle": chat_title } for issuer_sub, chat_title in cursor] } finally: connection.close()
def googlecallback(): token = oauth.google.authorize_access_token() update_google_token(token) userinfo = oauth.google.parse_id_token(token) with beeline.tracer("db connection"): with beeline.tracer("open db connection"): connection = psycopg2.connect(os.environ["POSTGRES_DSN"]) try: with trace_cm(connection, "link table maint transaction"): with trace_cm(connection.cursor(), "link table maint cursor") as cursor: with beeline.tracer("link table exists query"): cursor.execute( "SELECT table_name FROM information_schema.tables") if ("link", ) not in list(cursor): with beeline.tracer("link table create query"): cursor.execute(""" CREATE TABLE link (sub text, link_name text, issuer_sub text) """) with trace_cm(connection, "link google transaction"): with trace_cm(connection.cursor(), "link google cursor") as cursor: trace_call("google link exists query")(cursor.execute)( "SELECT sub FROM link WHERE sub = %s AND link_name = 'google'", (session["profile"]["user_id"], ), ) if cursor.rowcount: trace_call("update google link query")(cursor.execute)( """ UPDATE link SET issuer_sub = %s WHERE sub = %s AND link_name = 'google' """, (userinfo["sub"], session["profile"]["user_id"]), ) else: trace_call("insert google link query")(cursor.execute)( """ INSERT INTO link (sub, link_name, issuer_sub) VALUES (%s, 'google', %s) """, (session["profile"]["user_id"], userinfo["sub"]), ) finally: connection.close() return redirect("/dashboard")
def googletelegramlinks_get(): """List calendar chat links for the user.""" sub = session.get("profile", {}).get("user_id") assert sub response = oauth.google.get( "https://www.googleapis.com/calendar/v3/users/me/calendarList") response.raise_for_status() calendars_by_id = { calendar["id"]: calendar["summary"] for calendar in response.json()["items"] } with beeline.tracer("db connection"): with beeline.tracer("open db connection"): connection = psycopg2.connect(os.environ["POSTGRES_DSN"]) try: with beeline.tracer("calendar chat link transaction"), connection: with beeline.tracer("cursor"), connection.cursor() as cursor: with beeline.tracer("calendar chat link query"): cursor.execute( """ SELECT calendarchatlink.external_id, calendarchatlink.calendar_id, telegram.chat_title FROM calendarchatlink, telegram WHERE calendarchatlink.sub = %s AND telegram.issuer_sub = calendarchatlink.chat_id AND calendarchatlink.chat_type = 'telegram' AND calendarchatlink.calendar_type = 'google' """, (sub, ), ) rows = list(cursor) finally: connection.close() return { "googleTelegramLinks": [{ "externalId": row[0], "googleCalendarSummary": calendars_by_id[row[1]], "telegramChatTitle": row[2], } for row in rows] }
def __call__(self, execute, sql, params, many, context): vendor = context['connection'].vendor trace_name = "django_%s_query" % vendor with beeline.tracer(trace_name): beeline.add_context({ "type": "db", "db.query": sql, "db.query_args": params, }) try: db_call_start = datetime.datetime.now() result = execute(sql, params, many, context) db_call_diff = datetime.datetime.now() - db_call_start beeline.add_context_field("db.duration", db_call_diff.total_seconds() * 1000) except Exception as e: beeline.add_context_field("db.error", str(type(e))) beeline.add_context_field( "db.error_detail", beeline.internal.stringify_exception(e)) raise else: return result finally: if vendor in ('postgresql', 'mysql'): beeline.add_context({ "db.last_insert_id": context['cursor'].cursor.lastrowid, "db.rows_affected": context['cursor'].cursor.rowcount, })
def lambda_handler(event, context): trace_context = None input = None parent_trace = None output = {} logging.debug(f"event: {json.dumps(event)}") init_beeline() # Attempt to get trace_context(s) from the input input = event.get("Input", None) if input: trace_context = input.get("trace_context", None) # Start trace if it isn't already, otherwise resume if trace_context: trace_id, parent_id, context = beeline.trace.unmarshal_trace_context( trace_context) logging.info(f"Resuming trace: {trace_id}") trace = beeline.start_trace(trace_id=trace_id, parent_span_id=parent_id, context=context) # add a field to test context propogation beeline.add_trace_field( event.get("Path", "UnknownPath").lower(), uuid.uuid4()) beeline.add_context( {"name": event.get("Path", "Missing Path Information")}) beeline.add_context( {"function_name": event.get("Path", "Missing Path Information")}) random_sleep() beeline.finish_span(trace) else: trace = start_trace() beeline.add_trace_field("c3po", "r2d2") logging.info(f"Starting Trace") with beeline.tracer( name=event.get("Path", "Missing Path Information")): random_sleep() trace_context = beeline.get_beeline( ).tracer_impl.marshal_trace_context() # If final step close the parent trace if event.get("Path") == "Step4": # 2019-03-26T20:14:13.192Z parent_trace_id, parent_parent_id, parent_context_data = beeline.trace.unmarshal_trace_context( trace_context) start_time = datetime.strptime(event.get("start_time"), "%Y-%m-%dT%H:%M:%S.%fZ") close_final_trace(parent_trace_id, parent_parent_id, parent_context_data, start_time) # Close only (send pending) beeline.close() # Return the trace_context to the SFN output["trace_context"] = trace_context return output
def _execute_and_wait_for_job(job_data): job_id = JobsPersistence.create(job_data) # wait for job to finish: with beeline.tracer("waiting for workers"): period = 0.5 # check every X seconds n_checks = int(REQUEST_TIMEOUT / period) for _ in range(n_checks): job = JobsPersistence.get_by_id(job_id) if job["current_status"] in ["finished", "error"]: break time.sleep(0.5) JobsPersistence.delete(job_id) if job["current_status"] == "finished": results = json.loads(job["results"]) if len(results) != 1: return flask.make_response( jsonify( id=None, code=400, message= "This endpoint can only succeed if process graph yields exactly one result, instead it received: {}." .format(len(results)), links=[]), 400) s3 = boto3.client( 's3', endpoint_url=S3_LOCAL_URL, region_name="eu-central-1", aws_access_key_id=AWS_ACCESS_KEY_ID, aws_secret_access_key=AWS_SECRET_ACCESS_KEY, ) result = results[0] filename = result["filename"] object_key = '{}/{}'.format(job_id, os.path.basename(filename)) s3_object = s3.get_object(Bucket=RESULTS_S3_BUCKET_NAME, Key=object_key) content = s3_object['Body'].read() response = flask.make_response(content, 200) response.mimetype = result["type"] return response if job["current_status"] == "error": return flask.make_response( jsonify(id=None, code=job["error_code"], message=job["error_msg"], links=[]), job["http_code"]) return flask.make_response( jsonify(id=None, code="Timeout", message="Request timed out.", links=[]), 408)
def trace_cm(cm, *args, **kwargs): """ Add tracing around an existing context manager. The args and kwargs are passed to beeline.tracer. """ with beeline.tracer(*args, **kwargs): with cm as cm_obj: yield cm_obj
def honeycomb_middleware(next, root, info, **args): with beeline.tracer(name="graphql_execute"): beeline.add_context({ "graphql.parent_type": root._meta.name if root and hasattr(root, "_meta") else "", "graphql.field_name": info.field_name, "graphql.args": args, }) return next(root, info, **args)
def telegramdeeplink(): sub = session.get("profile", {}).get("user_id") assert sub with beeline.tracer("db connection"): with beeline.tracer("open db connection"): connection = psycopg2.connect(os.environ["POSTGRES_DSN"]) try: with trace_cm(connection, "secret table exists transaction"): with trace_cm(connection.cursor(), "cursor") as cursor: with beeline.tracer("secret table exists query"): cursor.execute(""" SELECT table_name FROM information_schema.tables WHERE table_name = 'secret' """) if not cursor.rowcount: with beeline.tracer("create secret table query"): cursor.execute(""" CREATE TABLE secret ( sub text, secret text, expires timestamp ) """) with trace_cm(connection, "delete expired secrets transaction"): with trace_cm(connection.cursor(), "cursor") as cursor: with beeline.tracer("delete expired secrets query"): cursor.execute( "DELETE FROM secret WHERE expires < %s", (datetime.datetime.utcnow(), ), ) with trace_cm(connection, "create new secret transaction"): with trace_cm(connection.cursor(), "cursor") as cursor: secret = secrets.token_urlsafe(16) expires = datetime.datetime.utcnow() + datetime.timedelta( hours=1) with beeline.tracer("insert new secret query"): cursor.execute( """ INSERT INTO secret ( sub, secret, expires ) VALUES (%s, %s, %s) """, (sub, secret, expires), ) finally: connection.close() bot_name = os.environ["TELEGRAM_BOT_NAME"] return {"deeplinkUrl": f"https://t.me/{bot_name}?startgroup={secret}"}
def update_google_token(token, refresh_token=None, access_token=None): userinfo = oauth.google.parse_id_token(token) with beeline.tracer("db connection"): with beeline.tracer("open db connection"): connection = psycopg2.connect(os.environ["POSTGRES_DSN"]) try: with trace_cm(connection, "google table maint transaction"): with trace_cm(connection.cursor(), "cursor") as cursor: with beeline.tracer("google table exists query"): cursor.execute( """ SELECT table_name FROM information_schema.tables WHERE table_name = 'google' """ ) if not cursor.rowcount: with beeline.tracer("google table create query"): cursor.execute( "CREATE TABLE google (issuer_sub text, token text)" ) with trace_cm(connection, "save google token transaction"): with trace_cm(connection.cursor(), "cursor") as cursor: with beeline.tracer("google token exists query"): cursor.execute( "SELECT issuer_sub FROM google WHERE issuer_sub = %s", (userinfo["sub"],), ) if cursor.rowcount: with beeline.tracer("update google token query"): cursor.execute( "UPDATE google SET token = %s WHERE issuer_sub = %s", (json.dumps(token), userinfo["sub"]), ) else: with beeline.tracer("insert google token query"): cursor.execute( """ INSERT INTO google ( issuer_sub, token ) VALUES (%s, %s) """, (userinfo["sub"], json.dumps(token)), ) finally: connection.close()
def _beeline_wrapper(event, context): global COLD_START # don't blow up the world if the beeline has not been initialized if not beeline.get_beeline(): return handler(event, context) try: # assume we're going to get bad values sometimes in our headers trace_id, parent_id, trace_context = None, None, None try: trace_id, parent_id, trace_context = _get_trace_data(event) except Exception as e: beeline.internal.log( 'error attempting to extract trace context: %s', beeline.internal.stringify_exception(e)) pass with beeline.tracer(name=handler.__name__, trace_id=trace_id, parent_id=parent_id): beeline.add_context({ "app.function_name": getattr(context, 'function_name', ""), "app.function_version": getattr(context, 'function_version', ""), "app.request_id": getattr(context, 'aws_request_id', ""), "app.event": event, "meta.cold_start": COLD_START, }) # if there is custom context attached from upstream, add that now if isinstance(trace_context, dict): for k, v in trace_context.items(): beeline.add_trace_field(k, v) resp = handler(event, context) if resp is not None: beeline.add_context_field('app.response', resp) return resp finally: # This remains false for the lifetime of the module COLD_START = False # we have to flush events before the lambda returns beeline.get_beeline().client.flush()
async def all_players(self) -> List[dict]: with beeline.tracer("BlaseballClient/all_players"): # note: the official API has /database/players, but it 500s if you try # to use it to read all players. We'll batch our requests into a few # smaller ones. async def get_players_for_team(team: dict) -> Iterable[dict]: sublists = await asyncio.gather( self.players(team["bench"]), self.players(team["lineup"]), self.players(team["rotation"]), self.players(team["bullpen"]), ) return itertools.chain(*sublists) teams = await self.all_teams() gathered_players = [get_players_for_team(team) for team in teams] sublists = await asyncio.gather(*gathered_players) return list(itertools.chain(*sublists))
def sirivm(self, message): try: with beeline.tracer(name="sirivm"): if self.command is None: self.command = import_bod_avl.Command().do_source() response_timestamp = parse_datetime(message["when"]) beeline.add_context({ "items_count": len(message["items"]), "age": (now() - response_timestamp).total_seconds() }) vehicle_cache_keys = [self.command.get_vehicle_cache_key(item) for item in message["items"]] with beeline.tracer(name="cache get many"): vehicle_ids = cache.get_many(vehicle_cache_keys) # code: id with beeline.tracer(name="vehicles in bulk"): vehicles = self.command.vehicles.in_bulk(vehicle_ids.values()) # id: vehicle self.command.vehicle_cache = { # code: vehicle key: vehicles[vehicle_id] for key, vehicle_id in vehicle_ids.items() if vehicle_id in vehicles } with beeline.tracer(name="handle items"): for item in message["items"]: self.command.handle_item(item, response_timestamp) with beeline.tracer(name="save"): db_wrapper = HoneyDBWrapper() with ExitStack() as stack: for connection in connections.all(): stack.enter_context(connection.execute_wrapper(db_wrapper)) self.command.save() with beeline.tracer(name="set many"): cache.set_many({ key: value for key, value in self.command.vehicle_id_cache.items() if key not in vehicle_ids or value != vehicle_ids[key] }, 43200) except Exception as e: capture_exception(e) raise Exception
def default_usage(length): with beeline.tracer("single int"): passwd = pwgen(length, symbols=True, allowed_symbols=valid_chars) + "\n" single_count.inc() return Response(passwd, mimetype="text/plain")
def healthz(): with beeline.tracer("healthz"): return Response(f"Current version: {short_hash}.", status=200)
def hello_world(): with beeline.tracer("home"): return render_template('index.html', short_hash=short_hash)
def trace_and_call(wrapped, instance, args, kwargs): with beeline.tracer(*beeline_args, **beeline_kwargs): return wrapped(*args, **kwargs)
def my_sum(a, b): with beeline.tracer(name="my_sum"): return a + b
async def _get_team(session: aiohttp.ClientSession, id: str) -> dict: with beeline.tracer("remote/database/team"): params = {"id": id} async with session.get(_api_route("database/team"), params=params) as resp: resp.raise_for_status() return await resp.json()
async def _get_teams(session: aiohttp.ClientSession) -> List[dict]: with beeline.tracer("remote/database/allTeams"): async with session.get(_api_route("database/allTeams")) as resp: resp.raise_for_status() return await resp.json()
async def players(self, ids: List[str]) -> List[dict]: with beeline.tracer("BlaseballClient/players"): async with aiohttp.ClientSession( headers={"Cookie": f"connect.sid={self._auth_cookie}"} ) as session: return await _get_players(session, ids)
async def team(self, id: str) -> dict: with beeline.tracer("BlaseballClient/team"): async with aiohttp.ClientSession( headers={"Cookie": f"connect.sid={self._auth_cookie}"} ) as session: return await _get_team(session, id)
def telegramwebhook(): if not request.json.get("message"): return ("", 204) assert (request.args["telegram_bot_webhook_token"] == os.environ["TELEGRAM_BOT_WEBHOOK_TOKEN"]) telegram_key = os.environ["TELEGRAM_KEY"] send_message_url = f"https://api.telegram.org/bot{telegram_key}/sendMessage" text = request.json["message"].get("text", "") chat_id = request.json["message"]["chat"]["id"] if text.startswith("/start"): secret = text.rsplit(" ", 1)[1] with beeline.tracer("db connection"): with beeline.tracer("open db connection"): connection = psycopg2.connect(os.environ["POSTGRES_DSN"]) try: with trace_cm(connection, "delete expired secrets transaction"): with trace_cm(connection.cursor(), "cursor") as cursor: with beeline.tracer("delete expired secrets query"): cursor.execute( "DELETE FROM secret WHERE expires < %s", (datetime.datetime.utcnow(), ), ) with trace_cm(connection, "lookup secret transaction"): with trace_cm(connection.cursor(), "cursor") as cursor: with beeline.tracer("lookup secret query"): cursor.execute( "SELECT sub FROM secret WHERE secret = %s", (secret, ), ) if cursor.rowcount: (sub, ) = next(cursor) else: telegram_response = requests.get( send_message_url, data={ "chat_id": chat_id, "text": unwrap_heredoc(f""" Could not link this group to your Fructify account. Please go to <a href="{request.base_url}/dashboard">your Fructify dashboard</a> to link a Telegram group to your account. """), "parse_mode": "HTML", }, ) telegram_response.raise_for_status() return ("", 204) with trace_cm(connection, "link table exists transaction"): with trace_cm(connection.cursor(), "cursor") as cursor: with beeline.tracer("link table exists query"): cursor.execute(""" SELECT table_name FROM information_schema.tables WHERE table_name = 'link' """) if not cursor.rowcount: with beeline.tracer("create link table query"): cursor.execute(""" CREATE TABLE link (sub text, link_name text, issuer_sub text) """) with trace_cm(connection, "link telegram transaction"): with trace_cm(connection.cursor(), "cursor") as cursor: with beeline.tracer("telegram link exists query"): cursor.execute( """ SELECT sub FROM link WHERE sub = %s AND link_name = 'telegram' AND issuer_sub = %s """, (sub, str(chat_id)), ) if not cursor.rowcount: with beeline.tracer("insert telegram link query"): cursor.execute( """ INSERT INTO link (sub, link_name, issuer_sub) VALUES (%s, 'telegram', %s) """, (sub, str(chat_id)), ) with trace_cm(connection, "telegram table exists transaction"): with trace_cm(connection.cursor(), "cursor") as cursor: with beeline.tracer("telegram table exists query"): cursor.execute(""" SELECT table_name FROM information_schema.tables WHERE table_name = 'telegram' """) if not cursor.rowcount: with beeline.tracer("create telegram table query"): cursor.execute(""" CREATE TABLE telegram (issuer_sub text, chat_title text) """) with trace_cm(connection, "save telegram transaction"): with trace_cm(connection.cursor(), "cursor") as cursor: with beeline.tracer("telegram exists query"): cursor.execute( "SELECT issuer_sub FROM telegram WHERE issuer_sub = %s", (str(chat_id), ), ) chat_title = request.json["message"]["chat"]["title"] if cursor.rowcount: with beeline.tracer("update telegram query"): cursor.execute( """ UPDATE telegram SET chat_title = %s WHERE issuer_sub = %s """, (chat_title, str(chat_id)), ) else: with beeline.tracer("insert telegram query"): cursor.execute( """ INSERT INTO telegram ( issuer_sub, chat_title ) VALUES (%s, %s) """, (str(chat_id), chat_title), ) with trace_cm(connection, "delete secret transaction"): with trace_cm(connection.cursor(), "cursor") as cursor: with beeline.tracer("delete secret query"): cursor.execute( "DELETE FROM secret WHERE secret = %s", (secret, ), ) telegram_response = requests.get( send_message_url, data={ "chat_id": chat_id, "text": "This group has been linked to your Fructify account.", }, ) telegram_response.raise_for_status() return ("", 204) finally: connection.close() telegram_response = requests.get( send_message_url, data={ "chat_id": chat_id, "text": f"Received {text}" }, ) telegram_response.raise_for_status() return ("", 204)
async def _get_players(session: aiohttp.ClientSession, ids: List[str]) -> List[dict]: with beeline.tracer("remote/database/players"): params = {"ids": ",".join(ids)} async with session.get(_api_route("database/players"), params=params) as resp: resp.raise_for_status() return await resp.json()
async def all_teams(self) -> List[dict]: with beeline.tracer("BlaseballClient/all_teams"): async with aiohttp.ClientSession( headers={"Cookie": f"connect.sid={self._auth_cookie}"} ) as session: return await _get_teams(session)
def googletelegramlinks_put(): """Link a calendar and chat in the database, and add a webhook for edits.""" sub = session.get("profile", {}).get("user_id") assert sub google_calendar_id = request.json["googleCalendarId"] telegram_chat_id = request.json["telegramChatId"] calendar_list_response = oauth.google.get( "https://www.googleapis.com/calendar/v3/users/me/calendarList") calendar_list_response.raise_for_status() calendar_ids = [ calendar["id"] for calendar in calendar_list_response.json()["items"] ] assert google_calendar_id in calendar_ids with beeline.tracer("db connection"): with beeline.tracer("open db connection"): connection = psycopg2.connect(os.environ["POSTGRES_DSN"]) try: with beeline.tracer( "check chat ownership transaction"), connection: with beeline.tracer("cursor"), connection.cursor() as cursor: with beeline.tracer("check chat ownership query"): cursor.execute( """ SELECT issuer_sub FROM link WHERE link_name = 'telegram' AND sub = %s AND issuer_sub = %s """, (sub, telegram_chat_id), ) assert cursor.rowcount with beeline.tracer( "calendarchatlink maintenance transaction"), connection: with beeline.tracer("cursor"), connection.cursor() as cursor: with beeline.tracer("calendarchatlink exists query"): cursor.execute(""" SELECT table_name FROM information_schema.tables WHERE table_name = 'calendarchatlink' """) if not cursor.rowcount: with beeline.tracer( "create calendatchatlink table query"): cursor.execute(""" CREATE TABLE calendarchatlink ( external_id text, sub text, calendar_id text, calendar_type text, chat_id text, chat_type text ) """) with beeline.tracer( "googlewatch maintenance transaction"), connection: with beeline.tracer("cursor"), connection.cursor() as cursor: with beeline.tracer("googlewatch exists query"): cursor.execute(""" SELECT table_name FROM information_schema.tables WHERE table_name = 'googlewatch' """) if not cursor.rowcount: with beeline.tracer("create googlewatch table query"): cursor.execute(""" CREATE TABLE googlewatch ( external_id text, resource_id text, sub text, calendar_id text ) """) with beeline.tracer( "google telegram link exists transaction"), connection: with beeline.tracer("cursor"), connection.cursor() as cursor: with beeline.tracer("google telegram link exists query"): cursor.execute( """ SELECT sub FROM calendarchatlink WHERE sub = %s AND calendar_id = %s AND calendar_type = 'google' AND chat_id = %s AND chat_type = 'telegram' """, (sub, google_calendar_id, telegram_chat_id), ) if cursor.rowcount: return ({"error": "link exists already"}, 400) with beeline.tracer("find googlewatch transaction"), connection: with beeline.tracer("cursor"), connection.cursor() as cursor: with beeline.tracer("googlewatch exists query"): cursor.execute( """ SELECT external_id, resource_id FROM googlewatch WHERE calendar_id = %s AND sub = %s """, (google_calendar_id, sub), ) if cursor.rowcount: external_id, resource_id = next(cursor) else: external_id = str(uuid.uuid4()) resource_id = None with beeline.tracer( "insert google telegram link transaction"), connection: with beeline.tracer("cursor"), connection.cursor() as cursor: with beeline.tracer("insert google telegram link query"): cursor.execute( """ INSERT INTO calendarchatlink ( external_id, sub, calendar_id, calendar_type, chat_id, chat_type ) VALUES (%s, %s, %s, 'google', %s, 'telegram') """, (external_id, sub, google_calendar_id, telegram_chat_id), ) if not resource_id: request_expiration = datetime.utcnow() + timedelta(days=28) watch_response = oauth.google.post( ("https://www.googleapis.com/calendar/v3/calendars" f"/{google_calendar_id}/events/watch"), json={ "id": external_id, "type": "web_hook", "address": url_for( "googlecalendarwebhook.googlecalendarwebhook", _external=True, ), "expiration": int(request_expiration.timestamp() * 1000), }, ) watch_response.raise_for_status() watch_json = watch_response.json() resource_id = watch_json["resourceId"] response_expiration = datetime.utcfromtimestamp( int(watch_json["expiration"]) / 1000) with beeline.tracer( "insert googlewatch transaction"), connection: with beeline.tracer( "cursor"), connection.cursor() as cursor: with beeline.tracer("insert googlewatch query"): cursor.execute( """ INSERT INTO googlewatch ( external_id, resource_id, sub, calendar_id ) VALUES (%s, %s, %s, %s) """, (external_id, resource_id, sub, google_calendar_id), ) with beeline.tracer( "renewwatchcron maintenance transaction"), connection: with beeline.tracer( "cursor"), connection.cursor() as cursor: with beeline.tracer("renewwatchcron exists query"): cursor.execute(""" SELECT table_name FROM information_schema.tables WHERE table_name = 'renewwatchcron' """) if not cursor.rowcount: with beeline.tracer("renewwatchcron create query"): cursor.execute(""" CREATE TABLE renewwatchcron ( sub text, external_id text, cron_id text ) """) cron_response = requests.get( "https://www.easycron.com/rest/add", params={ "token": os.environ["EASYCRON_KEY"], "url": url_for( "renewwatchcron.renewwatchcron", _external=True, external_id=external_id, ), "cron_expression": f"{response_expiration:%M %H %d %m * %Y}", "timezone_from": "2", "timezone": "UTC", }, ) cron_response.raise_for_status() error = cron_response.json().get("error", {}).get("message") if error: raise Exception(error) with beeline.tracer( "insert renewwatchcron transaction"), connection: with beeline.tracer( "cursor"), connection.cursor() as cursor: with beeline.tracer("insert renewwatchcron query"): cursor.execute( """ INSERT INTO renewwatchcron ( sub, external_id, cron_id ) VALUES (%s, %s, %s) """, (sub, external_id, cron_response.json()["cron_job_id"]), ) finally: connection.close() return ({"message": "link created"}, 201)