def send_email(): from cloud_sql import sql_handler uid = flask.session.get('uid') data = flask.request.json logging.info(f'Receiving request {data} for session uid {uid}') access_token = sql_handler.get_nylas_access_token(uid) if not access_token: response = flask.jsonify({'status': 'no auth'}) response.status_code = 402 return response logging.info(f'Retrieved Nylas access token {access_token}') nylas = APIClient( app_id=app.config["NYLAS_OAUTH_CLIENT_ID"], app_secret=app.config["NYLAS_OAUTH_CLIENT_SECRET"], access_token=access_token ) try: draft = nylas.drafts.create() draft.subject = data.get('subject') draft.body = data.get('body') draft.to = data.get('to') draft.cc = data.get('cc') draft.send() logging.info('email sent successfully') response = flask.jsonify('email sent successfully') response.status_code = 200 return response except Exception as e: logging.error(f'Sending email failed! Error message is {str(e)}') response = flask.jsonify(str(e)) response.status_code = 400 return response
def main(event, context): """Triggered from a message on a Cloud Pub/Sub topic. Args: event (dict): Event payload. context (google.cloud.functions.Context): Metadata for the event. """ name = base64.b64decode(event['data']).decode('utf-8') client = google.cloud.logging.Client() client.setup_logging() import logging credentials = GoogleCredentials.get_application_default() service = discovery.build('compute', 'v1', credentials=credentials) project = os.environ.get('GCP_PROJECT', '') zone = os.environ.get('WORKER_ZONE', '') try: r = service.instances().delete(project=project, zone=zone, instance=name).execute() logging.error(r) except Exception as e: logging.error(e)
def orders_paid(): """ This is the public endpoint (no auth) for shopify orders_paid webhook. """ data = flask.request.json logging.info(f'Receiving orders_paid request {data}') if data.get('topic') != 'ORDERS_PAID': logging.warning(f'Invalid not ORDERS_PAID data received {data}') elif not data.get('domain') or not data.get('payload'): logging.warning(f'Invalid shop/customer data received {data}') else: try: shop = data.get('domain') order_id = data.get('payload').get('id') customer_id = data.get('payload').get('customer').get('id') payload = data.get('payload') res = sql_handler.save_orders_paid(shop, order_id, customer_id, payload) if res.status_code == 200: logging.info('Data saved to cloud SQL') except Exception as e: logging.error(f'Saving events error: {e}') response = flask.jsonify('OK') response.status_code = 200 return response
def gen_lifo_tracker_id(account_id, contract_data): """ Create and save tracking url for given influencer. Currently only support Shopify platform. :param account_id: influencer account_id :param contract_data: the data used for signing influencer-brand contract, which includes contractual details. """ lifo_tracker_id = create_tracker_id( uid=account_id, brand_campaign_id=contract_data.get('brand_campaign_id')) try: domain_or_url = contract_data.get('brand_id') shop_url = generate_shop_url(domain_or_url) if not shop_url: response = flask.jsonify({'Status': 'Invalid shop url'}) response.status_code = 422 return response tracking_url = f'{shop_url}/?lftracker={lifo_tracker_id}' commission = contract_data.get('fixed_commission') commission_type = contract_data.get('commission_type') commission_percentage = contract_data.get('percentage_commission') brand_campaign_id = contract_data.get('brand_campaign_id') res = sql_handler.save_lifo_tracker_id(account_id, lifo_tracker_id, domain_or_url, commission, commission_type, commission_percentage, brand_campaign_id, tracking_url) if len(res) > 0: logging.info(f'Data saved to cloud SQL: {tracking_url}') except Exception as e: logging.error(f'Saving events error: {e}') response = flask.jsonify({'Status': 'Failed'}) response.status_code = 400 return response return tracking_url
def instagram_search(): """ AM use. This is to search instagram account from Modash """ try: data = flask.request.json url = f'{MODASH_API_ENDPINT}/instagram/search' logging.info(f'Receiving request for url {url} and body {data}') headers = { 'Content-type': 'application/json', 'Authorization': MODASH_AUTH_HEADER } modash_search = requests.post(url, data=json.dumps(data), headers=headers) search_res = modash_search.json() logging.info(f'Modash search returned {search_res}') if search_res.get('error'): logging.error('Search returned error') response = flask.jsonify({'Error': 'Failed to search'}) response.status_code = 400 else: response = flask.jsonify(search_res) response.status_code = 200 except Exception as e: logging.error(f'Search error: {e}') response = flask.jsonify({'Error': 'Failed to search'}) response.status_code = 400 return response
def track(): """ public endpoint (no auth) Note: Shopify client side is using the following code snippet to send tracking events: # req.send(JSON.stringify({ # lifo_tracker_id: lifo_tracker_id, # shop: getShop(), # location: document.location, # navigator: navigator.userAgent, # referrer: document.referrer, # discount_code, # })), """ data = flask.request.json logging.info(f'Receiving /track request {data}') if not data.get('shop'): logging.warning(f'Invalid shop data received {data}') elif not data.get('lifo_tracker_id'): logging.debug(f'Skip none lifo event {data}') else: try: res = sql_handler.save_track_visit(data) if res.status_code == 200: logging.info('Data saved to cloud SQL') except Exception as e: logging.error(f'Saving events error: {e}') response = flask.jsonify({'status': 'OK'}) response.status_code = 200 return response
def getDocs(): logging.info("Received a request for /getDocs endpoint") try: # print(request.args) start = 0 size = 0 if 'start' in request.args: start = int(request.args.get('start')) if 'size' in request.args: size = int(request.args.get('size')) # print(start, size) body = {"from": start, "size": size, "query": {"match_all": {}}} logging.info("Sending a search request to Elasticsearch") logging.info("Request Body - " + json.dumps(body)) res = es.search(index=ES_INDEX, body=body) # print(res['hits']['hits']) logging.info("Sending response back to the client") return Response(response=json.dumps(res['hits']['hits']), status=200) except Exception as e: print("Something wrong occurred") print(e) logging.error("Something wrong occurred - " + str(e)) return Response(response=json.dumps({ 'success': False, 'message': 'Something wrong occurred' }), status=500)
def files(): """ POST: upload attachment GET: get attachment status. """ from cloud_sql import sql_handler uid = flask.session.get('uid') data = flask.request.files logging.info(f'Receiving request {data} for session uid {uid}') access_token = sql_handler.get_nylas_access_token(uid) if not access_token: response = flask.jsonify({'status': 'no auth'}) response.status_code = 402 return response logging.info(f'Retrieved Nylas access token {access_token}') nylas = APIClient( app_id=app.config["NYLAS_OAUTH_CLIENT_ID"], app_secret=app.config["NYLAS_OAUTH_CLIENT_SECRET"], access_token=access_token ) if flask.request.method == 'POST': try: # check if the post request has the file part logging.info(f'{[key for key in flask.request.files.keys()]}') if 'file' not in flask.request.files and 'UploadFiles' not in flask.request.files: logging.info('No file part') return flask.redirect(flask.request.url) file = flask.request.files['UploadFiles'] or flask.request.files['file'] # if user does not select file, browser also # submit an empty part without filename if file.filename == '': logging.info('No selected file') return flask.redirect(flask.request.url) if file: filename = secure_filename(file.filename) logging.info(f'receiving file name {filename}') # file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) nylas_file = nylas.files.create() nylas_file.filename = file.filename nylas_file.stream = file # .save() saves the file to Nylas, file.id can then be used to attach the file to an email nylas_file.save() response = flask.jsonify({'file_id': nylas_file.id}) response.status_code = 200 return response except Exception as e: logging.error(f'Uploading attachment failed! Error message is {str(e)}') response = flask.jsonify({'error': f'Uploading attachment failed! Error message is {str(e)}'}) response.status_code = 400 return response elif flask.request.method == 'GET': file_id = flask.request.args.get('file_id') if not file_id: response = flask.jsonify({'error': 'need valid file_id query param'}) response.status_code = 412 nylas_file = nylas.files.get(file_id) response = flask.jsonify({'file': nylas_file}) response.status_code = 200 return response
def post(self): id = self.request.get('id') if id: url = Url.get_by_id(int(id)) if url: result = None try: result = urlfetch.fetch(url.url, allow_truncated=True) except urlfetch.DownloadError: url.status = 'DE' logging.info('DownloadError, url: %s' % (url.url)) except urlfetch.ResponseTooLargeError: url.status = 'RTL' logging.info('ResponseTooLargeError, url: %s' % (url.url)) except urlfetch.InvalidURLError: url.status = 'IUE' logging.info('InvalidURLError, url: %s' % (url.url)) except: url.status = 'UE' logging.error('"Unexpected error: %s, url: %s' % (sys.exc_info()[0], url.url)) if result: if result.content_was_truncated: logging.debug('truncated') if result.status_code: url.status = str(result.status_code) if result and result.status_code and result.status_code == 200: url.valid = 2 else: if url.valid > -5: url.valid = url.valid - 1 else: logging.info('Broken url: %s' % (url.url)) url.last_check = datetime.datetime.now() url.put()
def handle_error(e): logging.error(f"Handle_error: {json.dumps(str(e))}") e_args = e.args if type(e_args) is tuple: error_info = e_args[0] error_code = e_args[1] return json.dumps(error_info), error_code, standard_headers return str(e), 500, standard_headers
def token_verification(id_token): try: decoded_token = auth.verify_id_token(id_token) except ValueError or exceptions.InvalidArgumentError: logging.error('id_token not string or empty or invalid') return '' except auth.RevokedIdTokenError: logging.error('id_token has been revoked') return '' return decoded_token
def system_notification_util(subject, body, to_email, to_name): """ Generic util method for sending email notifications to AM """ if not subject or not body: logging.error('Illegal call to /am/system_notifications with empty email subject or body') response = flask.jsonify({'status': 'empty email subject or body'}) response.status_code = 200 return response if not to_email or not to_name: logging.error('Illegal call to /am/system_notifications with empty to_email or to_name') response = flask.jsonify({'status': 'empty to_email or to_name'}) response.status_code = 200 return response return send_email_notifications(subject=subject, body=body, to_email=to_email, to_name=to_name)
def send_welcome_email(data, context): logging.info('Function triggered by creation/deletion of user: %s' % data["uid"]) logging.info('Created at: %s' % data["metadata"]["createdAt"]) if 'email' in data: logging.info('Email: %s' % data["email"]) email = data['email'] _send_sendgrid_email( from_email=DEFAULT_CONTACT_EMAIL, to_emails=email, subject='Welcome to sign up with us!', text_content='Thanks you for signing up to our platform!') else: logging.error('User signed up without email, skip sending email')
def inf_roi(): shop = flask.session['uid'] campaign_id = flask.request.args.get('campaign_id') try: sqldata_all = sql_handler.get_all_data_per_shop_per_campaign( shop, campaign_id) per_inf_roi = calculate_per_inf_roi(sqldata_all) except Exception as e: logging.error(f'Getting final results error: {e}') per_inf_roi = [] logging.info( f'For shop: {shop}, getting total campaign report of {per_inf_roi}') response = flask.jsonify(per_inf_roi) response.status_code = 200 return response
def failJob(self, job, wsdefFailed ): logging.error( f"Job {job.jobKey}:{wsdefFailed.stepname} failed.") # Go through the worksteps until we find the one that failed. # Mark it as failed, and any subsequent ones as cancelled proj = self.projects[job.projectId] foundFailedStep = False for wsdef in proj.workstepDefs: if not foundFailedStep and wsdef.stepname == wsdefFailed.stepname: foundFailedStep = True job.setWorkstepStatus( wsdef.stepname, JobStatus.FAIL ) elif foundFailedStep and job.worksteps[ wsdef.stepname ] == JobStatus.TODO: job.setWorkstepStatus(wsdef.stepname, JobStatus.CANCEL) self.commitJobChanges( job )
def track_visits(): shop = flask.session['uid'] if not shop: response = flask.jsonify({'Status': 'Failed'}) response.status_code = 422 return response try: # schema: COUNT(*) as visits, shop sql_results = sql_handler.counts_visits_per_shop(shop) visit_results = count_visits_daily(sql_results) except Exception as e: logging.error(f'Getting track_visit error: {e}') logging.debug(f'Obtained tracking report {visit_results}') response = flask.jsonify(visit_results) response.status_code = 200 return response
def get_email_thread(): from cloud_sql import sql_handler uid = flask.session.get('uid') data = flask.request.json logging.info(f'Receiving request {data} for session uid {uid}') search_email = flask.request.args.get('email') sender_email = flask.session.get('email') if not search_email: response = flask.jsonify({'error': 'need valid search_email query param'}) response.status_code = 412 return response access_token = sql_handler.get_nylas_access_token(uid) if not access_token: response = flask.jsonify({'status': 'no auth'}) response.status_code = 402 return response logging.info(f'Retrieved Nylas access token {access_token}') nylas = APIClient( app_id=app.config["NYLAS_OAUTH_CLIENT_ID"], app_secret=app.config["NYLAS_OAUTH_CLIENT_SECRET"], access_token=access_token ) try: # Return all messages found in the user's inbox full_message = [] messages_to = nylas.messages.where(to=search_email) for m in messages_to: message = message_to_dict(m) if len(message['from']) > 0 and message['from'][0]['email'] == sender_email: full_message.append(message) messages_from = nylas.messages.where(from_=search_email) for m in messages_from: message = message_to_dict(m) if len(message['to']) > 0 and message['to'][0]['email'] == sender_email: full_message.append(message) full_message.sort(key=lambda x: x['received_at'], reverse=True) response = flask.jsonify(full_message[0:50]) response.status_code = 200 return response except Exception as e: logging.error(f'Sending email failed! Error message is {str(e)}') response = flask.jsonify(str(e)) response.status_code = 400 return response
def events(event_id): from cloud_sql import sql_handler if not event_id: logging.error('Need a valid event id') response = flask.jsonify('Need a valid event id') response.status_code = 400 return response uid = flask.session.get('uid') access_token = sql_handler.get_nylas_access_token(uid) if not access_token: logging.info(f'The account {uid} has not authorized yet') response = flask.jsonify({'status': 'no auth'}) response.status_code = 402 return response nylas_client = APIClient( app_id=app.config["NYLAS_OAUTH_CLIENT_ID"], app_secret=app.config["NYLAS_OAUTH_CLIENT_SECRET"], access_token=access_token ) try: if flask.request.method == "GET": event = nylas_client.events.get(event_id) response = flask.jsonify(event_to_dict(event)) response.status_code = 200 return response elif flask.request.method == "DELETE": nylas_client.events.delete(event_id, notify_participants='true') response = flask.jsonify('{Status: OK}') response.status_code = 200 return response elif flask.request.method == "PUT": data = flask.request.json event = nylas_client.events.where(event_id=event_id).first() if not event: response = flask.jsonify('unable to modify event') response.status_code = 400 return response event = update_event_from_json(event, data, event.calendar_id) event.save(notify_participants='true') logging.info('Calendar event updated successfully') response = flask.jsonify('Calendar event updated successfully') response.status_code = 200 return response except Exception as e: response = flask.jsonify(str(e)) response.status_code = 400 return response
def shop_redact(): """ This is the public endpoint (no auth) for shopify shop/redact webhook. """ data = flask.request.json logging.info(f'Receiving shop_redact request {data}') try: shop_domain = data.get('shop_domain') res = sql_handler.del_shop_info(shop_domain) if res.status_code == 200: logging.info('Shop data deleted from cloud SQL') except Exception as e: logging.error(f'Shop data deleted events error: {e}') response = flask.jsonify('OK') response.status_code = 200 return response
def customers_data_request(): """ This is the public endpoint (no auth) for shopify customers/data_request webhook. """ data = flask.request.json logging.info(f'Receiving customers_data_request request {data}') try: shop_domain = data.get('shop_domain') customer_id = data.get('customer').get('id') res = sql_handler.get_customer_data(shop_domain, customer_id) if res.status_code == 200: logging.info('Customer data queried from cloud SQL') except Exception as e: logging.error(f'Customer data queried error: {e}') response = flask.jsonify({'result': str(res.response)}) response.status_code = 200 return response
def discovered_notifications(): """ Called when AM team has pushed influencers to brand side to complete the influencer discovery request. The "Discover more" icon in the influencer discovery section will recover to clickable. """ data = flask.request.json brand_contact_name = data.get('brand_contact_name') brand_email = data.get('brand_email') brand_campaign_name = data.get('brand_campaign_name') or 'unknown' if not brand_contact_name or not brand_email: logging.error('Illegal call to /am/discovered_influencers_notifications with empty brand information') response = flask.jsonify({'status': 'empty brand information'}) response.status_code = 200 return response subject = f'Lifo has matched more influencers for campaign: {brand_campaign_name}' body = 'You can check it out by log back into login.lifo.ai' return system_notification_util(subject, body, to_email=brand_email, to_name=brand_contact_name)
def hook(): if request.method == "OPTIONS": # CORS preflight return _build_cors_prelight_response() if request.path.startswith('/brand') or request.path.startswith( '/am') or request.path.startswith('/influencer'): id_token = flask.request.headers.get( 'Authorization') or flask.request.args.get('id_token') if not id_token: logging.error('Valid id_token required') response = flask.jsonify('Valid id_token required') response.status_code = 401 return response decoded_token = token_verification(id_token) uid = decoded_token['uid'] if not uid: logging.error('id_token verification failed') response = flask.jsonify('id_token verification failed') response.status_code = 401 return response logging.info( f'request path is: {request.path} with decoded token {decoded_token}' ) if decoded_token.get(ACCOUNT_MANAGER_FLAG): logging.info('AM account has admin access') elif not decoded_token.get( ACCOUNT_MANAGER_FLAG) and request.path.startswith('/am'): response = flask.jsonify({"status": "not authorized"}) response.status_code = 403 return response elif (request.path.startswith('/brand') and not decoded_token.get(STORE_ACCOUNT))\ or (request.path.startswith('/influencer') and decoded_token.get(STORE_ACCOUNT)): response = flask.jsonify({"status": "not authorized"}) response.status_code = 403 return response flask.session['uid'] = uid flask.session[FROM_SHOPIFY] = decoded_token.get(FROM_SHOPIFY) flask.session[STORE_ACCOUNT] = decoded_token.get(STORE_ACCOUNT) flask.session[ACCOUNT_MANAGER_FLAG] = decoded_token.get( ACCOUNT_MANAGER_FLAG) flask.session['name'] = decoded_token.get('name') flask.session['email'] = decoded_token.get('email') else: logging.debug(f'By passing auth for request {request.path}')
def upload_file(): logging.info("Received a request for /upload endpoint") try: f = request.files['file'] blob_name_uuid = str(uuid.uuid4()) fin = f.stream.read() filename = secure_filename(f.filename) print("filename:", filename) producer.send(topic=KAFKA_GCP_BLOB_TOPIC, value=fin, key=blob_name_uuid) logging.info("Sent a message to GCP blob worker") print("Sent message to GCP blob worker") # .add_callback(on_blob_success) # def on_ocr_success(): # pass # def on_blob_success(): producer.send(topic=KAFKA_OCR_FILE_TOPIC, value=fin, key=blob_name_uuid) logging.info("Sent a message to OCR worker") print("Sent message to OCR worker") # .add_callback(on_ocr_success) response = { 'success': True, # 'message': 'Image read and scanned successfully' } logging.info("Sending response back to the client") return Response(response=json.dumps(response), status=200) except Exception as e: print("Something wrong occurred") print(e) logging.error("Something wrong occurred - " + str(e)) return Response(response=json.dumps({ 'success': False, 'message': 'Something wrong occurred' }), status=500)
def send_email_util(subject, body, to_email, to_name, bcc_email='*****@*****.**', bcc_name='lifo customer support', uid=None): """ This method is a generic email sending util without attachments. """ from cloud_sql import sql_handler if not uid: uid = flask.session.get('uid') access_token = sql_handler.get_nylas_access_token(uid) if not access_token: response = flask.jsonify({'status': 'no auth'}) response.status_code = 402 return response logging.info(f'Retrieved Nylas access token') nylas = APIClient( app_id=app.config["NYLAS_OAUTH_CLIENT_ID"], app_secret=app.config["NYLAS_OAUTH_CLIENT_SECRET"], access_token=access_token ) try: draft = nylas.drafts.create() draft.subject = subject draft.to = [{'email': to_email, 'name': to_name}] # bcc our Lifo's internal support account to allow better tracking. draft.bcc = [{'email': bcc_email, 'name': bcc_name}] draft.body = body draft.tracking = {'links': 'true', 'opens': 'true', 'thread_replies': 'true', } draft.send() logging.info('email sent successfully') response = flask.jsonify('email sent successfully') response.status_code = 200 return response except Exception as e: logging.error(f'Sending email failed! Error message is {str(e)}') response = flask.jsonify(str(e)) response.status_code = 400 return response
def shipping_notifications(): """ When a tracking number is created for a particular campaign, send out email with tracking information to influencers. """ data = flask.request.json inf_name = data.get('inf_name') inf_email = data.get('inf_email') campaign_name = data.get('campaign_name') or 'unknown' tracking_number = data.get('tracking_number') product_name = data.get('product_name') carrier = data.get('carrier') if not tracking_number or not product_name: logging.error('Illegal call to /am/shipping_notifications with tracking information or product name') response = flask.jsonify({'status': 'empty product information'}) response.status_code = 200 return response subject = f'Lifo has shipped product {product_name} for your campaign: {campaign_name}' body = f'Your tracking number is through {carrier}: {tracking_number}' return system_notification_util(subject, body, to_email=inf_email, to_name=inf_name)
def getImage(): logging.info("Received a request for /getImage endpoint") try: if 'uuid' in request.args: image_uuid = request.args.get('uuid') gcp_utils.download_blob(BUCKET_NAME, image_uuid, 'main/temp.png') # f_out = open('main/temp.png') logging.info("Sending response back to the client") return send_file('temp.png', mimetype='image/gif') except Exception as e: print("Something wrong occurred") print(e) logging.error("Something wrong occurred - " + str(e)) return Response(response=json.dumps({ 'success': False, 'message': 'Something wrong occurred' }), status=500)
def discover_more_notifications(): """ Called when brand customers click on the "Discover more" button in the influencer discovery section. It sends the meta data to AM support group email. """ data = flask.request.json brand = data.get('brand') brand_contact_name = data.get('brand_contact_name') brand_email = data.get('brand_email') brand_campaign_name = data.get('brand_campaign_name') or 'unknown' logging.info(f'/discover_more_notifications receiving {data}') if not brand: logging.error('Illegal call to /discover_more_notifications with empty brand name') response = flask.jsonify({'status': 'empty brand name'}) response.status_code = 200 return response subject = f'{brand} {brand_contact_name} with email {brand_email} is requesting more influencers for campaign: {brand_campaign_name}' body = 'Please log into AM tool and push them more influencers' return customer_request_util(subject, body)
def hook(): if request.method == "OPTIONS": # CORS preflight return _build_cors_prelight_response() if flask.session.get('uid'): logging.info('request has been verified') return id_token = flask.request.headers.get('Authorization') or flask.request.args.get('id_token') if not id_token: logging.error('Valid id_token required') response = flask.jsonify('Valid id_token required') response.status_code = 401 return response decoded_token = token_verification(id_token) uid = decoded_token['uid'] if not uid: logging.error('id_token verification failed') response = flask.jsonify('id_token verification failed') response.status_code = 401 return response logging.info(f'request path is: {request.path} with decoded token {decoded_token}') flask.session['uid'] = uid
def get_revenue_per_shop(shop): revenue_results = {'shop': shop} try: ts_results = sql_handler.get_revenue_ts_per_shop(shop) revenue_ts = calculate_shop_daily_revenue(ts_results) except Exception as e: logging.error(f'Getting revenue daily ts error: {e}') revenue_ts = [] try: campaign_results = sql_handler.get_revenue_ts_per_campaign(shop) campaign_revenue = calculate_campaign_daily_revenue(campaign_results) except Exception as e: logging.error(f'Getting revenue error: {e}') campaign_revenue = {} total_revenue = 0 for rev in campaign_revenue.values(): total_revenue += rev revenue_results['revenue_ts'] = revenue_ts revenue_results['campaign_revenue'] = campaign_revenue revenue_results['shop_revenue'] = total_revenue logging.info(f'Revenue results are {revenue_results}') return revenue_results
def get_thread_by_id(): from cloud_sql import sql_handler uid = flask.session.get('uid') data = flask.request.json logging.info(f'Receiving request {data} for session uid {uid}') thread_id = flask.request.args.get('thread_id') if not thread_id: response = flask.jsonify({'error': 'need valid thread_id query param'}) response.status_code = 412 return response access_token = sql_handler.get_nylas_access_token(uid) if not access_token: response = flask.jsonify({'status': 'no auth'}) response.status_code = 402 return response logging.info(f'Retrieved Nylas access token {access_token}') nylas = APIClient( app_id=app.config["NYLAS_OAUTH_CLIENT_ID"], app_secret=app.config["NYLAS_OAUTH_CLIENT_SECRET"], access_token=access_token ) try: full_message = [] messages = nylas.messages.where(thread_id=thread_id) for m in messages: message = message_to_dict(m) full_message.append(message) full_message.sort(key=lambda x: x['received_at']) response = flask.jsonify(full_message) response.status_code = 200 return response except Exception as e: logging.error(f'Sending email failed! Error message is {str(e)}') response = flask.jsonify(str(e)) response.status_code = 400 return response