Catching Exception in Dynamodb Python query - aws-lambda

I am trying to get values from dynamodb using partition key, the code is working fine if partition key value is found. But when partition key is not found exception is thrown and its not getting catched.
In the code i have made use of custom layer d_c_fun which has logger function defined in that.
I have tried elseif function with below logic.
if partition key "data_id" is found then values is returned properly. but when key value is not present it is throwing error instead of giving response as from exception blocks as below.
import json
import boto3
import logging
import d_c_fun as cf
LOGGER = cf.get_logger()
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('tablename')
def lambda_handler(event, context):
LOGGER.lambda_init(event, context)
# Event value received through trigger
D_Id = event['D_Id']
if not D_Id:
raise Exception ("data_id value not passed")
#LOGGER.error(""data_id value not passed"")
elif D_Id :
resp = table.get_item(Key={
"data_id":D_Id})
LOGGER.info("Response Output value for the %s: %s", D_Id, resp['Item'])
return resp['Item']
else:
raise Exception ("The data_id - %s provided doesn't exist in db", D_Id)
1) o/p: When data_id is matched in table as below.
Input event
{ "D_Id": "data2" }
output from code:
{ "table_name": "data2", "data_id": "data2" }
2) o/p: When data_id is not matched table as below.
input event value:
{ "D_Id": "data" }
output from code:
{ "errorMessage": "'Item'", "errorType": "KeyError",
"stackTrace": [
" File \"/var/task/demo.py\", line 23, in lambda_handler\n LOGGER.info(\"Response Output value for the %s: %s\", D_Id,
resp['Item'])\n" ] }
Expected o/p should be in case of data_id is not matched.
The data_id - data provided doesn't exist in db".
3) o/p: When data_id is pass as null value.
input event value:
{ "D_Id": "" }
output from code:
{ "errorMessage": "data_id value not passed", "errorType":
"Exception", "stackTrace": [
" File \"/var/task/demo.py\", line 18, in lambda_handler\n raise Exception (\"data_id not set\")\n" ] }
expected output should be.
"errorMessage": "data_id value not passed",

My original answer is totally out of your purpose. Try this.
import json
import boto3
import logging
import d_c_fun as cf
LOGGER = cf.get_logger()
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('tablename')
def lambda_handler(event, context):
LOGGER.lambda_init(event, context)
# Event value received through trigger
D_Id = event['D_Id']
if not D_Id:
raise Exception ("data_id value not passed")
#LOGGER.error(""data_id value not passed"")
else:
try:
resp = table.get_item(Key={"data_id":D_Id})
LOGGER.info("Response Output value for the %s: %s", D_Id, resp['Item'])
return resp['Item']
except:
raise Exception ("The data_id - %s provided doesn't exist in db", D_Id)

I have developed this code as well and its working as expected :).
import json
import boto3
import logging
from botocore.exceptions import ClientError
import d_c_fun as cf
LOGGER = cf.get_logger()
def get_dynamodb_client():
dynamodb = boto3.client('dynamodb', region_name='REGION_NAME')
return dynamodb
def get_dynamodb_resource():
dynamodb = boto3.resource('dynamodb', region_name='REGION_NAME')
return dynamodb
def lambda_handler(event, context):
D_Id = event['D_Id']
if not D_Id:
raise NameError("Field data_id is empty, Please enter data_id")
return resp['Item']
resp = get_dynamodb_resource().Table("TABLE_NAME").get_item(Key={
"data_id":D_Id})
if 'Item' not in resp:
raise KeyError(
'Data "{}" not found. '.format(D_Id) +
'Please enter valid data_id.'
)
return resp['Item']
else:
LOGGER.info("Got the Item Successfully - \n %s", resp['Item'])
return resp['Item']

Related

get_item in Lambda to read one item from dynamodb table having issues

I have a simple table with agentId (PK) and agentRole(SK). I am trying read one item from that table. Using boto3 client
I am getting syntax error on print statement.
import json
import boto3
client = boto3.client('dynamodb')
def lambda_handler(event, context):
response = client.get_item(
TableName='USBCallCenterTable',
Key={
'agentId': {
'N': str(1)
},
'agentRole':{
'S': 'Manager'
}
}
)
print(response)
"errorMessage": "name 'response' is not defined",
"errorType": "NameError",
"requestId": "",
Print is not an argument of get_item. Try to put the print outside get_item. I think it should solve the problem.

Creating a dynamodb table using Lambda function (python) - error

