pygrestqlambda 0.0.3__py3-none-any.whl → 0.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,9 +6,9 @@ from datetime import date, datetime
6
6
  from decimal import Decimal
7
7
  from uuid import UUID
8
8
 
9
- def json_output(value: object) -> str:
9
+ def to_string(value: object) -> str:
10
10
  """
11
- Calculates the serialised version of an object to return in a JSON response
11
+ Calculates the string version of an object to return in a JSON response
12
12
  """
13
13
 
14
14
  # Handle UUIDs
@@ -6,11 +6,11 @@ Returns payload structure expected by REST API Gateway
6
6
  https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format
7
7
  """
8
8
 
9
- from base64 import b64encode
9
+ from base64 import b64encode, b64decode
10
10
  from dataclasses import dataclass
11
11
  import json
12
12
  import logging
13
- from pygrestqlambda.aws.lambda_function.json_transform import json_output
13
+ from pygrestqlambda.aws.lambda_function.json_transform import to_string
14
14
 
15
15
 
16
16
  @dataclass
@@ -22,25 +22,40 @@ class Response:
22
22
  status_code: int | None = 401
23
23
  headers: dict | None = None
24
24
  multi_value_headers: dict | None = None
25
- body: str | None = None
25
+ body: str | dict | None = None
26
26
 
27
27
  def get_payload(self) -> dict:
28
28
  """
29
29
  Gets payload to send to REST API Gateway
