I'm using urllib3 library in Lambda and python3 code that fetches the webhook url of MSTeams from AWS Secret Manager and sends a http post request to publish a notification.
My webhook url starts with https and looks like this "https://outlook.office.com/webhook/.......". On executing the lambda function, I get an error as shown below LocationParseError Failed to parse:
Code
import urllib3
http = urllib3.PoolManager()
MSTEAMS_WEBHOOK_SECRET_NAME = os.getenv('MSTEAMS_WEBHOOK_SECRET_NAME')
HOOK_URL = get_secret(MSTEAMS_WEBHOOK_SECRET_NAME,"eu-west-1")
def get_secret(secret_name, region_name):
# Create a Secrets Manager client
session = boto3.session.Session()
client = session.client(
service_name='secretsmanager',
region_name=region_name
)
get_secret_value_response = client.get_secret_value(
SecretId=secret_name,
VersionStage="AWSCURRENT"
)
if 'SecretString' in get_secret_value_response:
secret = get_secret_value_response['SecretString']
return secret
else:
decoded_binary_secret = base64.b64decode(get_secret_value_response['SecretBinary'])
return decoded_binary_secret
def lambda_handler(event, context):
message = {
"#context": "https://schema.org/extensions",
"#type": "MessageCard",
"themeColor": data["colour"],
"title": title,
"text": "accountId:\n" + account_id + " <br/>\n"
}
webhook_encoded_body = json.dumps(message).encode('utf-8')
response = http.request('POST',HOOK_URL, body=webhook_encoded_body)
errorMessage
{
"errorMessage": "Failed to parse: {\"msteams-secret\":\"https://outlook.office.com/webhook/dxxxxxx#d779xxxxx-xxxxxx/IncomingWebhook/axxxxxx5/ca746326-bxxx-4xxx-8x-xxxxx\"}",
"errorType": "LocationParseError",
"stackTrace": [
[
"/var/task/lambda_function.py",
145,
"lambda_handler",
"resp = http.request('POST',HOOK_URL, body=webhook_encoded_body)"
],
[
"/var/runtime/urllib3/request.py",
80,
"request",
"method, url, fields=fields, headers=headers, **urlopen_kw"
],
[
"/var/runtime/urllib3/request.py",
171,
"request_encode_body",
"return self.urlopen(method, url, **extra_kw)"
],
[
"/var/runtime/urllib3/poolmanager.py",
324,
"urlopen",
"u = parse_url(url)"
],
[
"/var/runtime/urllib3/util/url.py",
392,
"parse_url",
"return six.raise_from(LocationParseError(source_url), None)"
],
[
"<string>",
3,
"raise_from",
""
]
]
}
Here is how I solved it
Deployed the lambda zip file again, with correct dependencies like requests, urllib3 in the same folder
Apparently, I was trying to store the secret as key/value pair in AWS Secret manager so it was not able to parse a dictionary. I changed the secret type to plaintext
Related
I have set up an AWS PinPoint project and I'm trying to test it by sending an event from a lambda function:
import boto3
import datetime
import time
client = boto3.client('pinpoint')
app_id = '1234abcd'
endpoint_id = 'test_endpoint'
address = 'test#test.com'
def lambda_handler(event, context):
response = client.put_events(
ApplicationId = app_id,
EventsRequest={
'BatchItem': {
endpoint_id: {
'Endpoint': {
'ChannelType': 'EMAIL',
'Address': address,
'Attributes': {
'Cart': ['Hat'],
'Purchased': ['No']
}
},
'Events':{
'cart-event-2': {
'Attributes':{
'AddedToCart': 'Hat'
},
'EventType': 'AddToCartEvent',
'Metrics': {
'price': 29.95
},
'Timestamp': datetime.datetime.fromtimestamp(time.time()).isoformat()
}
}
}
}
}
)
return response
But I am receiving an error that the resource cannot be found, even though I can see it in Pin Point console:
{
"errorMessage": "An error occurred (NotFoundException) when calling the PutEvents operation: Resource not found",
"errorType": "NotFoundException",
"requestId": "xxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx",
"stackTrace": [
" File \"/var/task/lambda_function.py\", line 12, in lambda_handler\n response = client.put_events(\n",
" File \"/var/runtime/botocore/client.py\", line 391, in _api_call\n return self._make_api_call(operation_name, kwargs)\n",
" File \"/var/runtime/botocore/client.py\", line 719, in _make_api_call\n raise error_class(parsed_response, operation_name)\n"
]
}
Turns out I was just in the wrong region on my AWS account. ðŸ§
I created the AWS pinpoint project in one region but was trying to send events to the project from another AWS region, which was why I was getting the NotFoundException.
It shows credentials are missing when I try to use the Google Gmail API to send messages. I want to send an email to my other Gmail account using the Google Gmail API.
import sys
import requests
import base64
import sys
from email.mime.text import MIMEText
AccessToken = ""
params = {
"grant_type": "refresh_token",
"client_id": "xxxxxxxxxxxxxxx",
"client_secret": "xxxxxxxxxxxxxxx",
"refresh_token": "xxxxxxxxxxxxxxxxxxxx",
}
authorization_url = "https://www.googleapis.com/oauth2/v4/token"
r = requests.post(authorization_url, data=params)
if r.ok:
AccessToken = str((r.json()['access_token']))
EmailFrom = "Test1#gmail.com"
EmailTo = "test2#gmail.com"
def create_message(sender, to, subject, message_text):
message = MIMEText(message_text, 'html')
message['to'] = to
message['from'] = sender
message['subject'] = subject
raw = base64.urlsafe_b64encode(message.as_bytes())
raw = raw.decode()
body = {'raw': raw}
return body
body = create_message(EmailFrom, EmailTo, "Just wanna Say Waka Waka!", "Waka Waka!")
url = "https://gmail.googleapis.com/gmail/v1/users/me/messages/send"
header = {
'Authorization': 'Bearer ' + AccessToken,
'Content-Type': 'application/json',
'Accept': 'application/json'
}
r = requests.post(
url,
header,
body
)
print("\n")
print(r.text)
print("\n")
Error:
{
"error": {
"code": 401,
"message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"errors": [
{
"message": "Login Required.",
"domain": "global",
"reason": "required",
"location": "Authorization",
"locationType": "header"
}
],
"status": "UNAUTHENTICATED",
"details": [
{
"#type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "CREDENTIALS_MISSING",
"domain": "googleapis.com",
"metadata": {
"method": "caribou.api.proto.MailboxService.SendMessage",
"service": "gmail.googleapis.com"
}
}
]
}
}
I'm not a google api user, but I've used oauth several times, and your setup is a bit different than what I usually use or what I see from a quick sniff of Google's documention. For example, I use client-creds instead of a refresh token. For what I'm seeing above in your code, no reason to refresh an old token, when you can just mint another one. Compared to yours, usually with oauth, we'll do something like auth=(client_id, client_secret)
Lastly, before you change anything big, when you changed your header to place the AccessToken variable in quotes, did you use an f-string? What is the output of
print(header)
after you have defined it? Is it what you expect? If you didn't format it, it's going to have the variable's name rather than value.
If that's all ok, I'd try to write it according to OAuth standards that I've used several times. Not telling you how to do it, but you could try something like:
def getKey():
url = "https://www.googleapis.com/oauth2/v4/token"
client_id = "*yourgoogleclientid*"
client_secret = "*yourgoogleclientsecret*"
data = {
'grant_type': 'client_credentials'
}
r = requests.post(url, json=data, auth=(client_id, client_secret))
key = r.json()['access_token']
return key
def getWhatever(key, url):
header = {
'Authorization': f'Bearer {key} '
}
params = {
'whatever params': 'you might need'
}
response = requests.get(url, headers=header, params=params)
* parse, process, return, whatever you'd like to do with the response.*
now to use it....
if __name__ == '__main__':
myKey = getKey()
whatImLookingFor = getWhatever(myKey, "*https://google_api_endpoint*")
Now i try to use python to code one CMS project for contacts, but i have some problems.
I code this def but it is not work with google people API.
https://developers.google.com/people/v1/contacts
The logs
File "/Users/nguyenngoclinh/.conda/envs/1z_vietnam/lib/python3.7/site-packages/googleapiclient/http.py", line 907, in execute
raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 404 when requesting https://people.googleapis.com/v1/%7B'people/c9194159806299427121'%7D:deleteContact?alt=json returned "Not Found">
delete fuction is bellow
def delete_contacts_with_resourceName(creds,http,number_of_contact):
# Call the People API
service = build('people', 'v1', credentials=creds)
print('Ban dang xoa', number_of_contactcontact, 'contacts')
results = service.people().connections().list(
resourceName='people/me',
pageSize=number_of_contactofcontact,
personFields='names,emailAddresses,phoneNumbers,emailAddresses,addresses').execute()
service = discovery.build('people', 'v1', http=http,
discoveryServiceUrl='https://people.googleapis.com/$discovery/rest')
connections = results.get('connections', [])
for person in connections:
abcd = person.get('resourceName')
service.people().deleteContact(resourceName={abcd}).execute()
But the creat contact def bellow also work.
def creat_a_google_contact(http):
# POST / v1 / people: createContact
# HTTP / 1.1
# Body: {"names": [{"givenName": "John", "familyName": "Doe"}]}
# Host: people.googleapis.com
service = discovery.build('people', 'v1', http=http,
discoveryServiceUrl='https://people.googleapis.com/$discovery/rest')
service.people().createContact(body={
"names": [
{
'givenName': "Nguyen Ngoc Linh",
"familyName": "29N2359 BMW 325i"
}
],
"phoneNumbers": [
{
'value': "0979955664"
}
],
"emailAddresses": [
{
'value': "bk.nguyenlinh#gmail.com"
}
],
"addresses": [
{
"streetAddress": "So 1 ngo 85 Lang Ha",
"extendedAddress": "Ba Dinh",
"city": "Ha Noi",
"region": "Ha Noi",
"postalCode": "10000",
"country": "Vietnam",
"countryCode": "84"
}
]
}).execute()
The def main, please anyone help me
def main():
creds = get_credentials(FLOW)
# print_list_google_contact_with_number_of_contacts(creds,1000)
http=get_http(creds)
#creat_a_google_contact(http)
# print_result(creds)
delete_contacts_with_resourceName(creds,http,1000)
#print_resourceName(creds, 2000)
Resource name is not formatted correctly:
If you try deleting the contact via Try this API from this page, you will notice that the URL to access has this shape:
https://people.googleapis.com/v1/people/c7142462727258425368:deleteContact
Where people/c7142462727258425368 is the contact's resourceName. That is to say, the resource name does not have single quotes (' ') nor brakets ({ }) around it. Since the resource name is not probably formatted in the URL, the API is not recognizing it, causing the 404 error.
That's what's failing in your request:
https://people.googleapis.com/v1/%7B'people/c9194159806299427121'%7D:deleteContact
To fix this, just remove the brackets around abcd when you provide it as resource name. It should be like this:
service.people().deleteContact(resourceName=abcd).execute()
Reference:
Method: people.deleteContact
I am trying to print all workbooks for tableau using python with tableauserverclient but it keeps giving me 401001: Sign-in Error though I am able to sign in via the same credentials in my tableau server.
Tableau Server version : "Tableau Server Version: 10.2.0 (10200.17.0223.1918) 64-bit"
Code:
import tableauserverclient as TSC
tableau_auth = TSC.TableauAuth('xxx.com', 'xxxx', site_id='Xxxx')
server = TSC.Server('https://xxxx.xx.com')
server.add_http_options({'verify': False})
with server.auth.sign_in(tableau_auth):
all_workbook_items, pagination_item = server.workbooks.get()
print([workbook.name for workbook in all_workbook_items])
error:
Traceback (most recent call last):
File "C:/repo/classes/TableauRefresh.py", line 6, in <module>
with server.auth.sign_in(tableau_auth):
File "C:\trepo\myvenv\lib\site-packages\tableauserverclient\server\endpoint\endpoint.py", line 121, in wrapper
return func(self, *args, **kwargs)
File "C:\repo\myvenv\lib\site-packages\tableauserverclient\server\endpoint\auth_endpoint.py", line 32, in sign_in
self._check_status(server_response)
File "C:\repo\myvenv\lib\site-packages\tableauserverclient\server\endpoint\endpoint.py", line 70, in _check_status
raise ServerResponseError.from_response(server_response.content, self.parent_srv.namespace)
tableauserverclient.server.endpoint.exceptions.ServerResponseError:
401001: Signin Error
Error signing in to Tableau Server
One possibility is that your using SAML SSO for logins. The REST API doesn't work with SAML, so you are going to have to create a native Tableau user and login with those credentials or use a personal access token made with your current account. To create a token manually, go to Account Settings->Personal Access Tokens. Just be aware the tokens expire after 15 days of not being used.
Here's example code from the Tableau website:
# This example shows how to use the Tableau Server REST API
# to sign in to a server, get back an authentication token and
# site ID, and then sign out.
# The example runs in Python 2.7 and Python 3.3 code
import requests, json
# NOTE! Substitute your own values for the following variables
use_pat_flag = True # True = use personal access token for sign in, false = use username and password for sign in.
server_name = "YOUR_SERVER" # Name or IP address of your installation of Tableau Server
version = "x.x" # API version of your server
site_url_id = "SITE_SUBPATH" # Site (subpath) to sign in to. An empty string is used to specify the default site.
# For username and password sign in
user_name = "USERNAME" # User name to sign in as (e.g. admin)
password = "{PASSWORD}"
# For Personal Access Token sign in
personal_access_token_name = "TOKEN_NAME" # Name of the personal access token.
personal_access_token_secret = "TOKEN_VALUE" # Value of the token.
signin_url = "https://{server}/api/{version}/auth/signin".format(server=server_name, version=version)
if use_pat_flag:
# The following code constructs the body for the request.
# The resulting element will look similar to the following example:
#
# {
# "credentials": {
# "personalAccessTokenName": "TOKEN_NAME",
# "personalAccessTokenSecret": "TOKEN_VALUE",
# "site": {
# "contentUrl": ""
# }
# }
# }
#
payload = { "credentials": { "personalAccessTokenName": personal_access_token_name, "personalAccessTokenSecret": personal_access_token_secret, "site": {"contentUrl": site_url_id }}}
headers = {
'accept': 'application/json',
'content-type': 'application/json'
}
else:
# The following code constructs the body for the request. The resulting element will# look similar to the following example:
#
#
# {
# "credentials": {
# "name": "USERNAME",
# "password": "PASSWORD",
# "site": {
# "contentUrl": ""
# }
# }
# }
#
payload = { "credentials": { "name": user_name, "password": password, "site": {"contentUrl": site_url_id }}}
headers = {
'accept': 'application/json',
'content-type': 'application/json'
}
# Send the request to the server
req = requests.post(signin_url, json=payload, headers=headers, verify=False)
req.raise_for_status()
# Get the response
response = json.loads(req.content)
# Parse the response JSON. The response body will look similar
# to the following example:
#
# {
# "credentials": {
# "site": {
# "id": "xxxxxxxxxx-xxxx-xxxx-xxxxxxxxxx",
# "contentUrl": ""
# },
# "user": {
# "id": "xxxxxxxxxx-xxxx-xxxx-xxxxxxxxxx"
# },
# "token": "CREDENTIALS_TOKEN"
# }
# }
#
# Get the authentication token from the credentials element
token = response["credentials"]["token"]
# Get the site ID from the <site> element
site_id = response["credentials"]["site"]["id"]
print('Sign in successful!')
print('\tToken: {token}'.format(token=token))
print('\tSite ID: {site_id}'.format(site_id=site_id))
# Set the authentication header using the token returned by the Sign In method.
headers['X-tableau-auth']=token
# ... Make other calls here ...
# Sign out
signout_url = "https://{server}/api/{version}/auth/signout".format(server=server_name, version=version)
req = requests.post(signout_url, data=b'', headers=headers, verify=False)
req.raise_for_status()
print('Sign out successful!')
I am trying to retrieve the authentication token from the API using requests library from python. Here is the attempt I have made so far:
def get_token():
data = {
"auth" : {
"identity" : {
"methods" : [ "password" ],
"password": {
"user" : {
"name" : OS_USERNAME,
"domain": { "name": "Default" },
"password": OS_PASSWORD
}
}
}
}
}
r = requests.post(
OS_AUTH_URL+'/auth/tokens',
headers = HEADERS,
json = data, # https://stackoverflow.com/questions/9733638
verify = False
)
print(r.content)
j = json.loads(r.content)
return j['token']['user']['id']
I get token in the response :
{
"token": {
"issued_at": "2018-07-03T11:03:59.000000Z",
"audit_ids": [
"Fg1ywtZBQ1CkigCw70If9g"
],
"methods": [
"password"
],
"expires_at": "2018-07-03T12:03:59.000000Z",
"user": {
"password_expires_at": null,
"domain": {
"id": "default",
"name": "Default"
},
"id": "e0dc5beb383a46b98dad824c5d76e719",
"name": "admin"
}
}
}
However, when I am reusing this token to get, for instance, the list of projects :
def get_tenantID():
r = requests.get(
OS_AUTH_URL+'/auth/projects',
headers = HEADERS,
verify = False
)
return r
r = get_token()
HEADERS['X-Auth-Project-Id'] = 'admin'
HEADERS['X-Auth-Token'] = r
r = get_tenantID()
I get this error as if I would not be authenticated:
<Response [401]>
{"error": {"message": "The request you have made requires authentication.", "code": 401, "title": "Unauthorized"}}
Googling around and using openstack token issue command showed me that usually, token are more like:
gAAAAABaCo1F1CIMVlsTBeuqYH8tm2qR29tbkmUL4vZuhCNPXJI39TQ2YzL6Twoj8fNcAyLe3WhCYW2O1YpbBF0G8mo4bt7Kf0IRsoDOoJ6uWa3RYyJ5SQNoB_5n8EnVXbKPxFYOZ_iFBnaVtL1_XDrGbwsrlDeyy8lZTDdLsqY52pUhFR-7Uow
which is not what I get with get_token.
What am I doing wrong?
Many thanks!
When you use the auth API directly, the token issued comes in the X-Subject-Token header.
Thus, to retrieve in your python example, you could access the response.headers dict like this:
token = r.headers['X-Subject-Token']
More info about authentication in the Keystone v3 docs
1. fetch authentication token as mentioned below:
r = requests.post(
OS_AUTH_URL+'/auth/tokens',
headers = HEADERS,
json = data,
verify = False
)
token = r.headers[X-Subject-Token]
2. Pass this token in header for further request:
{
'X-Auth-Token': token
}