add tests to email
This commit is contained in:
@@ -103,6 +103,7 @@ def unlink(org_id: str, user_id: str):
|
||||
def _create_user(user: User, org: Org) -> bool:
|
||||
now_ = now()
|
||||
user_id = uuid4()
|
||||
email_verified = '@users.noreply.saladeaula.digital' in user.email
|
||||
|
||||
try:
|
||||
with dyn.transact_writer() as transact:
|
||||
@@ -111,7 +112,7 @@ def _create_user(user: User, org: Org) -> bool:
|
||||
**user.model_dump(),
|
||||
'id': user_id,
|
||||
'sk': '0',
|
||||
'email_verified': False,
|
||||
'email_verified': email_verified,
|
||||
'tenant_id': {org.id},
|
||||
# Post-migration: uncomment the folloing line
|
||||
# 'org_id': {org.id},
|
||||
@@ -123,7 +124,7 @@ def _create_user(user: User, org: Org) -> bool:
|
||||
'id': user_id,
|
||||
# Post-migration: rename `emails` to `EMAIL`
|
||||
'sk': f'emails#{user.email}',
|
||||
'email_verified': False,
|
||||
'email_verified': email_verified,
|
||||
'email_primary': True,
|
||||
'created_at': now_,
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@ def add(
|
||||
)
|
||||
transact.put(
|
||||
item={
|
||||
'id': 'EMAIL_VERIFICATION',
|
||||
'sk': uuid4(),
|
||||
'id': user_id,
|
||||
'sk': f'EMAIL_VERIFICATION#{uuid4()}',
|
||||
'name': name,
|
||||
'email': email,
|
||||
'user_id': user_id,
|
||||
@@ -81,6 +81,27 @@ def add(
|
||||
return JSONResponse(status_code=HTTPStatus.CREATED)
|
||||
|
||||
|
||||
@router.post('/<user_id>/emails/<email>/request-verification')
|
||||
def request_verification(user_id: str, email: Annotated[EmailStr, Path]):
|
||||
now_ = now()
|
||||
name = dyn.collection.get_item(
|
||||
KeyPair(user_id, SortKey('0', path_spec='name')),
|
||||
raise_on_error=False,
|
||||
)
|
||||
dyn.put_item(
|
||||
item={
|
||||
'id': user_id,
|
||||
'sk': f'EMAIL_VERIFICATION#{uuid4()}',
|
||||
'name': name,
|
||||
'email': email,
|
||||
'user_id': user_id,
|
||||
'ttl': ttl(start_dt=now_, days=30),
|
||||
'created_at': now_,
|
||||
}
|
||||
)
|
||||
return JSONResponse(status_code=HTTPStatus.NO_CONTENT)
|
||||
|
||||
|
||||
class EmailVerificationNotFoundError(NotFoundError): ...
|
||||
|
||||
|
||||
@@ -88,15 +109,18 @@ class EmailVerificationNotFoundError(NotFoundError): ...
|
||||
def verify(user_id: str, hash: str):
|
||||
email = dyn.collection.get_item(
|
||||
KeyPair(
|
||||
pk='EMAIL_VERIFICATION',
|
||||
sk=SortKey(hash, path_spec='email'),
|
||||
pk=user_id,
|
||||
sk=SortKey(f'EMAIL_VERIFICATION#{hash}', path_spec='email'),
|
||||
),
|
||||
exc_cls=EmailVerificationNotFoundError,
|
||||
)
|
||||
|
||||
with dyn.transact_writer() as transact:
|
||||
transact.delete(key=KeyPair('EMAIL_VERIFICATION', hash))
|
||||
transact.delete(
|
||||
key=KeyPair(user_id, f'EMAIL_VERIFICATION#{hash}'),
|
||||
)
|
||||
transact.update(
|
||||
# Post-migration (users): rename `emails` to `EMAIL`
|
||||
key=KeyPair(user_id, f'emails#{email}'),
|
||||
update_expr='SET email_verified = :true, updated_at = :now',
|
||||
expr_attr_values={
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import json
|
||||
from http import HTTPMethod, HTTPStatus
|
||||
|
||||
from ..conftest import HttpApiProxy, LambdaContext
|
||||
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
|
||||
|
||||
from ...conftest import HttpApiProxy, LambdaContext
|
||||
|
||||
|
||||
def test_get_emails(
|
||||
@@ -23,6 +25,35 @@ def test_get_emails(
|
||||
assert len(body['items']) == 2
|
||||
|
||||
|
||||
def test_add_email(
|
||||
app,
|
||||
seeds,
|
||||
http_api_proxy: HttpApiProxy,
|
||||
lambda_context: LambdaContext,
|
||||
dynamodb_persistence_layer: DynamoDBPersistenceLayer,
|
||||
):
|
||||
r = app.lambda_handler(
|
||||
http_api_proxy(
|
||||
raw_path='/users/15bacf02-1535-4bee-9022-19d106fd7518/emails',
|
||||
method=HTTPMethod.POST,
|
||||
body={
|
||||
'email': 'osergiosiqueira+pytest@gmail.com',
|
||||
},
|
||||
),
|
||||
lambda_context,
|
||||
)
|
||||
|
||||
assert r['statusCode'] == HTTPStatus.CREATED
|
||||
email_verification = dynamodb_persistence_layer.collection.query(
|
||||
KeyPair(
|
||||
'15bacf02-1535-4bee-9022-19d106fd7518',
|
||||
'EMAIL_VERIFICATION',
|
||||
)
|
||||
)
|
||||
assert 'name' in email_verification['items'][0]
|
||||
assert email_verification['items'][0]['email'] == 'osergiosiqueira+pytest@gmail.com'
|
||||
|
||||
|
||||
def test_email_as_primary(
|
||||
app,
|
||||
seeds,
|
||||
@@ -45,7 +76,7 @@ def test_email_as_primary(
|
||||
assert r['statusCode'] == HTTPStatus.NO_CONTENT
|
||||
|
||||
|
||||
def test_get_orgs(
|
||||
def test_remove_emal(
|
||||
app,
|
||||
seeds,
|
||||
http_api_proxy: HttpApiProxy,
|
||||
@@ -53,10 +84,10 @@ def test_get_orgs(
|
||||
):
|
||||
r = app.lambda_handler(
|
||||
http_api_proxy(
|
||||
raw_path='/users/213a6682-2c59-4404-9189-12eec0a846d4/orgs',
|
||||
method=HTTPMethod.GET,
|
||||
raw_path='/users/15bacf02-1535-4bee-9022-19d106fd7518/emails/osergiosiqueira@gmail.com',
|
||||
method=HTTPMethod.DELETE,
|
||||
),
|
||||
lambda_context,
|
||||
)
|
||||
|
||||
assert r['statusCode'] == HTTPStatus.OK
|
||||
assert r['statusCode'] == HTTPStatus.NO_CONTENT
|
||||
20
api.saladeaula.digital/tests/routes/users/test_orgs.py
Normal file
20
api.saladeaula.digital/tests/routes/users/test_orgs.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from http import HTTPMethod, HTTPStatus
|
||||
|
||||
from ...conftest import HttpApiProxy, LambdaContext
|
||||
|
||||
|
||||
def test_get_orgs(
|
||||
app,
|
||||
seeds,
|
||||
http_api_proxy: HttpApiProxy,
|
||||
lambda_context: LambdaContext,
|
||||
):
|
||||
r = app.lambda_handler(
|
||||
http_api_proxy(
|
||||
raw_path='/users/213a6682-2c59-4404-9189-12eec0a846d4/orgs',
|
||||
method=HTTPMethod.GET,
|
||||
),
|
||||
lambda_context,
|
||||
)
|
||||
|
||||
assert r['statusCode'] == HTTPStatus.OK
|
||||
8
api.saladeaula.digital/uv.lock
generated
8
api.saladeaula.digital/uv.lock
generated
@@ -93,15 +93,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-lambda-powertools"
|
||||
version = "3.20.0"
|
||||
version = "3.23.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "jmespath" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/83/c3/ee5b3ed77be236ff0accafabb137a3a1431925ae11b50fc31fab09d60db1/aws_lambda_powertools-3.20.0.tar.gz", hash = "sha256:97cdaa52060972a59d0e769e197d6bffe5a1869c54b6a0c6c69bf31656de5bdd", size = 697927, upload-time = "2025-09-09T09:56:11.78Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/38/24/78f320a310d98df8c831e15c5f04fec20ba4958253deb165ab2d10d3392b/aws_lambda_powertools-3.23.0.tar.gz", hash = "sha256:30ab45960989dd75a4d84de4f156509458f8782038d532eee2f815488d7cc929", size = 702835, upload-time = "2025-11-13T16:44:23.659Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/37/d036d4c33f0d943dbd5257d2c26f5a60375452705526b34977357f8faebf/aws_lambda_powertools-3.20.0-py3-none-any.whl", hash = "sha256:1641eaf38e59470a28262e34d5d447bf6713223ea8896bf5ce442ffb3418c9c9", size = 842168, upload-time = "2025-09-09T09:56:09.847Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/48/f59597b0acbe3bcd829ae5b13b49a29039c5b2a5a6771f765ad3f3f576a3/aws_lambda_powertools-3.23.0-py3-none-any.whl", hash = "sha256:f3d16f1b0304c686cc956ecf0f6f8907d21992a4a5070e2388c21571d8c84cc2", size = 848256, upload-time = "2025-11-13T16:44:21.459Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
@@ -622,7 +622,7 @@ dependencies = [
|
||||
requires-dist = [
|
||||
{ name = "arnparse", specifier = ">=0.0.2" },
|
||||
{ name = "authlib", specifier = ">=1.6.5" },
|
||||
{ name = "aws-lambda-powertools", extras = ["all"], specifier = ">=3.18.0" },
|
||||
{ name = "aws-lambda-powertools", extras = ["all"], specifier = ">=3.23.0" },
|
||||
{ name = "dictdiffer", specifier = ">=0.9.0" },
|
||||
{ name = "ftfy", specifier = ">=6.3.1" },
|
||||
{ name = "glom", specifier = ">=24.11.0" },
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { LockIcon } from 'lucide-react'
|
||||
import type { Route } from './+types'
|
||||
|
||||
import { LockIcon } from 'lucide-react'
|
||||
|
||||
export function meta({}: Route.MetaArgs) {
|
||||
return [{ title: 'Acesso negado · EDUSEG®' }]
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ authors = [
|
||||
]
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"aws-lambda-powertools[all]>=3.18.0",
|
||||
"aws-lambda-powertools[all]>=3.23.0",
|
||||
"ftfy>=6.3.1",
|
||||
"glom>=24.11.0",
|
||||
"orjson>=3.10.15",
|
||||
|
||||
1645
layercake/uv.lock
generated
1645
layercake/uv.lock
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user