30
30
  """
31
31
 
32
+ is_json = False
33
+ if isinstance(self.body, dict):
34
+ is_json = True
35
+
32
36
  # Set headers
33
37
  if self.headers is None:
34
38
  self.headers = {}
35
39
 
36
40
  if "Content-Type" not in self.headers:
37
- self.headers["Content-Type"] = "application/json"
41
+ logging.debug("No content type header set")
42
+ if is_json:
43
+ logging.debug("Using application/json for content-type")
44
+ self.headers["Content-Type"] = "application/json"
45
+ else:
46
+ logging.debug("Using text/plain for content-type")
47
+ self.headers["Content-Type"] = "text/plain"
38
48
 
39
49
  # Calculate body
40
50
  if self.is_base64_encoded:
41
51
  body = b64encode(self.body).decode("utf-8")
42
52
  else:
43
- body = json.dumps(self.body, default=json_output)
53
+ if is_json:
54
+ logging.debug("Body is a JSON object")
55
+ body = json.dumps(self.body, default=to_string)
56
+ else:
57
+ logging.debug("Body is plain text")
58
+ body = self.body
44
59
 
45
60
  logging.debug("Transforming dataclass dictionary to JSON")
46
61
  data = {
@@ -52,3 +67,83 @@ class Response:
52
67
  }
53
68
 
54
69
  return data
70
+
71
+ # pylint: disable=too-many-instance-attributes
72
+ class Request:
73
+ """
74
+ Lambda function proxy integration request
75
+ """
76
+
77
+ def __init__(self, event: dict):
78
+ self.event = event
79
+ # Extract authorisation information
80
+ self.cognito_uid = self.get_cognito_uid()
81
+ # Extract headers needed for body and response
82
+ self.accept: str = self.get_header('accept')
83
+ self.content_type: str = self.get_header('content-type')
84
+ # Extract resource
85
+ self.resource = event.get('resource')
86
+ self.method = event.get('httpMethod')
87
+ # Extract parameters
88
+ self.query_params = event.get('multiValueQueryStringParameters')
89
+ self.path_params = event.get('pathParameters')
90
+ # Extract body
91
+ self.body = self.get_body()
92
+
93
+
94
+ def get_body(self):
95
+ """
96
+ Returns body from request, decodes from base64 if necessary
97
+ """
98
+
99
+ body = self.event.get('body')
100
+ content = body
101
+ if self.event.get('isBase64Encoded'):
102
+ if body:
103
+ content = b64decode(body)
104
+
105
+ # Handle no content type
106
+ if self.content_type is None:
107
+ return content
108
+
109
+ # Handle plain text
110
+ if self.content_type.lower() == 'text/plain':
111
+ return str(content)
112
+
113
+ # Handle JSON
114
+ if self.content_type.lower() == 'application/json':
115
+ return json.loads(content)
116
+
117
+ return content
118
+
119
+
120
+ def get_cognito_uid(self):
121
+ """
122
+ Retrieve Cognito UID from supplied claim
123
+ """
124
+ claims = self.event.get('requestContext', {}).get('authorizer', {}).get('claims')
125
+
126
+ if claims is None:
127
+ logging.info('No claims in event request context authoriser')
128
+ return None
129
+
130
+ cognito_uid = claims.get('sub')
131
+
132
+ return cognito_uid
133
+
134
+
135
+ def get_header(self, header_name: str):
136
+ """
137
+ Retrieve Accept header
138
+ """
139
+
140
+ headers = self.event.get('headers')
141
+ if headers is None:
142
+ return None
143
+
144
+ # Lowercase all the headers
145
+ headers_lower = {k.lower():v for k,v in headers.items()}
146
+
147
+ accept = headers_lower.get(header_name.lower())
148
+
149
+ return accept
@@ -0,0 +1,124 @@
1
+ Metadata-Version: 2.4
2
+ Name: pygrestqlambda
3
+ Version: 0.0.4
4
+ Summary: PostgreSQL REST API framework for AWS Lambda functions
5
+ Project-URL: Homepage, https://github.com/mesogate/pygrestqlambda
6
+ Project-URL: Issues, https://github.com/mesogate/pygrestqlambda/issues
7
+ Author-email: Voquis Limited <opensource@voquis.com>
8
+ License-File: LICENSE
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.11
13
+ Requires-Dist: aws-xray-sdk
14
+ Requires-Dist: boto3
15
+ Provides-Extra: dev
16
+ Requires-Dist: build; extra == 'dev'
17
+ Requires-Dist: docker; extra == 'dev'
18
+ Requires-Dist: httpx; extra == 'dev'
19
+ Requires-Dist: psycopg[binary]; extra == 'dev'
20
+ Requires-Dist: pylint; extra == 'dev'
21
+ Requires-Dist: pytest; extra == 'dev'
22
+ Requires-Dist: pytest-cov; extra == 'dev'
23
+ Requires-Dist: pytest-xdist; extra == 'dev'
24
+ Requires-Dist: ruff; extra == 'dev'
25
+ Requires-Dist: twine; extra == 'dev'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # Python PostgreSQL REST API framework for AWS Lambda functions
29
+ > [!NOTE]
30
+ > Project status: `Alpha`
31
+
32
+ A REST API web framework for persisting records in a PostgreSQL database.
33
+
34
+ ## Supported features
35
+ - Automatic creation of `uid` fields
36
+ - Automatic setting of `created_at` and `last_updated_at` timestamps
37
+ - Automatic setting of `creator_uid` and `last_updater_uid`
38
+ - RDS with IAM credentials
39
+
40
+ ## Examples
41
+ See [Examples docs directory](./docs/examples/)
42
+
43
+ ## Sequence diagrams
44
+
45
+ ### High-level infrastructure
46
+
47
+ This sequence diagram shows how a lambda function running this library is intended to be deployed.
48
+
49
+ ```mermaid
50
+ sequenceDiagram
51
+ # Set up actors and participants
52
+ actor User
53
+ participant APIGW as API Gateway
54
+ participant Cognito as Cognito
55
+ box Purple This library
56
+ participant Lambda as Lambda Function
57
+ end
58
+ participant RDS as RDS Database
59
+
60
+ # Set up Sequences
61
+ User ->> APIGW: HTTP /resource
62
+ activate APIGW
63
+ APIGW -->> Cognito: Authenticate
64
+ activate Cognito
65
+ Cognito ->> APIGW: Authenticated
66
+ deactivate Cognito
67
+ APIGW ->> Lambda: Send proxy integration request
68
+ activate Lambda
69
+ Lambda ->> RDS: Fetch/mutate
70
+ activate RDS
71
+ RDS -->> Lambda: Return records
72
+ deactivate RDS
73
+ Lambda -->> APIGW: Return response
74
+ deactivate Lambda
75
+ APIGW -->> User: Return response
76
+ deactivate APIGW
77
+ ```
78
+
79
+ ### Low-level architecture
80
+
81
+ This sequence diagram shows the layers within the library that handle request and response processing.
82
+
83
+ ```mermaid
84
+ sequenceDiagram
85
+
86
+ Participant APIGW as API Gateway
87
+ box Purple This library as a deployed Lambda Function
88
+ Participant CONT as Controller
89
+ Participant REQM as Request Mapper
90
+ Participant RESOURCEM as Resource Mapper
91
+ Participant DBM as Database Mapper
92
+ Participant RESPONSEM as Response Mapper
93
+ end
94
+ Participant RDS
95
+
96
+ APIGW ->> CONT: Send `event` dict
97
+ activate CONT
98
+ CONT ->> REQM: Map request
99
+ activate REQM
100
+ REQM ->> CONT: Mapped request
101
+ deactivate REQM
102
+
103
+ CONT ->> RESOURCEM: Map resource
104
+ activate RESOURCEM
105
+ RESOURCEM -->> CONT: Mapped resource
106
+ deactivate RESOURCEM
107
+
108
+ CONT ->> DBM: Request resource operation
109
+ activate DBM
110
+ DBM ->> RDS: Perform resource operation
111
+ activate RDS
112
+ RDS -->> DBM: Return resources
113
+ deactivate RDS
114
+ DBM -->> CONT: Return resource
115
+ deactivate DBM
116
+
117
+ CONT ->> RESPONSEM: Map response
118
+ activate RESPONSEM
119
+ RESPONSEM -->> CONT: Mapped response
120
+ deactivate RESPONSEM
121
+
122
+ CONT -->> APIGW: Return response
123
+ deactivate CONT
124
+ ```
@@ -1,11 +1,11 @@
1
1
  pygrestqlambda/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  pygrestqlambda/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  pygrestqlambda/aws/lambda_function/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- pygrestqlambda/aws/lambda_function/json_transform.py,sha256=SlUnG23gzy_zHpVP-thRYaWc91w6OFisTGrxKah6UL4,634
5
- pygrestqlambda/aws/lambda_function/rest_api_gateway_proxy_integration.py,sha256=TDn9D_BivcTPoNK1PhgymnF0V8blWaEE0cYV9v3aCLQ,1699
4
+ pygrestqlambda/aws/lambda_function/json_transform.py,sha256=Hb09h8n4tYS58vnP03QG46cV_ghCrVzfBctHdUURv6g,628
5
+ pygrestqlambda/aws/lambda_function/rest_api_gateway_proxy_integration.py,sha256=xE1w7dd-AUTQZB1Jc8DG_pqibWpasYB1rM1BQwuAJyI,4505
6
6
  pygrestqlambda/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  pygrestqlambda/db/record.py,sha256=6HwvVIB8iJxxcoLeFgk51Gik_w7--w5fQzLN0ntB8iw,1527
8
- pygrestqlambda-0.0.3.dist-info/METADATA,sha256=yshe1yJ4gp-ndptTYh93fEIGhWxabm9aWycVLBfkgfI,1294
9
- pygrestqlambda-0.0.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
- pygrestqlambda-0.0.3.dist-info/licenses/LICENSE,sha256=8QeS1c5uv4AYoEG3M80OSCBuC_Pk-6vjU1VBUnlX2m0,1071
11
- pygrestqlambda-0.0.3.dist-info/RECORD,,
8
+ pygrestqlambda-0.0.4.dist-info/METADATA,sha256=Bm0rNQxaQJ_LY5m9zF75BRO3pCzcGbhbZMRPvTnx47s,3796
9
+ pygrestqlambda-0.0.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ pygrestqlambda-0.0.4.dist-info/licenses/LICENSE,sha256=8QeS1c5uv4AYoEG3M80OSCBuC_Pk-6vjU1VBUnlX2m0,1071
11
+ pygrestqlambda-0.0.4.dist-info/RECORD,,
@@ -1,37 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: pygrestqlambda
3
- Version: 0.0.3
4
- Summary: PostgreSQL REST API framework for AWS Lambda functions
5
- Project-URL: Homepage, https://github.com/mesogate/pygrestqlambda
6
- Project-URL: Issues, https://github.com/mesogate/pygrestqlambda/issues
7
- Author-email: Voquis Limited <opensource@voquis.com>
8
- License-File: LICENSE
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Classifier: Programming Language :: Python :: 3
12
- Requires-Python: >=3.11
13
- Requires-Dist: aws-xray-sdk
14
- Requires-Dist: boto3
15
- Provides-Extra: dev
16
- Requires-Dist: build; extra == 'dev'
17
- Requires-Dist: pylint; extra == 'dev'
18
- Requires-Dist: pytest; extra == 'dev'
19
- Requires-Dist: pytest-cov; extra == 'dev'
20
- Requires-Dist: ruff; extra == 'dev'
21
- Requires-Dist: twine; extra == 'dev'
22
- Description-Content-Type: text/markdown
23
-
24
- # Python PostgreSQL REST API framework for AWS Lambda functions
25
- > [!NOTE]
26
- > Project status: `Alpha`
27
-
28
- A REST API web framework for persisting records in a PostgreSQL database.
29
-
30
- ## Supported features
31
- - Automatic creation of `uid` fields
32
- - Automatic setting of `created_at` and `last_updated_at` timestamps
33
- - Automatic setting of `creator_uid` and `last_updater_uid`
34
- - RDS with IAM credentials
35
-
36
- ## Examples
37
- See [Examples docs directory](./docs/examples/)