add apikey
This commit is contained in:
@@ -23,7 +23,7 @@ Example
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import asdict, dataclass
|
||||||
|
|
||||||
import boto3
|
import boto3
|
||||||
from aws_lambda_powertools import Logger, Tracer
|
from aws_lambda_powertools import Logger, Tracer
|
||||||
@@ -34,14 +34,19 @@ from aws_lambda_powertools.utilities.data_classes.api_gateway_authorizer_event i
|
|||||||
)
|
)
|
||||||
from aws_lambda_powertools.utilities.typing import LambdaContext
|
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||||
from botocore.endpoint_provider import Enum
|
from botocore.endpoint_provider import Enum
|
||||||
|
from layercake.dynamodb import DynamoDBCollection, DynamoDBPersistenceLayer, KeyPair
|
||||||
|
|
||||||
|
from boto3clients import dynamodb_client
|
||||||
from cognito import get_user
|
from cognito import get_user
|
||||||
|
from settings import USER_TABLE
|
||||||
|
|
||||||
APIKEY_PREFIX = 'sk-'
|
APIKEY_PREFIX = 'sk-'
|
||||||
|
|
||||||
tracer = Tracer()
|
tracer = Tracer()
|
||||||
logger = Logger(__name__)
|
logger = Logger(__name__)
|
||||||
idp_client = boto3.client('cognito-idp')
|
idp_client = boto3.client('cognito-idp')
|
||||||
|
user_layer = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client)
|
||||||
|
collect = DynamoDBCollection(user_layer)
|
||||||
|
|
||||||
|
|
||||||
@tracer.capture_lambda_handler
|
@tracer.capture_lambda_handler
|
||||||
@@ -53,16 +58,8 @@ def lambda_handler(event: APIGatewayAuthorizerEventV2, context: LambdaContext):
|
|||||||
if not bearer:
|
if not bearer:
|
||||||
return APIGatewayAuthorizerResponseV2(authorize=False).asdict()
|
return APIGatewayAuthorizerResponseV2(authorize=False).asdict()
|
||||||
|
|
||||||
if bearer.auth_type == TokenType.USER_TOKEN:
|
kwargs = asdict(_authorizer(bearer))
|
||||||
user = get_user(bearer.token, idp_client=idp_client)
|
return APIGatewayAuthorizerResponseV2(**kwargs).asdict()
|
||||||
|
|
||||||
if user:
|
|
||||||
return APIGatewayAuthorizerResponseV2(
|
|
||||||
authorize=True,
|
|
||||||
context=dict(user=user),
|
|
||||||
).asdict()
|
|
||||||
|
|
||||||
return APIGatewayAuthorizerResponseV2(authorize=False).asdict()
|
|
||||||
|
|
||||||
|
|
||||||
class TokenType(str, Enum):
|
class TokenType(str, Enum):
|
||||||
@@ -76,6 +73,25 @@ class BearerToken:
|
|||||||
token: str
|
token: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Authorizer:
|
||||||
|
authorize: bool = False
|
||||||
|
context: dict | None = None
|
||||||
|
|
||||||
|
|
||||||
|
def _authorizer(bearer: BearerToken) -> Authorizer:
|
||||||
|
try:
|
||||||
|
match bearer.auth_type:
|
||||||
|
case TokenType.USER_TOKEN:
|
||||||
|
user = get_user(bearer.token, idp_client=idp_client)
|
||||||
|
return Authorizer(True, {'user': user})
|
||||||
|
case TokenType.API_KEY:
|
||||||
|
apikey = collect.get_item(KeyPair('apikey', bearer.token))
|
||||||
|
return Authorizer(True, {'tenant': apikey['tenant']})
|
||||||
|
except Exception:
|
||||||
|
return Authorizer()
|
||||||
|
|
||||||
|
|
||||||
def _parse_bearer_token(s: str) -> BearerToken | None:
|
def _parse_bearer_token(s: str) -> BearerToken | None:
|
||||||
"""Parses and identifies a bearer token as either an API key or a user token."""
|
"""Parses and identifies a bearer token as either an API key or a user token."""
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from tqdm import tqdm
|
|||||||
from boto3clients import dynamodb_client
|
from boto3clients import dynamodb_client
|
||||||
|
|
||||||
elastic_client = Elasticsearch('http://127.0.0.1:9200')
|
elastic_client = Elasticsearch('http://127.0.0.1:9200')
|
||||||
files = (
|
jsonl_files = (
|
||||||
'test-orders.jsonl',
|
'test-orders.jsonl',
|
||||||
'test-users.jsonl',
|
'test-users.jsonl',
|
||||||
'test-enrollments.jsonl',
|
'test-enrollments.jsonl',
|
||||||
@@ -16,7 +16,7 @@ files = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def put_item(item: dict, table_name: str, *, dynamodb_client) -> bool:
|
def put_item(item: dict, table_name: str, /, dynamodb_client) -> bool:
|
||||||
try:
|
try:
|
||||||
dynamodb_client.put_item(
|
dynamodb_client.put_item(
|
||||||
TableName=table_name,
|
TableName=table_name,
|
||||||
@@ -28,7 +28,7 @@ def put_item(item: dict, table_name: str, *, dynamodb_client) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def scan_table(table_name: str, *, dynamodb_client, **kwargs) -> Generator:
|
def scan_table(table_name: str, /, dynamodb_client, **kwargs) -> Generator:
|
||||||
try:
|
try:
|
||||||
r = dynamodb_client.scan(TableName=table_name, **kwargs)
|
r = dynamodb_client.scan(TableName=table_name, **kwargs)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -45,6 +45,31 @@ def scan_table(table_name: str, *, dynamodb_client, **kwargs) -> Generator:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Elastic:
|
||||||
|
def __init__(self, client: Elasticsearch) -> None:
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
def index_item(
|
||||||
|
self,
|
||||||
|
id: str,
|
||||||
|
index: str,
|
||||||
|
doc: dict,
|
||||||
|
):
|
||||||
|
return self.client.index(
|
||||||
|
index=index,
|
||||||
|
id=id,
|
||||||
|
document=_serialize_python_type(doc),
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete_index(self, index: str) -> bool:
|
||||||
|
try:
|
||||||
|
self.client.indices.delete(index=index)
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _serialize_python_type(value: Any) -> Any:
|
def _serialize_python_type(value: Any) -> Any:
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
return {k: _serialize_python_type(v) for k, v in value.items()}
|
return {k: _serialize_python_type(v) for k, v in value.items()}
|
||||||
@@ -58,53 +83,27 @@ def _serialize_python_type(value: Any) -> Any:
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def index_item(
|
|
||||||
id: str,
|
|
||||||
index: str,
|
|
||||||
doc: dict,
|
|
||||||
*,
|
|
||||||
elastic_client: Elasticsearch,
|
|
||||||
):
|
|
||||||
return elastic_client.index(
|
|
||||||
index=index,
|
|
||||||
id=id,
|
|
||||||
document=_serialize_python_type(doc),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def delete_index(index: str, *, elastic_client: Elasticsearch) -> bool:
|
|
||||||
try:
|
|
||||||
elastic_client.indices.delete(index=index)
|
|
||||||
except Exception:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
for file in tqdm(files, desc='Processing files'):
|
elastic = Elastic(elastic_client)
|
||||||
|
|
||||||
|
for file in tqdm(jsonl_files, desc='Processing files'):
|
||||||
with jsonl.readlines(f'seeds/{file}') as lines:
|
with jsonl.readlines(f'seeds/{file}') as lines:
|
||||||
table_name = file.removesuffix('.jsonl')
|
table_name = file.removesuffix('.jsonl')
|
||||||
|
|
||||||
for line in tqdm(lines, desc=f'Processing lines in {file}'):
|
for line in tqdm(lines, desc=f'Processing lines in {file}'):
|
||||||
put_item(line, table_name, dynamodb_client=dynamodb_client)
|
put_item(line, table_name, dynamodb_client)
|
||||||
|
|
||||||
for file in tqdm(files, desc='Scanning tables'):
|
for file in tqdm(jsonl_files, desc='Scanning tables'):
|
||||||
table_name = file.removesuffix('.jsonl')
|
table_name = file.removesuffix('.jsonl')
|
||||||
delete_index(table_name, elastic_client=elastic_client)
|
elastic.delete_index(table_name)
|
||||||
|
|
||||||
for record in tqdm(
|
for record in tqdm(
|
||||||
scan_table(
|
scan_table(
|
||||||
table_name,
|
table_name,
|
||||||
dynamodb_client=dynamodb_client,
|
dynamodb_client,
|
||||||
FilterExpression='sk = :sk',
|
FilterExpression='sk = :sk',
|
||||||
ExpressionAttributeValues={':sk': {'S': '0'}},
|
ExpressionAttributeValues={':sk': {'S': '0'}},
|
||||||
),
|
),
|
||||||
desc=f'Indexing {table_name}',
|
desc=f'Indexing {table_name}',
|
||||||
):
|
):
|
||||||
index_item(
|
elastic.index_item(id=record['id'], index=table_name, doc=record)
|
||||||
id=record['id'],
|
|
||||||
index=table_name,
|
|
||||||
doc=record,
|
|
||||||
elastic_client=elastic_client,
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
class UserNotFound(Exception): ...
|
||||||
|
|
||||||
|
|
||||||
def get_user(access_token: str, *, idp_client) -> dict | None:
|
def get_user(access_token: str, *, idp_client) -> dict | None:
|
||||||
"""Gets the user attributes and metadata for a user."""
|
"""Gets the user attributes and metadata for a user."""
|
||||||
try:
|
try:
|
||||||
user = idp_client.get_user(AccessToken=access_token)
|
user = idp_client.get_user(AccessToken=access_token)
|
||||||
except idp_client.exceptions.ClientError:
|
except idp_client.exceptions.ClientError:
|
||||||
return None
|
raise UserNotFound()
|
||||||
else:
|
else:
|
||||||
return {attr['Name']: attr['Value'] for attr in user['UserAttributes']}
|
return {attr['Name']: attr['Value'] for attr in user['UserAttributes']}
|
||||||
|
|||||||
@@ -43,11 +43,9 @@ class CoursePayload(BaseModel):
|
|||||||
|
|
||||||
@router.post('/', compress=True, tags=['Course'])
|
@router.post('/', compress=True, tags=['Course'])
|
||||||
def post_course(payload: CoursePayload):
|
def post_course(payload: CoursePayload):
|
||||||
org = Org(id='*', name='EDUSEG')
|
|
||||||
|
|
||||||
create_course(
|
create_course(
|
||||||
course=payload.course,
|
course=payload.course,
|
||||||
org=org,
|
org=Org(id='*', name='default'),
|
||||||
persistence_layer=course_layer,
|
persistence_layer=course_layer,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ LIMIT = 25
|
|||||||
def me():
|
def me():
|
||||||
user: AuthenticatedUser = router.context['user']
|
user: AuthenticatedUser = router.context['user']
|
||||||
acls = collect.get_items(
|
acls = collect.get_items(
|
||||||
KeyPair(user.id, PrefixKey('acls#')),
|
KeyPair(user.id, PrefixKey('acls')),
|
||||||
limit=LIMIT,
|
limit=LIMIT,
|
||||||
)
|
)
|
||||||
workspaces = collect.get_items(
|
workspaces = collect.get_items(
|
||||||
KeyPair(user.id, PrefixKey('orgs#')),
|
KeyPair(user.id, PrefixKey('orgs')),
|
||||||
limit=LIMIT,
|
limit=LIMIT,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class BadRequestError(MissingError, PowertoolsBadRequestError): ...
|
|||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
user_layer = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client)
|
user_layer = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client)
|
||||||
collect = DynamoDBCollection(user_layer, exception_cls=BadRequestError)
|
collect = DynamoDBCollection(user_layer, exc_cls=BadRequestError)
|
||||||
elastic_client = Elasticsearch(**ELASTIC_CONN)
|
elastic_client = Elasticsearch(**ELASTIC_CONN)
|
||||||
|
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ def get_idp(id: str):
|
|||||||
)
|
)
|
||||||
def get_emails(id: str):
|
def get_emails(id: str):
|
||||||
return collect.get_items(
|
return collect.get_items(
|
||||||
KeyPair(id, PrefixKey('emails#')),
|
KeyPair(id, PrefixKey('emails')),
|
||||||
start_key=router.current_event.get_query_string_value('start_key', None),
|
start_key=router.current_event.get_query_string_value('start_key', None),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -126,6 +126,6 @@ def get_logs(id: str):
|
|||||||
)
|
)
|
||||||
def get_orgs(id: str):
|
def get_orgs(id: str):
|
||||||
return collect.get_items(
|
return collect.get_items(
|
||||||
KeyPair(id, PrefixKey('orgs#')),
|
KeyPair(id, PrefixKey('orgs')),
|
||||||
start_key=router.current_event.get_query_string_value('start_key', None),
|
start_key=router.current_event.get_query_string_value('start_key', None),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{"id": {"S": "apikey"}, "sk": {"S": "32504457-f133-4c00-936b-6aa712ca9f40"}}
|
||||||
{"updateDate": {"S": "2024-02-08T16:42:33.776409-03:00"}, "createDate": {"S": "2019-03-25T00:00:00-03:00"}, "email_verified": {"BOOL": true}, "cognito:sub": {"S": "58efed8d-d276-41a8-8502-4ab8b5a6415e"}, "cpf": {"S": "07879819908"}, "sk": {"S": "0"}, "email": {"S": "sergio@somosbeta.com.br"}, "id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "name": {"S": "S\u00e9rgio Rafael de Siqueira"}, "lastLogin": {"S": "2024-02-08T20:53:45.818126-03:00"}, "orgs": {"L": [{"S": "cJtK9SsnJhKPyxESe7g3DG"}, {"S": "edp8njvgQuzNkLx2ySNfAD"}, {"S": "8TVSi5oACLxTiT8ycKPmaQ"}]}}
|
{"updateDate": {"S": "2024-02-08T16:42:33.776409-03:00"}, "createDate": {"S": "2019-03-25T00:00:00-03:00"}, "email_verified": {"BOOL": true}, "cognito:sub": {"S": "58efed8d-d276-41a8-8502-4ab8b5a6415e"}, "cpf": {"S": "07879819908"}, "sk": {"S": "0"}, "email": {"S": "sergio@somosbeta.com.br"}, "id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "name": {"S": "S\u00e9rgio Rafael de Siqueira"}, "lastLogin": {"S": "2024-02-08T20:53:45.818126-03:00"}, "orgs": {"L": [{"S": "cJtK9SsnJhKPyxESe7g3DG"}, {"S": "edp8njvgQuzNkLx2ySNfAD"}, {"S": "8TVSi5oACLxTiT8ycKPmaQ"}]}}
|
||||||
{"sk": {"S": "acl#admin"}, "id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "create_date": {"S": "2022-06-13T15:00:24.309410-03:00"}}
|
{"sk": {"S": "acl#admin"}, "id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "create_date": {"S": "2022-06-13T15:00:24.309410-03:00"}}
|
||||||
{"emailVerified": {"BOOL": true}, "updateDate": {"S": "2024-02-08T16:42:33.776409-03:00"}, "createDate": {"S": "2024-01-19T22:53:43.135080-03:00"}, "deliverability": {"S": "DELIVERABLE"}, "sk": {"S": "emails#osergiosiqueira@gmail.com"}, "id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "primaryEmail": {"BOOL": false}, "emailDeliverable": {"BOOL": true}}
|
{"emailVerified": {"BOOL": true}, "updateDate": {"S": "2024-02-08T16:42:33.776409-03:00"}, "createDate": {"S": "2024-01-19T22:53:43.135080-03:00"}, "deliverability": {"S": "DELIVERABLE"}, "sk": {"S": "emails#osergiosiqueira@gmail.com"}, "id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "primaryEmail": {"BOOL": false}, "emailDeliverable": {"BOOL": true}}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ Globals:
|
|||||||
Architectures:
|
Architectures:
|
||||||
- x86_64
|
- x86_64
|
||||||
Layers:
|
Layers:
|
||||||
- !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:20
|
- !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:21
|
||||||
Environment:
|
Environment:
|
||||||
Variables:
|
Variables:
|
||||||
TZ: America/Sao_Paulo
|
TZ: America/Sao_Paulo
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{"id": {"S": "apikey"}, "sk": {"S": "32504457-f133-4c00-936b-6aa712ca9f40"}, "tenant": {"M": {"id": {"S": "*"}, "name": {"S": "default"}}}}
|
||||||
{"id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "sk": {"S": "0"}, "update_date": {"S": "2024-02-08T16:42:33.776409-03:00"}, "create_date": {"S": "2019-03-25T00:00:00-03:00"}, "email_verified": {"BOOL": true}, "cognito:sub": {"S": "58efed8d-d276-41a8-8502-4ab8b5a6415e"}, "cpf": {"S": "07879819908"}, "email": {"S": "sergio@somosbeta.com.br"}, "name": {"S": "S\u00e9rgio Rafael de Siqueira"}, "last_login": {"S": "2024-02-08T20:53:45.818126-03:00"}, "tenant:org_id": {"L": [{"S": "cJtK9SsnJhKPyxESe7g3DG"}]}}
|
{"id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "sk": {"S": "0"}, "update_date": {"S": "2024-02-08T16:42:33.776409-03:00"}, "create_date": {"S": "2019-03-25T00:00:00-03:00"}, "email_verified": {"BOOL": true}, "cognito:sub": {"S": "58efed8d-d276-41a8-8502-4ab8b5a6415e"}, "cpf": {"S": "07879819908"}, "email": {"S": "sergio@somosbeta.com.br"}, "name": {"S": "S\u00e9rgio Rafael de Siqueira"}, "last_login": {"S": "2024-02-08T20:53:45.818126-03:00"}, "tenant:org_id": {"L": [{"S": "cJtK9SsnJhKPyxESe7g3DG"}]}}
|
||||||
{"id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "sk": {"S": "cognito"}, "create_date": {"S": "2025-03-03T17:12:26.443507-03:00"}, "sub": {"S": "58efed8d-d276-41a8-8502-4ab8b5a6415e"}}
|
{"id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "sk": {"S": "cognito"}, "create_date": {"S": "2025-03-03T17:12:26.443507-03:00"}, "sub": {"S": "58efed8d-d276-41a8-8502-4ab8b5a6415e"}}
|
||||||
{"id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "sk": {"S": "emails#sergio@somosbeta.com.br"}, "email_verified": {"BOOL": true}, "update_date": {"S": "2024-02-08T16:42:33.776409-03:00"}, "create_date": {"S": "2019-03-25T00:00:00-03:00"}, "email_primary": {"BOOL": true}, "mx_record_exists": {"BOOL": true}, "update_date": {"S": "2023-11-09T12:13:04.308986-03:00"}}
|
{"id": {"S": "5OxmMjL-ujoR5IMGegQz"}, "sk": {"S": "emails#sergio@somosbeta.com.br"}, "email_verified": {"BOOL": true}, "update_date": {"S": "2024-02-08T16:42:33.776409-03:00"}, "create_date": {"S": "2019-03-25T00:00:00-03:00"}, "email_primary": {"BOOL": true}, "mx_record_exists": {"BOOL": true}, "update_date": {"S": "2023-11-09T12:13:04.308986-03:00"}}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
|
from layercake.dynamodb import DynamoDBCollection, DynamoDBPersistenceLayer
|
||||||
|
|
||||||
import auth as app
|
import auth as app
|
||||||
from auth import _parse_bearer_token
|
from auth import _parse_bearer_token
|
||||||
|
|
||||||
from .conftest import LambdaContext
|
from .conftest import LambdaContext
|
||||||
|
|
||||||
|
|
||||||
def test_bearer_jwt(lambda_context: LambdaContext, dynamodb_seeds):
|
def test_bearer_jwt(lambda_context: LambdaContext):
|
||||||
# You should mock the Cognito user to pass the test
|
# You should mock the Cognito user to pass the test
|
||||||
app.get_user = lambda *args, **kwargs: {
|
app.get_user = lambda *args, **kwargs: {
|
||||||
'sub': '58efed8d-d276-41a8-8502-4ab8b5a6415e',
|
'sub': '58efed8d-d276-41a8-8502-4ab8b5a6415e',
|
||||||
@@ -29,6 +31,31 @@ def test_bearer_jwt(lambda_context: LambdaContext, dynamodb_seeds):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_bearer_apikey(
|
||||||
|
dynamodb_seeds,
|
||||||
|
dynamodb_persistence_layer: DynamoDBPersistenceLayer,
|
||||||
|
lambda_context: LambdaContext,
|
||||||
|
):
|
||||||
|
app.collect = DynamoDBCollection(dynamodb_persistence_layer)
|
||||||
|
|
||||||
|
event = {
|
||||||
|
'headers': {
|
||||||
|
'authorization': 'Bearer sk-32504457-f133-4c00-936b-6aa712ca9f40',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# This data was added from seeds
|
||||||
|
assert app.lambda_handler(event, lambda_context) == {
|
||||||
|
'isAuthorized': True,
|
||||||
|
'context': {'tenant': {'name': 'default', 'id': '*'}},
|
||||||
|
}
|
||||||
|
|
||||||
|
# This data was added from seeds
|
||||||
|
assert app.lambda_handler(
|
||||||
|
{'headers': {'authorization': 'Bearer sk-abc'}},
|
||||||
|
lambda_context,
|
||||||
|
) == {'isAuthorized': False}
|
||||||
|
|
||||||
|
|
||||||
def test_parse_bearer_token_api_key():
|
def test_parse_bearer_token_api_key():
|
||||||
bearer = _parse_bearer_token(
|
bearer = _parse_bearer_token(
|
||||||
'Bearer sk-35433970-6857-4062-bb43-f71683b2f68e',
|
'Bearer sk-35433970-6857-4062-bb43-f71683b2f68e',
|
||||||
|
|||||||
2
http-api/uv.lock
generated
2
http-api/uv.lock
generated
@@ -444,7 +444,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "layercake"
|
name = "layercake"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
source = { directory = "../layercake" }
|
source = { directory = "../layercake" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aws-lambda-powertools", extra = ["all"] },
|
{ name = "aws-lambda-powertools", extra = ["all"] },
|
||||||
|
|||||||
@@ -572,30 +572,30 @@ class DynamoDBCollection:
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
persistence_layer: DynamoDBPersistenceLayer,
|
persistence_layer: DynamoDBPersistenceLayer,
|
||||||
exception_cls: Type[ValueError] = MissingError,
|
exc_cls: Type[ValueError] = MissingError,
|
||||||
tz: str = TZ,
|
tz: str = TZ,
|
||||||
) -> None:
|
) -> None:
|
||||||
if not issubclass(exception_cls, ValueError):
|
if not issubclass(exc_cls, ValueError):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f'exception_cls must be a subclass of ValueError, got {exception_cls}'
|
f'exception_cls must be a subclass of ValueError, got {exc_cls}'
|
||||||
)
|
)
|
||||||
|
|
||||||
self.persistence_layer = persistence_layer
|
self.persistence_layer = persistence_layer
|
||||||
self.exception_cls = exception_cls
|
self.exc_cls = exc_cls
|
||||||
self.tz = tz
|
self.tz = tz
|
||||||
|
|
||||||
def get_item(
|
def get_item(
|
||||||
self,
|
self,
|
||||||
key: KeyPair,
|
key: KeyPair,
|
||||||
path_spec: str | None = None,
|
path_spec: str | None = None,
|
||||||
raise_if_missing: bool = True,
|
raise_on_error: bool = True,
|
||||||
default: Any = None,
|
default: Any = None,
|
||||||
delimiter: str = '#',
|
delimiter: str = '#',
|
||||||
) -> Any:
|
) -> Any:
|
||||||
exc_cls = self.exception_cls
|
exc_cls = self.exc_cls
|
||||||
data = self.persistence_layer.get_item(key)
|
data = self.persistence_layer.get_item(key)
|
||||||
|
|
||||||
if raise_if_missing and not data:
|
if raise_on_error and not data:
|
||||||
raise exc_cls(f'Item with {key} not found.')
|
raise exc_cls(f'Item with {key} not found.')
|
||||||
|
|
||||||
if path_spec and data:
|
if path_spec and data:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "layercake"
|
name = "layercake"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
description = "Add your description here"
|
description = "Add your description here"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = [
|
authors = [
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ def test_collection_get_item(
|
|||||||
pk='5OxmMjL-ujoR5IMGegQz',
|
pk='5OxmMjL-ujoR5IMGegQz',
|
||||||
sk='tenant',
|
sk='tenant',
|
||||||
),
|
),
|
||||||
raise_if_missing=False,
|
raise_on_error=False,
|
||||||
default={},
|
default={},
|
||||||
)
|
)
|
||||||
assert data_notfound == {}
|
assert data_notfound == {}
|
||||||
|
|||||||
Reference in New Issue
Block a user