I have defined 3 attributes in that table definition. agentId, agentName, agentRole. I want to create KeySchema on agentId (partitionkey) , agentRole (range key).
In my understanding the table can have 10 attributes. All those 10 attributes don't have to be part of the KeySchema. Because Keyschema is used to identify unique records. Right?
It throws the following error:
Response
{
"errorMessage": "An error occurred (ValidationException) when calling the
CreateTable operation: One or more parameter values were invalid: Number of attributes in
KeySchema does not exactly match number of attributes defined in AttributeDefinitions",
"errorType": "ClientError",
"requestId": "d8d07c59-f36c-4989-9ac2-6ada9d8f6521",
"stackTrace": [
" File \"/var/task/lambda_function.py\", line 8, in lambda_handler\n
response = client.create_table(\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"
]
}
import json
import boto3
client = boto3.client("dynamodb")
def lambda_handler(event, context):
response = client.create_table(
AttributeDefinitions=[
{
'AttributeName': 'agentId',
'AttributeType': 'N'
},
{
'AttributeName': 'agentRole',
'AttributeType': 'S'
},
{
'AttributeName': 'agentName',
'AttributeType': 'S'
}
],
TableName='CallCenterCallsTable,
KeySchema=[
{
'AttributeName': 'agentId',
'KeyType': 'HASH'
},
{
'AttributeName': 'agentRole',
'KeyType': 'RANGE'
}
],
BillingMode='PROVISIONED',
ProvisionedThroughput={
'ReadCapacityUnits': 1,
'WriteCapacityUnits': 1
}
)
print(response)
Remove agentName from the Attribute definitions.
See the documentation for Attribute Definitions:
Represents an attribute for describing the key schema for the table and indexes.
You aren't using agentName in the key schema or indexes, so it shouldn't be included in the table definition. DynamoDB is schemaless. You only need to define the hash key and sort key at creation time. DynamoDB doesn't care about any other attributes you may want to insert into your table.

Custom format for serializer ValidationError

Iv'e got a custom object-level validator in one of my serializers:
def validate(self, data):
# some checks on token
# set token to True or False
if not token:
raise serializers.ValidationError(
{
"status": "failed",
"message": _("token is not valid"),
}
)
return data
What I expect to get as an output is this:
{
"status": "failed",
"message": "token is not valid"
}
However, what I'm actually getting is:
{
"status": [
"failed"
],
"message": [
"token is not valid"
]
}
Is there anyway to achieve what I'm looking for?
Create a custom ValidatorError class:
from rest_framework import serializers, status
from rest_framework.exceptions import APIException
class PlainValidationError(APIException):
status_code = status.HTTP_400_BAD_REQUEST
default_detail = _("Invalid input.")
default_code = "invalid"
def __init__(self, detail=None, code=None):
if not isinstance(detail, dict):
raise serializers.ValidationError("Invalid Input")
self.detail = detail
Instead of using serializers.ValidationError use your custom ValidationError class:
def validate(self, data):
# some checks on token
# set token to True or False
if not token:
raise PlainValidationError(
{
"status": "failed",
"message": _("token is not valid"),
}
)
return data
It's not perfect but it does the job for me.

pytest json.decoder.JSONDecodeError

List item
Hi,
I need to pytest this function
def lambda_handler(event, context):
message = json.loads(event['Records'][0]['Sns']['Message'])
But it failed by json error
def test_lambda_handler():
event = {
"Records": [
{
"Sns" : { "Message" : "test" }
}
]
}
response = fw_init.lambda_handler( event,"")
JSONDecodeError("Expecting value", s, err.value) from None
E json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
Function loads() deserializes JSON from string. You trying to decode "Message" field value as JSON.
lambda_handler function's first argument is a JSON-formatted string, according to AWS documentation.
You need to pass an serialized data to lambda_handler function:
response = fw_init.lambda_handler(json.dumps(event) ,"")
In function lambda_handler() you need to deserialize data first, and then get field value:
def lambda_handler(event, context):
data = json.loads(event)
message = data['Records'][0]['Sns']['Message']

How to return customized JSON response for an error in graphene / django-graphene?

I want to add status field to error response, so instead of this:
{
"errors": [
{
"message": "Authentication credentials were not provided",
"locations": [
{
"line": 2,
"column": 3
}
]
}
],
"data": {
"viewer": null
}
}
It should be like this:
{
"errors": [
{
"status": 401, # or 400 or 403 or whatever error status suits
"message": "Authentication credentials were not provided",
"locations": [
{
"line": 2,
"column": 3
}
]
}
],
"data": {
"viewer": null
}
}
I found out that I only can change message by raising Exception inside resolver: raise Error('custom error message'), but how to add field?
Code example:
class Query(UsersQuery, graphene.ObjectType):
me = graphene.Field(SelfUserNode)
def resolve_me(self, info: ResolveInfo):
user = info.context.user
if not user.is_authenticated:
# but status attr doesn't exist...
raise GraphQLError('Authentication credentials were not provided', status=401)
return user
Update the default GraphQLView with the following:
from graphene_django.views import GraphQLView as BaseGraphQLView
class GraphQLView(BaseGraphQLView):
#staticmethod
def format_error(error):
formatted_error = super(GraphQLView, GraphQLView).format_error(error)
try:
formatted_error['context'] = error.original_error.context
except AttributeError:
pass
return formatted_error
urlpatterns = [
path('api', GraphQLView.as_view()),
]
This will look for the context attribute in any exceptions raised. If it exists, it'll populate the error with this data.
Now you can create exceptions for different use cases that populate the context attribute. In this case you want to add the status code to errors, here's an example of how you'd do that:
class APIException(Exception):
def __init__(self, message, status=None):
self.context = {}
if status:
self.context['status'] = status
super().__init__(message)
You'd use it like this:
raise APIException('Something went wrong', status=400)
I didn't found a way to solve your problem int the way that you propose, otherwise i extend the LoginRequiredMixin class like this:
class LoginRequiredMixin:
def dispatch(self, info, *args, **kwargs):
if not info.user.is_authenticated:
e = HttpError(HttpResponse(status=401, content_type='application/json'), 'Please log in first')
response = e.response
response.content = self.json_encode(info, [{'errors': [self.format_error(e)]}])
return response
return super().dispatch(info, *args, **kwargs)
class PrivateGraphQLView(LoginRequiredMixin, GraphQLView):
schema=schema
and in your url:
from django.views.decorators.csrf import csrf_exempt
from educor.schema import PrivateGraphQLView
url(r'^graphql', csrf_exempt(PrivateGraphQLView.as_view(batch=True)))
you can't see the status with the graphiql but in your client you can get it in the headers or you could modify this line to add in the response response.content = self.json_encode(info, [{'errors': [self.format_error(e)]}])
. Hope it helps anyway i'll leave you another possible solution https://github.com/graphql-python/graphene-django/issues/252

Resources