aletrn commited on
Commit
3dbe011
·
1 Parent(s): f2a79fa

[feat] handle better error responses

Browse files
README.md CHANGED
@@ -13,21 +13,24 @@ Build the docker image:
13
  # clean any old active containers
14
  docker stop $(docker ps -a -q); docker rm $(docker ps -a -q)
15
 
16
- # build the image, use the tag "semgeo"
17
- docker build . --tag semgeo --progress=plain
 
 
 
18
  ```
19
 
20
  Run the container (keep it on background) and show logs
21
 
22
  ```bash
23
- docker run -d --name semgeo -p 7860:7860 semgeo; docker logs -f semgeo
24
  ```
25
 
26
  Test it with curl:
27
 
28
  ```bash
29
  curl -X 'POST' \
30
- 'http://localhost:7860/infer_samgeo' \
31
  -H 'accept: application/json' \
32
  -d '{}'
33
  ```
 
13
  # clean any old active containers
14
  docker stop $(docker ps -a -q); docker rm $(docker ps -a -q)
15
 
16
+ # build the base docker image with the docker aws repository tag
17
+ docker build . -f dockerfiles/dockerfile-lambda-gdal-runner --tag 686901913580.dkr.ecr.eu-west-1.amazonaws.com/lambda-gdal-runner
18
+
19
+ # build the final docker image
20
+ docker build . -f dockerfiles/dockerfile-samgeo-api --tag lambda-samgeo-api --progress=plain
21
  ```
22
 
23
  Run the container (keep it on background) and show logs
24
 
25
  ```bash
26
+ docker run -d --name lambda-samgeo-api -p 8080:8080 lambda-samgeo-api; docker logs -f lambda-samgeo-api
27
  ```
28
 
29
  Test it with curl:
30
 
31
  ```bash
32
  curl -X 'POST' \
33
+ 'http://localhost:8080/infer_samgeo' \
34
  -H 'accept: application/json' \
35
  -d '{}'
36
  ```
dockerfiles/{dockerfile-base-webserver → dockerfile-lambda-gdal-runner} RENAMED
@@ -8,7 +8,6 @@ ARG RIE="https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/l
8
  # Set working directory to function root directory
9
  WORKDIR ${LAMBDA_TASK_ROOT}
10
  COPY requirements_dev.txt ${LAMBDA_TASK_ROOT}/requirements_dev.txt
11
- COPY ./src ${LAMBDA_TASK_ROOT}/src
12
 
13
  RUN apt update && apt install -y curl python3-pip
14
  RUN python -m pip install -r ${LAMBDA_TASK_ROOT}/requirements_dev.txt --target ${LAMBDA_TASK_ROOT}
@@ -21,4 +20,3 @@ RUN chmod +x /lambda-entrypoint.sh
21
  RUN ls -l /lambda-entrypoint.sh
22
 
23
  ENTRYPOINT ["/lambda-entrypoint.sh"]
24
- CMD [ "src.app.lambda_handler" ]
 
8
  # Set working directory to function root directory
9
  WORKDIR ${LAMBDA_TASK_ROOT}
10
  COPY requirements_dev.txt ${LAMBDA_TASK_ROOT}/requirements_dev.txt
 
11
 
12
  RUN apt update && apt install -y curl python3-pip
13
  RUN python -m pip install -r ${LAMBDA_TASK_ROOT}/requirements_dev.txt --target ${LAMBDA_TASK_ROOT}
 
20
  RUN ls -l /lambda-entrypoint.sh
21
 
22
  ENTRYPOINT ["/lambda-entrypoint.sh"]
 
dockerfiles/dockerfile-samgeo-api CHANGED
@@ -1,14 +1,13 @@
1
  FROM 686901913580.dkr.ecr.eu-west-1.amazonaws.com/lambda-gdal-runner:latest
2
 
 
3
  ARG LAMBDA_TASK_ROOT="/var/task"
4
  ARG PYTHONPATH="${LAMBDA_TASK_ROOT}:${PYTHONPATH}:/usr/local/lib/python3/dist-packages"
