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:
|
def _create_user(user: User, org: Org) -> bool:
|
||||||
now_ = now()
|
now_ = now()
|
||||||
user_id = uuid4()
|
user_id = uuid4()
|
||||||
|
email_verified = '@users.noreply.saladeaula.digital' in user.email
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with dyn.transact_writer() as transact:
|
with dyn.transact_writer() as transact:
|
||||||
@@ -111,7 +112,7 @@ def _create_user(user: User, org: Org) -> bool:
|
|||||||
**user.model_dump(),
|
**user.model_dump(),
|
||||||
'id': user_id,
|
'id': user_id,
|
||||||
'sk': '0',
|
'sk': '0',
|
||||||
'email_verified': False,
|
'email_verified': email_verified,
|
||||||
'tenant_id': {org.id},
|
'tenant_id': {org.id},
|
||||||
# Post-migration: uncomment the folloing line
|
# Post-migration: uncomment the folloing line
|
||||||
# 'org_id': {org.id},
|
# 'org_id': {org.id},
|
||||||
@@ -123,7 +124,7 @@ def _create_user(user: User, org: Org) -> bool:
|
|||||||
'id': user_id,
|
'id': user_id,
|
||||||
# Post-migration: rename `emails` to `EMAIL`
|
# Post-migration: rename `emails` to `EMAIL`
|
||||||
'sk': f'emails#{user.email}',
|
'sk': f'emails#{user.email}',
|
||||||
'email_verified': False,
|
'email_verified': email_verified,
|
||||||
'email_primary': True,
|
'email_primary': True,
|
||||||
'created_at': now_,
|
'created_at': now_,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ def add(
|
|||||||
)
|
)
|
||||||
transact.put(
|
transact.put(
|
||||||
item={
|
item={
|
||||||
'id': 'EMAIL_VERIFICATION',
|
'id': user_id,
|
||||||
'sk': uuid4(),
|
'sk': f'EMAIL_VERIFICATION#{uuid4()}',
|
||||||
'name': name,
|
'name': name,
|
||||||
'email': email,
|
'email': email,
|
||||||
'user_id': user_id,
|
'user_id': user_id,
|
||||||
@@ -81,6 +81,27 @@ def add(
|
|||||||
return JSONResponse(status_code=HTTPStatus.CREATED)
|
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): ...
|
class EmailVerificationNotFoundError(NotFoundError): ...
|
||||||
|
|
||||||
|
|
||||||
@@ -88,15 +109,18 @@ class EmailVerificationNotFoundError(NotFoundError): ...
|
|||||||
def verify(user_id: str, hash: str):
|
def verify(user_id: str, hash: str):
|
||||||
email = dyn.collection.get_item(
|
email = dyn.collection.get_item(
|
||||||
KeyPair(
|
KeyPair(
|
||||||
pk='EMAIL_VERIFICATION',
|
pk=user_id,
|
||||||
sk=SortKey(hash, path_spec='email'),
|
sk=SortKey(f'EMAIL_VERIFICATION#{hash}', path_spec='email'),
|
||||||
),
|
),
|
||||||
exc_cls=EmailVerificationNotFoundError,
|
exc_cls=EmailVerificationNotFoundError,
|
||||||
)
|
)
|
||||||
|
|
||||||
with dyn.transact_writer() as transact:
|
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(
|
transact.update(
|
||||||
|
# Post-migration (users): rename `emails` to `EMAIL`
|
||||||
key=KeyPair(user_id, f'emails#{email}'),
|
key=KeyPair(user_id, f'emails#{email}'),
|
||||||
update_expr='SET email_verified = :true, updated_at = :now',
|
update_expr='SET email_verified = :true, updated_at = :now',
|
||||||
expr_attr_values={
|
expr_attr_values={
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import json
|
import json
|
||||||
from http import HTTPMethod, HTTPStatus
|
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(
|
def test_get_emails(
|
||||||
@@ -23,6 +25,35 @@ def test_get_emails(
|
|||||||
assert len(body['items']) == 2
|
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(
|
def test_email_as_primary(
|
||||||
app,
|
app,
|
||||||
seeds,
|
seeds,
|
||||||
@@ -45,7 +76,7 @@ def test_email_as_primary(
|
|||||||
assert r['statusCode'] == HTTPStatus.NO_CONTENT
|
assert r['statusCode'] == HTTPStatus.NO_CONTENT
|
||||||
|
|
||||||
|
|
||||||
def test_get_orgs(
|
def test_remove_emal(
|
||||||
app,
|
app,
|
||||||
seeds,
|
seeds,
|
||||||
http_api_proxy: HttpApiProxy,
|
http_api_proxy: HttpApiProxy,
|
||||||
@@ -53,10 +84,10 @@ def test_get_orgs(
|
|||||||
):
|
):
|
||||||
r = app.lambda_handler(
|
r = app.lambda_handler(
|
||||||
http_api_proxy(
|
http_api_proxy(
|
||||||
raw_path='/users/213a6682-2c59-4404-9189-12eec0a846d4/orgs',
|
raw_path='/users/15bacf02-1535-4bee-9022-19d106fd7518/emails/osergiosiqueira@gmail.com',
|
||||||
method=HTTPMethod.GET,
|
method=HTTPMethod.DELETE,
|
||||||
),
|
),
|
||||||
lambda_context,
|
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]]
|
[[package]]
|
||||||
name = "aws-lambda-powertools"
|
name = "aws-lambda-powertools"
|
||||||
version = "3.20.0"
|
version = "3.23.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "jmespath" },
|
{ name = "jmespath" },
|
||||||
{ name = "typing-extensions" },
|
{ 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 = [
|
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]
|
[package.optional-dependencies]
|
||||||
@@ -622,7 +622,7 @@ dependencies = [
|
|||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "arnparse", specifier = ">=0.0.2" },
|
{ name = "arnparse", specifier = ">=0.0.2" },
|
||||||
{ name = "authlib", specifier = ">=1.6.5" },
|
{ 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 = "dictdiffer", specifier = ">=0.9.0" },
|
||||||
{ name = "ftfy", specifier = ">=6.3.1" },
|
{ name = "ftfy", specifier = ">=6.3.1" },
|
||||||
{ name = "glom", specifier = ">=24.11.0" },
|
{ name = "glom", specifier = ">=24.11.0" },
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { LockIcon } from 'lucide-react'
|
|
||||||
import type { Route } from './+types'
|
import type { Route } from './+types'
|
||||||
|
|
||||||
|
import { LockIcon } from 'lucide-react'
|
||||||
|
|
||||||
export function meta({}: Route.MetaArgs) {
|
export function meta({}: Route.MetaArgs) {
|
||||||
return [{ title: 'Acesso negado · EDUSEG®' }]
|
return [{ title: 'Acesso negado · EDUSEG®' }]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ authors = [
|
|||||||
]
|
]
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-lambda-powertools[all]>=3.18.0",
|
"aws-lambda-powertools[all]>=3.23.0",
|
||||||
"ftfy>=6.3.1",
|
"ftfy>=6.3.1",
|
||||||
"glom>=24.11.0",
|
"glom>=24.11.0",
|
||||||
"orjson>=3.10.15",
|
"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