I have written AWS lambda in python to send message to sns topic.
I am using aws code pipeline to deploy this code in clould.
Running this lambda by calling api gateway.
python code is as below:
import boto3
from shared.environment import Env
from account.initiate_transition.transition_event_service import TransitionEventService
from shared.utils import Logger
from shared.xray_utils import trace
from shared.keep_warm import keep_warm
LOG = Logger.initialise_logger('transition-sms', None)
ENV = Env({Env.SMS_SNS_TOPIC_ARN, Env.AWS_REGION_NAME, Env.SMS_TRANSITION_TABLE})
SNS_CLIENT = boto3.client('sns')
DYNAMO_DB = boto3.resource('dynamodb', region_name=ENV.get(Env.AWS_REGION_NAME))
COGNITO_CLIENT = boto3.client('cognito-idp', region_name=ENV.get(Env.AWS_REGION_NAME))
SMS_TRANSITION_TABLE = DYNAMO_DB.Table(ENV.get(Env.SMS_TRANSITION_TABLE))
EVENT_SERVICE = TransitionEventService(SMS_TRANSITION_TABLE,
SNS_CLIENT,
ENV.get(Env.SMS_SNS_TOPIC_ARN),
COGNITO_CLIENT)
#trace
#keep_warm
def lambda_handler(event, context):
LOG = Logger.initialise_logger('transition-sms', context.aws_request_id)
try:
return EVENT_SERVICE.handle(event)
except Exception as e:
LOG.error(str(e))
transition_event_service.py
from account.initiate_transition.transition_sms_event import TransitionSmsEvent
from shared.utils import ApiGatewayResponse, Logger
from shared.xray_utils import trace
from http import HTTPStatus
from uuid import uuid4
from botocore.exceptions import ClientError
from jose import jwt
LOG = Logger.get_logger(__name__)
class TransitionEventService:
def __init__(self, sms_transition_table, sns_client, topic_arn, cognito_client):
LOG.debug('Initialising TransitionEventService')
self.sms_transition_table = sms_transition_table
self.sns_client = sns_client
self.topic_arn = topic_arn
self.cognito_client = cognito_client
#trace
def handle(self, event):
try:
event_object = self.instantiate_event(event)
except Exception as e:
LOG.error(e)
return ApiGatewayResponse.init(HTTPStatus.BAD_REQUEST, {
'error': 'Invalid request'
})
quid = str(uuid4())
LOG.info('quid {}'.format(quid))
LOG.debug('Storing SMS transition details')
self.sms_transition_table.put_item(Item={
'id_token': event_object.id_token,
'landing_page': event_object.landing_page,
'quid': quid
})
# Get phone number claim and verified claim
LOG.debug('Decoding id_token to get unverified claims')
claims = jwt.get_unverified_claims(event_object.id_token)
user_pool_id = claims['UserPoolId']
username = claims['Username']
url = "account/verify-transition?quid=123&&username=xyz"
response = self.cognito_client.admin_get_user(
UserPoolId = user_pool_id,
Username = username
)
phone_number = response['phone_number']
LOG.debug('Sending Transition SMS')
self.send_transition_sms(url=url, phone_number=phone_number)
LOG.debug('SMS sent to {}'.format(phone_number))
return ApiGatewayResponse.init(HTTPStatus.OK)
def instantiate_event(self, event):
return TransitionSmsEvent(event)
def send_transition_sms(self, url: str, phone_number: str):
try:
LOG.debug('Publishing SMS url to SNS Topic:{}'.format(self.topic_arn))
self.sns_client.publish(
TopicArn=self.topic_arn,
Message=json.dumps({
'default': json.dumps({
'url': url,
'phone_number': phone_number
})
}),
MessageStructure='json'
)
except ClientError as e:
LOG.error(e)
raise e
I getting below error in cloud watch logs:
Could someone help me resolving this issue.
If you have requirement.txt then please verify below entry should be present
python-jose-cryptodome==1.3.2
You have to import the library needed by your functions. To do that you have to create a virtual env and activate it:
virtualenv v-env
source v-env/bin/activate
Then you install your different libraries
pip install library1
pip install library2
...
And finally you desactivate the virtual environment and package your function :
deactivate
zip -r function.zip .
More infos : https://amzn.to/2oif6Wv
if you using requirement.txt you must include dependencies which required.
so once you run or deploy your code through code pipeline it will download the dependencies and zip into artifact which you can use through s3 for your python lambda.
once you call api gateway back up by python lambda it will not fail while importing.
no module named jose
It shows that you haven't specify dependency in your requirement.txt file.
depedency require for jose is
python-jose-cryptodome==1.3.*
Check whether python-jose-cryptodome==1.3.2 is added in your requirement.txt file.
If not add it this is major reason your getting this error.
Related
I am not able to trigger a slack alert using the below code. This code worked in the notebook and we are able to trigger the notification to the same channel, but when we deployed in AWS using serverless deploy it is not triggering notification. There is no error message as well and the code is working as expected till print('working_well') part only the slack notification is not working. Do i need to add anything else apart from the below code, can someone please help here.
import json
import base64
import os
import boto3
import time
import gzip
import yaml
import pandas as pd
import traceback
import logging
import requests
from datetime import datetime
from src import Connection_details
def lambda_handler(event = {'jdbc_url': 'URL','Id':'1'
,'stage':'dev'} ,context = ''):
jdbc_url = event['jdbc_url']
storeId = str(event['Id'])
stage = event['stage']
Stores_not_working=[]
print(jdbc_url,Id,stage)
db = Connection_details.mysql_simple_connector(jdbc_url)
SLACK_WEBHOOK_URL = 'webhook_url'
SLACK_CHANNEL = "test"
def error_manager(e):
print(f"An error happenned: {e}")
try:
cnx = db.mysql_connection()
df = pd.read_sql_query("SELECT max(dateClosed) as max_date_closed FROM table",cnx)
test_date=df['max_date_closed'][0]
if(len(df)!=0):
message = f'It's working'
print(message)
slack_message = {'icon_emoji': ':robot_face:',
'channel': SLACK_CHANNEL,
'text': f"{message}"
}
print('working_well')
response = requests.post(SLACK_WEBHOOK_URL, data=json.dumps(
slack_message), headers={'Content-Type': 'application/json'})
except Exception as e:
error_manager(e)
I have tried same code in my python terminal(jupyter notebook) it worked and i am able see the notification in the test channel . But through AWS there is no error message nor an alert to the channel.
I am trying to send e-mail campaign mails with the SMTP API of Sendinblue.com, a cloud-based marketing communication software suite. I am running my configuration of the template script provided by Sendinblue as seen on the screenshot:
My initial script was as follows:
# ------------------
# Create a campaign\
# ------------------
# Include the Sendinblue library\
from __future__ import print_function
import time
from datetime import datetime
import sib_api_v3_sdk
from sib_api_v3_sdk.rest import ApiException
from pprint import pprint
myapikey = 'xkeysib-.................................' # full api key
# Instantiate the client\
sib_api_v3_sdk.configuration.api_key['api-sb'] = myapikey
api_instance = sib_api_v3_sdk.EmailCampaignsApi()
# Define the campaign settings\
email_campaigns = sib_api_v3_sdk.CreateEmailCampaign(
name= "Campaign sent via the API",
subject= "My subject",
sender= { "name": "From name", "email": "------#gmail.com"}, # personal email with which I was registered # sendinblue
type= "classic",
# Content that will be sent\
html_content= "Congratulations! You successfully sent this example campaign via the Sendinblue API.",
# Select the recipients\
recipients= {"listIds": [2, 7]},
# Schedule the sending in one hour\
scheduled_at = datetime.now()
)
# Make the call to the client\
try:
api_response = api_instance.create_email_campaign(email_campaigns)
pprint(api_response)
except ApiException as e:
print("Exception when calling EmailCampaignsApi->create_email_campaign: %s\n" % e)
I referred to Sendinblue Documentation in the cases where I had doubt. Most of the process seems self-explanatory, just the line with configuration.api_key['api-key'] = 'YOUR API KEY' was ambiguous as it is not quite clearly stated how exactly the api-key should be passed and the attribute of api_key I assumed api_key was holding the name of the API as defined in the SMTP & API Advanced tab. Even if I assume it should be holding some other values, it is not clear what they should be, what I can tell for sure is that sib_api_v3_sdk.configuration.api_key['api-key'] also resulted in AttributeError: module 'sib_api_v3_sdk.configuration' has no attribute 'api_key'.
After initially getting the error AttributeError: module 'sib_api_v3_sdk.configuration' has no attribute 'api_key' I researched a dozen of articles on StackOverflow, one of them on the Kubernetes topic where the script seemed to throw a similar error like the one I was getting, so I followed the advices described in Python kubernetes module has no attribute 'api_key'. Therefore, I tried reassigning the class attributes as follows:
configuration.api_key['api-sb'] = myapikey
api_instance = sib_api_v3_sdk.EmailCampaignsApi()
The fact that now I wasn't getting the error of a missing key but facing an 'unauthorized' message cheered me up for a short time, until I spent several hours trying to get past this. So the script that's now throwing the HTTP response body: {"message":"Key not found","code":"unauthorized"} is now this:
# ------------------
# Create a campaign\
# ------------------
# Include the Sendinblue library\
from __future__ import print_function
import time
from datetime import datetime
import sib_api_v3_sdk
from sib_api_v3_sdk.rest import ApiException
from pprint import pprint
myapikey = 'xkeysib-c..............' # key
# Instantiate the client\
sib_api_v3_sdk.configuration.api_key['api-sb'] = myapikey
api_instance = sib_api_v3_sdk.EmailCampaignsApi()
# Define the campaign settings\
email_campaigns = sib_api_v3_sdk.CreateEmailCampaign(
name= "Campaign sent via the API",
subject= "My subject",
sender= { "name": "From name", "email": "mail....#gmail.com"}, # personal gmail with which I was initially registered # sendinblue.com
type= "classic",
# Content that will be sent\
html_content= "Congratulations! You successfully sent this example campaign via the Sendinblue API.",
# Select the recipients\
recipients= {"listIds": [2, 7]},
# Schedule the sending in one hour\
scheduled_at = datetime.now()
)
# Make the call to the client\
try:
api_response = api_instance.create_email_campaign(email_campaigns)
pprint(api_response)
except ApiException as e:
print("Exception when calling EmailCampaignsApi->create_email_campaign: %s\n" % e)
My 1st question is: was my 1st script wrong and in what way?
Secondly: Is configuration.api_key['api-sb'] = myapikey the correct syntax, assuming that 'api-sb' is the name of my API key as shown on the screenshot?
And third: In the myapikey variable, when assigning the API key itself, should there be anything else as a prefix to the key?
Try this sample transactional email script. Worked for me in python.
https://developers.sendinblue.com/reference/sendtransacemail
Instead of this:
sib_api_v3_sdk.configuration.api_key['api-sb'] = myapikey
api_instance = sib_api_v3_sdk.EmailCampaignsApi()
do this:
configuration = sib_api_v3_sdk.Configuration()
configuration.api_key['api-sb'] = myapikey
api_instance = sib_api_v3_sdk.EmailCampaignsApi(sib_api_v3_sdk.ApiClient(configuration))
as shown in https://developers.sendinblue.com/reference/createemailcampaign-1.
I'm using Kubernetes Python Client to implement to implement a custom scheduler. Here is the code of the scheduler.
from kubernetes import client, config, watch
from kubernetes.client.rest import ApiException
config.load_kube_config()
v1 = client.CoreV1Api()
scheduler_name = 'custom-scheduler-test'
def nodes_available():
ready_nodes = []
for n in v1.list_node().items:
for status in n.status.conditions:
if status.status == 'True' and status.type == 'Ready':
ready_nodes.append(n.metadata.name)
return ready_nodes
def scheduler(name, node, namespace='default'):
body = client.V1ConfigMap()
target = client.V1ObjectReference()
target.kind = 'Node'
target.apiVersion = 'v1'
target.name = node
meta = client.V1ObjectMeta()
meta.name = name
body.target = target
body.metadata = meta
return v1.create_namespaced_binding(namespace, body)
def main():
w = watch.Watch()
for event in w.stream(v1.list_namespaced_pod, 'default'):
if event['object'].status.phase == 'Pending' and event['object'
].spec.scheduler_name == scheduler_name:
try:
res = scheduler(event['object'].metadata.name,random.choice(nodes_available()))
except ApiException as e:
print ("Exception when calling CoreV1Api->create_namespaced_binding: %s\n" % e)
if __name__ == '__main__':
main()
This code worked before for Kubernetes 1.6 but now I have 1.7 and it shows target.name: Required value. Any clue to fix the error?
Error Message
Exception when calling CoreV1Api->create_namespaced_binding: (500)
Reason: Internal Server Error
HTTP response headers: HTTPHeaderDict({'Content-Type': 'application/json', 'Date': 'Wed, 06 Jun 2018 20:55:04 GMT', 'Content-Length': '120'})
HTTP response body: {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"target.name: Required value","code":500}
Updated on 2018-06-08
If I use body = client.V1Binding() instead of body = client.V1ConfigMap() it shows following error message
File "/usr/lib/python2.7/site-packages/kubernetes/client/models/v1_binding.py", line 64, in __init__
self.target = target
File "/usr/lib/python2.7/site-packages/kubernetes/client/models/v1_binding.py", line 156, in target
raise ValueError("Invalid value for `target`, must not be `None`")
ValueError: Invalid value for `target`, must not be `None`
I faced the same problem am using python3, i just uninstalled kubernetes library by typing:
pip3 uninstall kubernetes
then i installed version 2.0.0 by typing:
pip install kubernetes==2.0.0
verify that is successfully installed:
pip3 freeze | grep kubernetes
I've used this workaround here.
I've rewritten the scheduler function like this:
def scheduler(name, node, namespace="default"):
target=client.V1ObjectReference()
target.kind="Node"
target.apiVersion="v1"
target.name= node
meta=client.V1ObjectMeta()
meta.name=name
body=client.V1Binding(target=target)
body.metadata=meta
return v1.create_namespaced_binding(namespace, body, _preload_content=False)
I want to send transactional and marketing emails using 'SendInBlue'. I also want to use Python language to do the same. I have visited the API doc of SendInBlue and followed the same procedure, still unsuccessful in sending the emails.
from mailin import Mailin
m = Mailin("https://api.sendinblue.com/v2.0","ScrWGqd296ya0CWq")
data = { "to" : {"aman#gmail.com":"to whom!"},
"from" : ["amandeep#gmail.com", "from email!"],
"subject" : "Subject...",
"html" : "This is the <h1>HTML</h1>",
"attachment" : ["https://example.com/path-to-file/filename1.pdf", "https://example.com/path-to-file/filename2.jpg"]
}
result = m.send_email(data)
print(result)
I have also downloaded mailin-api-python from github and ran this script. I don't have any idea where to setup to my smtp details.
**I have changed the API key just for security purpose.
I would strongly recommend you to use the latest version of SendinBlue's Python wrapper where they have provided an example
from __future__ import print_function
import time
import sib_api_v3_sdk
from sib_api_v3_sdk.rest import ApiException
from pprint import pprint
# Configure API key authorization: api-key
configuration = sib_api_v3_sdk.Configuration()
configuration.api_key['api-key'] = 'API-KEY'
# create an instance of the API class
api_instance = sib_api_v3_sdk.SMTPApi(sib_api_v3_sdk.ApiClient(configuration))
senderSmtp = sib_api_v3_sdk.SendSmtpEmailSender(name="test",email="youremail#gmail.com")
sendTo = sib_api_v3_sdk.SendSmtpEmailTo(email="recipientEmail#gmail.com",name="Recipient Name")
arrTo = [sendTo] #Adding `to` in a list
send_smtp_email = sib_api_v3_sdk.SendSmtpEmail(sender=senderSmtp,to=arrTo,html_content="This is a test",subject="This is a test subject") # SendSmtpEmail | Values to send a transactional email
try:
# Send a transactional email
api_response = api_instance.send_transac_email(send_smtp_email)
pprint(api_response)
except ApiException as e:
print("Exception when calling SMTPApi->send_transac_email: %s\n" % e)
I got a sample script working:
I successfully received the email and the messageId as the response.
Please let me know if it helps!
We created an application for Google Apps Marketplace. Our app works only if it's installed for everyone. But the problem is, some customers install our app for some organizations, not everyone. We want to display a specific message to those customers, but the problem is that we don't know if our app is installed for some organizations, or not installed at all. Therefore, customers who installed our app for some organizations get a message which is intended for customers who didn't install our app at all. We show them the install button but nothing happens when they install our app again, because it's already installed. We want to give them instructions how to change our app's status to "on for everyone".
How can we check if our app is installed for some organizations? We get the following error message from Google:
Failed to retrieve access token: {
"error" : "unauthorized_client",
"error_description" : "Unauthorized client or scope in request."
}
Which is the same error message we receive for cutomers who didn't install our app at all.
This is the Python function who throws the exception:
def _do_refresh_request(self, http_request):
"""Refresh the access_token using the refresh_token.
Args:
http_request: callable, a callable that matches the method signature of
httplib2.Http.request, used to make the refresh request.
Raises:
AccessTokenRefreshError: When the refresh fails.
"""
body = self._generate_refresh_request_body()
headers = self._generate_refresh_request_headers()
logger.info('Refreshing access_token')
resp, content = http_request(
self.token_uri, method='POST', body=body, headers=headers)
if resp.status == 200:
# TODO(jcgregorio) Raise an error if loads fails?
d = simplejson.loads(content)
self.token_response = d
self.access_token = d['access_token']
self.refresh_token = d.get('refresh_token', self.refresh_token)
if 'expires_in' in d:
self.token_expiry = datetime.timedelta(
seconds=int(d['expires_in'])) + datetime.datetime.utcnow()
else:
self.token_expiry = None
if self.store:
self.store.locked_put(self)
else:
# An {'error':...} response body means the token is expired or revoked,
# so we flag the credentials as such.
logger.info('Failed to retrieve access token: %s' % content)
error_msg = 'Invalid response %s.' % resp['status']
try:
d = simplejson.loads(content)
if 'error' in d:
error_msg = d['error']
self.invalid = True
if self.store:
self.store.locked_put(self)
except StandardError:
pass
raise AccessTokenRefreshError(error_msg)
Update 1: in Apps > Marketplace apps, an app can be on for everyone, on for selected orgs or off. We need to know the status of our app.
Update 2: I tried calling check_general_access but also when our application is uninstalled we receive True (Application has general access). This is after we confirmed that check_access returned False.
#staticmethod
def check_access(admin_email):
http = httplib2.Http()
credentials = SignedJwtAssertionCredentials(
SERVICE_EMAIL,
PRIVATE_KEY,
scope='https://apps-apis.google.com/a/feeds/emailsettings/2.0/ https://www.googleapis.com/auth/admin.directory.user.readonly',
sub=str(admin_email),
)
http = credentials.authorize(http)
try:
service = build(serviceName='admin', version='directory_v1', http=http)
logging.info("Application has access to admin's %s domain" % (admin_email))
return True
except Exception as e:
logging.info("Application does not have access to admin's %s domain (exception: %s)" % (admin_email, e.message))
return False
#staticmethod
def check_general_access():
http = httplib2.Http()
credentials = SignedJwtAssertionCredentials(
SERVICE_EMAIL,
PRIVATE_KEY,
scope='https://apps-apis.google.com/a/feeds/emailsettings/2.0/ https://www.googleapis.com/auth/admin.directory.user.readonly',
)
http = credentials.authorize(http)
try:
service = build(serviceName='admin', version='directory_v1', http=http)
logging.info("Application has general access")
return True
except Exception as e:
logging.info("Application does not have general access (exception: %s)" % e.message)
return False
Not sure, but may have found a way. From the documentation I asserted that domain wide access is needed to impersonate a user within the target domain. Service apps do not need this for other tasks. While convoluted, you can test if you get credentials without the sub parameter to SignedJwtAssertionCredentials. If this succeeds, but adding the sub parameter fails, you're installed but not domain wide.
Let us know if this works and obviously Google has some work to do there still.
You can add ping back, every hour or so call some end point. If the ping was too long ago they probably remove the app