MousaZeidBaker / aws-lambda-typing

Python type hints for AWS Lambda
MIT License
106 stars 19 forks source link

Fix APIGatewayProxyResponseV2 #17

Closed NiumXp closed 2 years ago

NiumXp commented 2 years ago

In v2, all fields can be omitted and when all are omitted, we can pass str, dict, list, etc... which will be converted to body.

MousaZeidBaker commented 2 years ago

Thanks for your contribution @NiumXp. Thats correct in v2 the API Gateway can infer the response, more about this here.

I don't understand the purpose of the SimpleJson type, isn't it enough to just add the totality flag? Then any of the keys can be omitted and at the same time you can not add any extra.

NiumXp commented 2 years ago

Thanks for your contribution @NiumXp. Thats correct in v2 the API Gateway can infer the response, more about this here. But I don't understand the purpose of the SimpleJson type, isn't it enough to just add the totality flag? Then any of the keys can be omitted and at the same time you can not add any extra.

I found this problem when I tried use a function like this

from aws_lambda_typing.responses.api_gateway_proxy import APIGatewayProxyResponseV2
from aws_lambda_typing.events.api_gateway_proxy import APIGatewayProxyEventV2
from aws_lambda_typing.context.context import Context

def handler(
    event: APIGatewayProxyEventV2,
    context: Context,
) -> APIGatewayProxyResponseV2:
    return {
        "statusCode": 200,
    }
Expression of type "dict[str, int]" cannot be assigned to return type "APIGatewayProxyResponseV2"
  "dict[str, int]" is incompatible with "APIGatewayProxyResponseV2"

We can fix that using the total of TypedDict.

But, actually I have functions that don't use any fields in APIGatewayProxyResponseV2 and returns a different dict.

from aws_lambda_typing.responses.api_gateway_proxy import APIGatewayProxyResponseV2
from aws_lambda_typing.events.api_gateway_proxy import APIGatewayProxyEventV2
from aws_lambda_typing.context.context import Context

def handler(
    event: APIGatewayProxyEventV2,
    context: Context,
) -> APIGatewayProxyResponseV2:
    return {
        "error": "Can't access ...; try again.",
        "hash": 123,
    }
Expression of type "dict[str, str | int]" cannot be assigned to return type "APIGatewayProxyResponseV2"
  "dict[str, str | int]" is incompatible with "APIGatewayProxyResponseV2"

According to AWS, the dict will be passed to the body field. Actually I need to create a TypedDict with the fields or do something like that:

{
    "body": os.dumps({
        "error": "Can't access ...; try again.",
        "hash": 123,
    })
}

If in the future I need to use statusCode, I will need to delete the type I created instead of just moving the fields to body.

MousaZeidBaker commented 2 years ago

I see what you are saying, but if you let the API Gateway infer the response format for you then your function is not returning an APIGatewayProxyResponseV2 response, you are actually just returning the body (which you mention yourself). Rest of the fields are set by the API Gateway for you.

If we change APIGatewayProxyResponseV2 to accept anything then the whole purpose of using specific type hints is defeated. I think you need to decide whether you want your functions to return an APIGatewayProxyResponseV2 response or just the body. If you choose the later then your functions return type could be typing.Any as the body can be a string, dictionary, list etc. If you choose the former, then the following is how it should be used

def handler(event: APIGatewayProxyEventV2, context: Context) -> APIGatewayProxyResponseV2:
    return {
        "body": json.dumps({
            "id": 123,
            "message": "hello world",
        })
    }

and if you want to use another field in the future, you can simply append it

def handler(event: APIGatewayProxyEventV2, context: Context) -> APIGatewayProxyResponseV2:
    return {
        "statusCode": 200,
        "body": json.dumps({
            "id": 123,
            "message": "hello world",
        })
    }
MousaZeidBaker commented 2 years ago

Could you also make a patch version bump of the project in the pyproject.toml ?