pytest json.decoder.JSONDecodeError - aws-lambda

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']

Related

Passing diffrent time slots in same field Django Rest framework

I want to send 3 diffrent time slots in a field ,But do not getting the diffrent slots,but getting error-->"message": "JSON parse error - Expecting property name enclosed in double quotes: line 3 column 9 (char 27)",
models.py
class Slot(models.Model):
time=models.TimeField(auto_now=False, auto_now_add=False,null=True,blank=True)
def __str__(self):
return str(self.time)
views.py
class Available(viewsets.ViewSet):
def create(self, request):
try:
data=request.data
timings=data.get('schedule')
for i in range(3):
time_obj=Slot()
time_obj.time=timings[i]['time']
time_obj.save()
return Response({"message":"Data delivered","success": True}, status=status.HTTP_200_OK)
except Exception as error:
traceback.print_exc()
return Response({"message": str(error), "success": False}, status=status.HTTP_200_OK)
postman response
{
"schedule":{
{
"time":"12:00"
},
{
"time":"12:10"
},
{
"time":"15:00"
}
}
}
looks like the data sent is not valid JSON it should be something like
{
"schedule":[
{
"time":"12:00"
},
{
"time":"12:10"
},
{
"time":"15:00"
}
]
}

Return any data from a query using GraphQL, Graphene and Python

I am receiving the following error:
{
"errors": [
{
"message": "Unknown argument \"project_id\" on field" +
\"get_project_detail_summary\" of type \"Query\".",
"locations": [
{
"line": 2,
"column": 30
}
]
}
]
}
With the following query:
query GetProjectDetailSummary($project_id: Int) {
get_project_detail_summary(project_id: $project_id) {
comments {
... on ManagerCommentNode {
id
text
created
}
... on VendorCommentNode {
id
text
created
}
... on TenantCommentNode {
id
text
created
}
}
}
}
With the following backend code, How can I get to the breakpoint?, or how do I send back custom data given a number?:
class CommentsUnion(graphene.types.union.Union):
class Meta:
name = 'CommentsUnion'
types = (ManagerCommentNode, VendorCommentNode, TenantCommentNode, )
class ProjectSummaryInput(graphene.InputObjectType):
project_id = graphene.Int()
class ProjectSummaryNode(graphene.ObjectType):
Input = ProjectSummaryInput
project_id = graphene.Int()
comments = graphene.List(CommentsUnion)
#classmethod
def resolve_comments(self, *args, **kwargs):
import pdb;pdb.set_trace()
return ProjectSummary.select_related('comments').objects.filter(comments__created__lt=dt)
class Query(graphene.ObjectType):
get_project_detail_summary = Field(ProjectSummaryNode)
In regards to the error.
Be sure to add a kwarg ( e.g. project_id in this example, it is the reason for the "unknown argument on field" error ) to the graphene.Field for get_project_detail_summary.
Like so:
class Query(graphene.ObjectType):
get_project_detail_summary = Field(ProjectSummaryNode, # see below for example
project_id=graphene.Int() # kwarg here
)
def resolve_get_project_detail_summary(self, info, **kwargs):
return ProjectSummary.objects.get(id=kwargs.get('project_id'))
In regards to returning any data.
This is a way ( returning a graphene.String ), but it untypes the response by putting everything inside a String:
from django.core.serializers.json import DjangoJSONEncoder
class ProjectSummaryRecentUpdatesNode(graphene.ObjectType):
Input = ProjectSummaryInput
recent_updates = graphene.String()
def resolve_recent_updates(self, resolve, **kwargs):
instance = Project.objects.get(id=resolve.variable_values.get("project_id"))
things = instance.things.all()
# these are all from different models, and the list is a bit longer than this.
querysets = (
("scheduled", get_scheduled(things, resolve, **kwargs)),
("completed", get_completed(things, resolve, **kwargs)),
("invoices", get_invoices(things, resolve, **kwargs)),
("expenditures", get_expenditures(things, resolve, **kwargs)),
("comments", get_comments(things, resolve, **kwargs)),
("files", get_files(things, resolve, **kwargs)),
)
recent_updates = []
for update_type, qs in querysets:
for item in qs:
item.update(
{
"recent_update_type": update_type
}
)
recent_updates.append(item)
return json.dumps(recent_updates, cls=DjangoJSONEncoder)
And on the frontend, with an import { Query } from "react-apollo"; element we can JSON.parse the field ... which has "any" (json serialized) data inside of it:
<Query
query={GET_PROJECT_DETAIL_SUMMARY_RECENT_UPDATES}
fetchPolicy="network-only"
variables={{ project_id: this.props.projectId }}
>
{({ loading, error, data }: QueryResult) => {
if (
data &&
data.get_project_detail_summary_recent_updates &&
data.get_project_detail_summary_recent_updates.recent_updates
) {
console.log(JSON.parse(data.get_project_detail_summary_recent_updates.recent_updates))
}
}}
</Query>
Side note:
If there isn't a large list of data types create a Union like object which has all of the fields needed from different models, or an actual Union, which seems like the correct way, as then types are not lost:
class ProjectSummaryNode(graphene.ObjectType):
# from FileNode graphene.ObjectType class
file__id = graphene.Int()
file__brief_description = graphene.String()
file_count = graphene.Int()
last_file_created = graphene.String()
last_file = graphene.String()
# from UploaderNode graphene.ObjectType class
uploader__first_name = graphene.String()
uploader__last_name = graphene.String()
# from CommentNode graphene.ObjectType class
comment = graphene.String()
class Meta:
name = "ProjectSummaryNode"
# example of Union, the above fields would be commented out, as they are part of
# the different graphene.ObjectType classes, and this line below would be uncommented
# types = ( FileNode, UploaderNode, CommentNode, )
Still open to suggestions on a better way to do this.

Catching Exception in Dynamodb Python query

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']

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

django: Testing POST-based views with json objects

I have a django application with several views that accept json objects via POST requests. The json objects are medium-complex with a few layers of nesting, so I'm using the json library to parse raw_post_data, as shown here:
def handle_ajax_call(request):
post_json = json.loads(request.raw_post_data)
... (do stuff with json query)
Next, I want to write tests for these views. Unfortunately, I can't figure out how to pass the json object to the Client. Here's a simplest-case version of my code:
def test_ajax_call(self):
c = Client()
call_command('loadfixtures', 'temp-fixtures-1') #Custom command to populate the DB
J = {
some_info : {
attr1 : "AAAA",
attr2 : "BBBB",
list_attr : [ "x", "y", "z" ]
},
more_info : { ... },
info_list : [ 1, 22, 23, 24, 5, 26, 7 ]
}
J_string = json.dumps(J)
response = c.post('/ajax/call/', data=J_string )
When I run the test, it fails with:
AttributeError: 'str' object has no attribute 'items'
How can I pass the JSON object in the Client.post method?
The documentation seems to imply that if you pass a content_type parameter to client.post, it will treat the data value as a document and POST it directly. So try this:
response = c.post('/ajax/call/', content_type='application/json', data=J_string)

Resources