I currently have POSTed items into the DynamoDB (the date is a string):
dynamodb
When I try accessing this via a GET, I get 404 not found (not a 502 so it appears the lambda response is OK):
get request
This is the code in my lambda function:
def lambda_handler(event, context):
logger.info(event)
httpMethod = event['httpMethod']
path = event['path']
if httpMethod == getMethod and path == answersPath:
response = buildResponse(200)
elif httpMethod == getMethod and path == dailyAnswerPath:
response = getAnswer(event['queryStringParameters']['day'])
else:
response = buildResponse(404, 'Not Found')
return response
def getAnswer(day):
try:
response = table.get_item(
Key = {
'day': day
}
)
if 'answer' in response:
return buildResponse(200, response['answer'])
else:
return buildResponse(404, {'Message':'Day %s not found' %day})
except:
logger.exception('getAnswer error day %s' %day)
def buildResponse(statusCode, body=None):
response = {
'statusCode': statusCode,
'headers': {
'Content-Type':'application/json',
'Access-Control-Allow-Origin': '*'
}
}
if body is not None:
response['body'] = json.dumps(body, cls=encodeddd)
return response
Related
My apologies for basic question. I am completely new to AWS as well as Python. I am trying to do sample code but facing a error. I'm trying to read some data from a dynamodb table, but facing error like this in AWS Cloudwatch logs:
"Runtime.HandlerNotFound: Handler 'lambda_handler' missing on module 'lambda_function'".
And Postman is throwing error as
"message": "Internal server error"
The code is:
import boto3
class userProfile:
def __init__(self):
dynamodb = boto3.resource('dynamodb')
self.table = dynamodb.Table('user_data')
def Create_table():
pass
def Read_table(self, event):
response = self.table.get_item(
Key = {
'user_name' : event['user_name']
}
)
if 'Item' in response:
return response['Item']
else:
return {
'statusCode':'404',
'body' : 'User Name ' + 'id ' + 'not found'
}
def Update_tabel():
pass
def lambda_handler(event, context):
if event:
user_Object = userProfile()
if event["tasktype"] == "read":
read_result = user_Object.Read_table(event['data'])
return read_result
else:
return {
'statusCode': '404',
'body': 'Not found'
}
I am trying to check the response status code after trigerring some API with a POST method, Response status code is of Magicmock instance type, i am checking whether the status code is inbetween 400 and 500 using comparison operator which works in python 2 but raises TypeError in python 3
import mock
response = <MagicMock name='Session().post()' id='130996186'>
Below code works in python 2
if (400 <= response.status_code <= 500):
print('works')
But when executed in python 3, raises
TypeError: '<=' not supported between instances of 'int' and 'MagicMock'
class BMRAPI(object):
root_url = None
def __init__(self, user, api_key, root_url=BMR_URL,
api_uri=RESULTS_API_URI):
self.log =
logging.getLogger("BMRframework.Reporting.BMR6.BMRAPI")
self.root_url = root_url
self.url = urljoin(root_url, api_uri)
self.log.info("Connecting to BMR REST API: %s" % self.url)
self.session = requests.Session()
auth = 'ApiKey {0}:{1}'.format(user, api_key)
self.session.headers.update({
'Content-type': 'application/json',
'Accept': 'text/plain',
'Authorization': auth})
self.session.trust_env = False # bypass the proxy
self.log.debug("Authenticating as: %s" % user)
self.log.debug("Using API Key: %s" % api_key)`enter code here`
self.log.info("Connection to REST API successful")
def url_for_resource(self, resource_name):
return urljoin(self.url, resource_name) + "/"
def create(self, resource_name, data):
response = self.session.post(self.url_for_resource(resource_name),
json.dumps(data), timeout=TIMEOUT)
return self.handle_response(response)
def handle_response(self, response):
if (400 <= response.status_code <= 500):
print('mars')
Below is the UNit test case
#mock.patch("requests.Session")
def BMRAPI(Session):
api = BMRAPI('http://1.2.3.4/', 'dummy_user', '12345')
data = {'hello': 123}
api.create('testresource', data)
This isn't exactly a fix, more of a workaround.
Instead of making that <= comparison, write a separate method:
def is_4xx_or_5xx_code(status_code):
return 400 <= status_code <= 500
if is_4xx_or_5xx_code(status_code=response.status_code):
print('works')
Then mock it in your tests.
#mock.patch('path.to_code.under_test.is_4xx_or_5xx_code')
def test_your_method(mock_status_code):
mock_status_code.return_value = True
# rest of the test.
This is my github repository for this live demo I have going on at the moment.
//index.js is where the POST request is being sent from
class Index extends React.Component {
state = {
loading: false,
};
handleClickLoading = () => {
this.setState(state => ({
loading: true,
}));
var jqXHR = $.ajax({
type: "POST",
url: "http://localhost:5000/login",
async: true,
data: { mydata: [Cookies.get('links'), Cookies.get('numSentences')] },
success: function(){
const data = JSON.parse(jqXHR.responseText);
console.log(data);
}
});
Cookies.set('final', jqXHR.responseText);
console.log(jqXHR.responseText)
};
render() {
const {loading} = this.state;
return (
<div>
<AppBar/>
<Block/>
<Card>
<CardActions>
<Button size="large"
fullWidth={true}
onClick={this.handleClickLoading}>Submit</Button>
</CardActions>
</Card>
<Fade
in={loading}
unmountOnExit
>
<BottomBar/>
</Fade>
</div>
)
}
}
export default Index;
and its trying to get data ran through python scripts through a Flask server:
...
def crossdomain(origin=None, methods=None, headers=None,
max_age=21600, attach_to_all=True,
automatic_options=True):
if methods is not None:
methods = ', '.join(sorted(x.upper() for x in methods))
if headers is not None and not isinstance(headers, list):
headers = ', '.join(x.upper() for x in headers)
if not isinstance(origin, list):
origin = ', '.join(origin)
if isinstance(max_age, timedelta):
max_age = max_age.total_seconds()
def get_methods():
if methods is not None:
return methods
options_resp = current_app.make_default_options_response()
return options_resp.headers['allow']
def decorator(f):
def wrapped_function(*args, **kwargs):
if automatic_options and request.method == 'OPTIONS':
resp = current_app.make_default_options_response()
else:
resp = make_response(f(*args, **kwargs))
if not attach_to_all and request.method != 'OPTIONS':
return resp
h = resp.headers
h['Access-Control-Allow-Origin'] = origin
h['Access-Control-Allow-Methods'] = get_methods()
h['Access-Control-Max-Age'] = str(max_age)
if headers is not None:
h['Access-Control-Allow-Headers'] = headers
return resp
f.provide_automatic_options = False
return update_wrapper(wrapped_function, f)
return decorator
#app.route("/")
def home():
return "hi"
#app.route("/index")
#app.route('/login', methods=['GET', 'POST', 'OPTIONS'])
#crossdomain(origin='*')
def login():
message = None
if request.method == 'POST':
datafromjs = request.form['mydata']
result = "test"
resp = make_response('{"response": '+result+'}')
resp.headers['Content-Type'] = "application/json"
resp.headers.add('Access-Control-Allow-Origin', '*')
resp.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
resp.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS')
return resp
return render_template('login.html', message='')
if __name__ == "__main__":
app.run(debug = True)
And this is the console output onClick from the browser:
and Flask:
The javascript is trying to fetch the output of a few python functions given a few parameters.
I'm just at an impasse here and it's the last cog in my project. I have no prior experience with this kind of stuff but any help is appreciated!
I believe this is a CORS issue, like JJ Hakala points out. Even if you are going to the same domain (localhost) but on a different port (8080 -> 5000) then you need an authorisation header to be present (server side).
If you can ensure that the header access-control-allow-origin:* is present (a wildcard domain approach) in server responses, then you should have no more ajax issues.
https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
I have a GET and PUT request built below:
from flask import Flask
from flask_restful import Api, Resource, reqparse
app = Flask(__name__)
api = Api(app)
userStorage =[
{
"id": "1234",
"currentBot": "BestBot"
}
]
class User(Resource):
def get(self, id):
for user in userStorage:
if(id == user["id"]):
return user, 200
return "User not found", 404
def put(self, id):
parser = reqparse.RequestParser()
parser.add_argument("currentBot")
args = parser.parse_args()
for user in userStorage:
if(id == user["id"]):
user["currentBot"] = args["currentBot"]
return user, 200
user = {
"id": id,
"currentBot": args["currentBot"]
}
userStorage.append(user)
return user, 201
def delete(self, id):
global userStorage
userStorage = [user for user in userStorage if user["id"] != id]
return "{} is deleted.".format(id), 200
api.add_resource(User, "/user/<string:id>")
app.run(debug = True, port = 4000)
Postman can properly get a response 200 when I do a simple get request but when I try to do a request through my own program it returns a 404
import requests
payload2Storage = {
"currentBot": "BestBot"
}
headers = {"Content-Type": "application/json"}
params = {
"id": "1234"
}
#response = requests.request("PUT", "http://127.0.0.1:4000/user/", data=payload2Storage, params=params, headers=headers)
response2 = requests.request("GET", "http://127.0.0.1:4000/user/", params=params, headers=headers)
Is there something wrong with my request to get the info from userStorage?
In the client code, changing from 127.0.0.1 to localhost worked for me. Try this:
response2 = requests.request("GET", "http://localhost:4000/user/", params=params, headers=headers)
OR in the server code, bind the server to 127.0.0.1 explicitly via host argument like this:
app.run(debug = True, port = 4000, host='127.0.0.1')
Other error in the code is user["id"] returns a str while id is a int, change the code as below:
def get(self, id):
for user in userStorage:
if(id == int(user["id"])):
return user, 200
return "User not found", 404
I am sending GET request from localhost:8000 to flask :
$(document).ready(function() {
$('#test').click(function() {
$.getJSON("http://localhost:5000/test/", function() {
}).done(function() {
document.location = "http://localhost:5000";
}).fail(function() {
console.log("server not ready.");
});
});
});
and in "server.py" I am handling GET like:
import app
#server.route('/test/',methods = ['GET'])
def test():
print "in test status check"
return jsonify({'status':'OK'})
However I am getting this error:
XMLHttpRequest cannot load http://127.0.0.1:**5000**/test/. Origin http://127.0.0.1:**8000** is not allowed by Access-Control-Allow-Origin.
In flask you can create custom decorator to control Access Origin Policy. This article may help you: http://flask.pocoo.org/snippets/56/
Code from article:
from datetime import timedelta
from flask import make_response, request, current_app
from functools import update_wrapper
def crossdomain(origin=None, methods=None, headers=None,
max_age=21600, attach_to_all=True,
automatic_options=True):
if methods is not None:
methods = ', '.join(sorted(x.upper() for x in methods))
if headers is not None and not isinstance(headers, basestring):
headers = ', '.join(x.upper() for x in headers)
if not isinstance(origin, basestring):
origin = ', '.join(origin)
if isinstance(max_age, timedelta):
max_age = max_age.total_seconds()
def get_methods():
if methods is not None:
return methods
options_resp = current_app.make_default_options_response()
return options_resp.headers['allow']
def decorator(f):
def wrapped_function(*args, **kwargs):
if automatic_options and request.method == 'OPTIONS':
resp = current_app.make_default_options_response()
else:
resp = make_response(f(*args, **kwargs))
if not attach_to_all and request.method != 'OPTIONS':
return resp
h = resp.headers
h['Access-Control-Allow-Origin'] = origin
h['Access-Control-Allow-Methods'] = get_methods()
h['Access-Control-Max-Age'] = str(max_age)
if headers is not None:
h['Access-Control-Allow-Headers'] = headers
return resp
f.provide_automatic_options = False
return update_wrapper(wrapped_function, f)
return decorator
And here is how you can use it:
#app.route('/my_service')
#crossdomain(origin='*')
def my_service():
return jsonify(foo='cross domain ftw')