5
 
 
 
6
  COPY ./src ${LAMBDA_TASK_ROOT}/src
7
 
8
- COPY .env ${LAMBDA_TASK_ROOT}
9
- RUN ls -l ${LAMBDA_TASK_ROOT}/.env
10
- RUN . ${LAMBDA_TASK_ROOT}/.env
11
-
12
  RUN ls -l /usr/bin/which
13
  RUN /usr/bin/which python
14
  RUN python -v
@@ -19,13 +18,11 @@ RUN ls -l ${LAMBDA_TASK_ROOT}
19
  RUN ls -ld ${LAMBDA_TASK_ROOT}
20
  RUN python -c "import sys; print(sys.path)"
21
  RUN python -c "import osgeo"
22
- RUN python -c "import rasterio"
23
  RUN python -c "import awslambdaric"
24
  RUN python -m pip list
25
  RUN python -m pip freeze
26
  RUN df -h
27
 
28
- RUN echo "$(date "+%Y%m%d_%H%M%S")" > ${LAMBDA_TASK_ROOT}/buildtime.txt
29
- RUN python -m pip freeze > ${LAMBDA_TASK_ROOT}/pythonpackages.txt
30
-
31
- CMD [ "src.surferdtm_prediction_api.app.lambda_handler" ]
 
1
  FROM 686901913580.dkr.ecr.eu-west-1.amazonaws.com/lambda-gdal-runner:latest
2
 
3
+ # Include global arg in this stage of the build
4
  ARG LAMBDA_TASK_ROOT="/var/task"
5
  ARG PYTHONPATH="${LAMBDA_TASK_ROOT}:${PYTHONPATH}:/usr/local/lib/python3/dist-packages"
6
 
7
+ # Set working directory to function root directory
8
+ WORKDIR ${LAMBDA_TASK_ROOT}
9
  COPY ./src ${LAMBDA_TASK_ROOT}/src
10
 
 
 
 
 
11
  RUN ls -l /usr/bin/which
12
  RUN /usr/bin/which python
13
  RUN python -v
 
18
  RUN ls -ld ${LAMBDA_TASK_ROOT}
19
  RUN python -c "import sys; print(sys.path)"
20
  RUN python -c "import osgeo"
21
+ # RUN python -c "import rasterio"
22
  RUN python -c "import awslambdaric"
23
  RUN python -m pip list
24
  RUN python -m pip freeze
25
  RUN df -h
26
 
27
+ ENTRYPOINT ["/lambda-entrypoint.sh"]
28
+ CMD [ "src.app.lambda_handler" ]
 
 
requirements_dev.txt CHANGED
@@ -1,6 +1,6 @@
1
  awslambdaric
2
  aws_lambda_powertools
3
  fastjsonschema
4
- pydantic
5
  jmespath
6
-
 
 
1
  awslambdaric
2
  aws_lambda_powertools
3
  fastjsonschema
 
4
  jmespath
5
+ pydantic
6
+ requests
src/app.py CHANGED
@@ -1,6 +1,8 @@
1
  import json
2
  import time
 
3
  from aws_lambda_powertools import Logger
 
4
  from aws_lambda_powertools.utilities.typing import LambdaContext
5
  from pydantic import BaseModel, ValidationError
6
 
@@ -10,18 +12,27 @@ from src.utilities.type_hints import input_floatlist, input_floatlist2
10
  logger = Logger()
11
 
12
 
 
 
 
 
 
13
  class BBoxWithPointInput(BaseModel):
14
  bbox: input_floatlist
15
  points: input_floatlist2
 
 
 
16
 
17
 
18
- def get_response(status: int, start_time: float, output: BaseModel = None) -> dict[str]:
19
  """
20
  Return a response for frontend clients.
21
 
22
  Args:
23
  status: status response
24
  start_time: request start time (float)
 
25
  output: dict we embed into our response
26
 
27
  Returns:
@@ -29,17 +40,22 @@ def get_response(status: int, start_time: float, output: BaseModel = None) -> di
29
 
30
  """
31
  messages = {200: "ok", 422: "validation error", 500: "internal server error"}
32
- body = {"duration_run": time.time() - start_time, "message": messages[status]}
33
  if status == 200:
34
- body = {"output": output.model_dump_json(), **body}
35
- return {
36
- "statusCode": status,
37
- "headers": {'Content-type': 'application/json', 'Accept': 'application/json'},
38
- "body": json.dumps(body)
39
- }
 
 
 
 
 
40
 
41
 
42
- def lambda_handler(event: dict, context: LambdaContext) -> dict[str]:
43
  logger.info(f"start with aws_request_id:{context.aws_request_id}.")
44
  start_time = time.time()
45
  try:
@@ -49,13 +65,13 @@ def lambda_handler(event: dict, context: LambdaContext) -> dict[str]:
49
  try:
50
  bbox_points = BBoxWithPointInput(bbox=event["bbox"], points=event["points"])
51
  logger.info(f"validation ok, bbox_points:{bbox_points}...")
52
- response = get_response(200, start_time, bbox_points)
53
  except ValidationError as ve:
54
  logger.error(f"validation error:{ve}.")
55
- response = get_response(422, start_time)
56
  except Exception as e:
57
  logger.error(f"exception:{e}.")
58
- response = get_response(500, start_time)
59
 
60
- logger.info(f"response:{response}...")
61
  return response
 
1
  import json
2
  import time
3
+ from http import HTTPStatus
4
  from aws_lambda_powertools import Logger
5
+ from aws_lambda_powertools.event_handler import content_types, Response
6
  from aws_lambda_powertools.utilities.typing import LambdaContext
7
  from pydantic import BaseModel, ValidationError
8
 
 
12
  logger = Logger()
13
 
14
 
15
+ class JSONResponse(Response):
16
+ def response_to_json(self):
17
+ return json.dumps(self.__dict__)
18
+
19
+
20
  class BBoxWithPointInput(BaseModel):
21
  bbox: input_floatlist
22
  points: input_floatlist2
23
+ duration_run: float = 0
24
+ message: str = ""
25
+ request_id: str = ""
26
 
27
 
28
+ def get_response(status: int, start_time: float, request_id: str, output: BBoxWithPointInput = None) -> str:
29
  """
30
  Return a response for frontend clients.
31
 
32
  Args:
33
  status: status response
34
  start_time: request start time (float)
35
+ request_id: str
36
  output: dict we embed into our response
37
 
38
  Returns:
 
40
 
41
  """
42
  messages = {200: "ok", 422: "validation error", 500: "internal server error"}
43
+ body = f"{messages[status]}, request_id: {request_id}."
44
  if status == 200:
45
+ output.duration_run = time.time() - start_time
46
+ output.message = messages[status]
47
+ output.request_id = request_id
48
+ body = output.model_dump_json()
49
+ response = JSONResponse(
50
+ status_code=status,
51
+ content_type=content_types.APPLICATION_JSON if status == 200 else content_types.TEXT_PLAIN,
52
+ body=body
53
+ )
54
+ logger.info(f"response type:{type(response)} => {response}.")
55
+ return response.response_to_json()
56
 
57
 
58
+ def lambda_handler(event: dict, context: LambdaContext):
59
  logger.info(f"start with aws_request_id:{context.aws_request_id}.")
60
  start_time = time.time()
61
  try:
 
65
  try:
66
  bbox_points = BBoxWithPointInput(bbox=event["bbox"], points=event["points"])
67
  logger.info(f"validation ok, bbox_points:{bbox_points}...")
68
+ response = get_response(HTTPStatus.OK.value, start_time, context.aws_request_id, bbox_points)
69
  except ValidationError as ve:
70
  logger.error(f"validation error:{ve}.")
71
+ response = get_response(422, start_time, context.aws_request_id)
72
  except Exception as e:
73
  logger.error(f"exception:{e}.")
74
+ response = get_response(500, start_time, context.aws_request_id)
75
 
76
+ logger.info(f"response_dumped:{response}...")
77
  return response