improve support to meili
This commit is contained in:
62
http-api/app/meili.py
Normal file
62
http-api/app/meili.py
Normal file
@@ -0,0 +1,62 @@
|
||||
import re
|
||||
|
||||
OPERATORS = [
|
||||
'!=',
|
||||
'>=',
|
||||
'<=',
|
||||
'=',
|
||||
'>',
|
||||
'<',
|
||||
'TO',
|
||||
'EXISTS',
|
||||
'IN',
|
||||
'NOT IN',
|
||||
]
|
||||
|
||||
|
||||
def remove_quotes(value: str) -> str:
|
||||
if (value.startswith('"') and value.endswith('"')) or (
|
||||
value.startswith("'") and value.endswith("'")
|
||||
):
|
||||
return value[1:-1]
|
||||
return value
|
||||
|
||||
|
||||
def parse_condition(condition: str) -> dict[str, str] | None:
|
||||
for op in OPERATORS:
|
||||
parts = condition.split(op)
|
||||
|
||||
if len(parts) == 2:
|
||||
attr, value = parts
|
||||
attr = attr.strip()
|
||||
value = remove_quotes(value.strip())
|
||||
|
||||
return {
|
||||
'attr': attr,
|
||||
'op': op,
|
||||
'value': value,
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def parse(s: str) -> list[dict]:
|
||||
filter_expr = re.sub(r'\s+', ' ', s).strip()
|
||||
|
||||
if filter_expr == '':
|
||||
return []
|
||||
|
||||
parts = re.split(r'\b(?:AND|OR)\b', filter_expr)
|
||||
conditions = []
|
||||
|
||||
for part in parts:
|
||||
condition = parse_condition(part.strip())
|
||||
|
||||
if condition:
|
||||
conditions.append(condition)
|
||||
|
||||
return conditions
|
||||
|
||||
|
||||
def encode(conditions: list[dict]):
|
||||
return ' AND '.join(f'{c["attr"]} {c["op"]} {c["value"]}' for c in conditions if c)
|
||||
@@ -1,15 +1,14 @@
|
||||
import urllib.parse as parse
|
||||
|
||||
from aws_lambda_powertools.event_handler.api_gateway import Router
|
||||
from aws_lambda_powertools.event_handler.exceptions import (
|
||||
NotFoundError,
|
||||
)
|
||||
from layercake.dynamodb import (
|
||||
DynamoDBPersistenceLayer,
|
||||
KeyPair,
|
||||
SortKey,
|
||||
TransactKey,
|
||||
)
|
||||
from meilisearch import Client as Meilisearch
|
||||
|
||||
import meili
|
||||
from boto3clients import dynamodb_client
|
||||
from config import MEILISEARCH_API_KEY, MEILISEARCH_HOST, ORDER_TABLE, USER_TABLE
|
||||
from middlewares import Tenant, TenantMiddleware
|
||||
@@ -35,10 +34,16 @@ def get_orders():
|
||||
sort = event.get_query_string_value('sort', 'create_date:desc')
|
||||
page = int(event.get_query_string_value('page', '1'))
|
||||
hits_per_page = int(event.get_query_string_value('hitsPerPage', '25'))
|
||||
filter_ = parse.unquote(event.get_query_string_value('filter', ''))
|
||||
filter_ = meili.parse(parse.unquote(event.get_query_string_value('filter', '')))
|
||||
|
||||
if tenant.id != '*':
|
||||
filter_ = f'tenant = {tenant.id}'
|
||||
filter_ = [
|
||||
{
|
||||
'attr': 'tenant',
|
||||
'op': '=',
|
||||
'value': tenant.id,
|
||||
},
|
||||
] + filter_
|
||||
|
||||
return meili_client.index(ORDER_TABLE).search(
|
||||
query,
|
||||
@@ -47,7 +52,7 @@ def get_orders():
|
||||
'locales': ['pt'],
|
||||
'page': page,
|
||||
'hitsPerPage': hits_per_page,
|
||||
'filter': filter_,
|
||||
'filter': meili.encode(filter_),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -59,7 +64,11 @@ def get_orders():
|
||||
summary='Get order',
|
||||
)
|
||||
def get_order(id: str):
|
||||
return order_layer.collection.get_item(
|
||||
KeyPair(id, '0'),
|
||||
exc_cls=NotFoundError,
|
||||
return order_layer.collection.get_items(
|
||||
TransactKey(id)
|
||||
+ SortKey('0')
|
||||
+ SortKey('items', path_spec='items')
|
||||
+ SortKey('address')
|
||||
+ SortKey('nfse', path_spec='nfse')
|
||||
+ SortKey('fees', path_spec='fees'),
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
{"sk": {"S": "items"}, "id": {"S": "XhAgiMsDG9rLhkN9urFru3"}, "items": {"L": [{"M": {"name": {"S": "NR-10 B\u00e1sico"}, "id": {"S": "38"}, "quantity": {"N": "1"}, "unit_price": {"N": "149"}}}]}}
|
||||
{"qrcode": {"S": "https://faturas.iugu.com/qr_code/d61cb3b6-da4d-48af-8497-4e2ed1f2f92f-27f9"}, "sk": {"S": "pix"}, "id": {"S": "XhAgiMsDG9rLhkN9urFru3"}, "qrcode_text": {"S": "00020101021226840014br.gov.bcb.pix2562qr.iugu.com/public/payload/v2/D61CB3B6DA4D48AF84974E2ED1F2F92F5204000053039865406149.005802BR5922MACIEL DOS SANTOS CIA6013FLORIANOPOLIS62070503***6304068E"}, "create_date": {"S": "2023-12-28T10:19:49.867940-03:00"}}
|
||||
{"sk": {"S": "user"}, "user_id": {"S": "c57MXV4BByFZLajcvBHdnN"}, "id": {"S": "XhAgiMsDG9rLhkN9urFru3"}, "create_date": {"S": "2023-12-28T10:20:54.229700-03:00"}}
|
||||
{"payment_method": {"S": "CREDIT_CARD"}, "status": {"S": "PAID"}, "assignee": {"M": {"name": {"S": "Alessandra Larivia"}}}, "total": {"N": "149"}, "installments": {"N": "1"}, "due_date": {"S": "2024-02-08T08:46:59.771233-03:00"}, "email": {"S": "financeiro@aquanobile.com.br"}, "name": {"S": "AQUA NOBILE SERVI\u00c7OS LTDA"}, "create_date": {"S": "2024-02-08T08:41:59.773135-03:00"}, "payment_date": {"S": "2024-02-08T08:42:10.910170-03:00"}, "phone_number": {"S": "+553130705599"}, "sk": {"S": "0"}, "cnpj": {"S": "11278500000106"}, "id": {"S": "KpZTYvu4RzgMJW3A2DF6cC"}, "update_date": {"S": "2024-02-08T08:42:10.910170-03:00"}}
|
||||
{"payment_method": {"S": "CREDIT_CARD"}, "status": {"S": "PAID"}, "assignee": {"M": {"name": {"S": "Alessandra Larivia"}}}, "total": {"N": "149"}, "installments": {"N": "1"}, "due_date": {"S": "2024-02-08T08:46:59.771233-03:00"}, "email": {"S": "financeiro@aquanobile.com.br"}, "name": {"S": "AQUA NOBILE SERVI\u00c7OS LTDA"}, "create_date": {"S": "2024-02-08T08:41:59.773135-03:00"}, "payment_date": {"S": "2024-02-08T08:42:10.910170-03:00"}, "phone_number": {"S": "+553130705599"}, "sk": {"S": "0"}, "cnpj": {"S": "11278500000106"}, "id": {"S": "KpZTYvu4RzgMJW3A2DF6cC"}, "update_date": {"S": "2024-02-08T08:42:10.910170-03:00"}, "tenant": {"S": "123"}}
|
||||
{"city": {"S": "Nova Lima"}, "postcode": {"S": "34006065"}, "neighborhood": {"S": "Vila da Serra"}, "complement": {"S": "sala 211"}, "sk": {"S": "address"}, "id": {"S": "KpZTYvu4RzgMJW3A2DF6cC"}, "street_number": {"S": "1021"}, "street": {"S": "Alameda Oscar Niemeyer"}, "state": {"S": "MG"}}
|
||||
{"last4": {"S": "6188"}, "brand": {"S": "Mastercard"}, "sk": {"S": "credit_card"}, "id": {"S": "KpZTYvu4RzgMJW3A2DF6cC"}}
|
||||
{"sk": {"S": "fees"}, "fees": {"N": "5"}, "id": {"S": "KpZTYvu4RzgMJW3A2DF6cC"}, "create_date": {"S": "2024-02-08T08:42:13.440374-03:00"}}
|
||||
@@ -25,7 +25,6 @@
|
||||
{"city": {"S": "Marab\u00e1"}, "postcode": {"S": "68501535"}, "neighborhood": {"S": "Cidade Nova"}, "complement": {"S": ""}, "sk": {"S": "address"}, "id": {"S": "3Gkayh3FBuCSMW5eE5NfH5"}, "street_number": {"S": "S/N"}, "street": {"S": "Rua Ivete Vargas"}, "state": {"S": "PA"}}
|
||||
{"sk": {"S": "items"}, "id": {"S": "3Gkayh3FBuCSMW5eE5NfH5"}, "items": {"L": [{"M": {"name": {"S": "NR-10 Complementar (SEP)"}, "id": {"S": "55"}, "quantity": {"N": "6"}, "unit_price": {"N": "149"}}}]}}
|
||||
{"sk": {"S": "lock"}, "id": {"S": "3Gkayh3FBuCSMW5eE5NfH5"}, "lock_type": {"S": "CNPJ"}, "create_date": {"S": "2024-02-08T07:52:14.397071-03:00"}}
|
||||
{"sk": {"S": "nfse"}, "nfse": {"S": "10382"}, "id": {"S": "3Gkayh3FBuCSMW5eE5NfH5"}, "create_date": {"S": "2024-02-08T07:52:14.361212-03:00"}}
|
||||
{"sk": {"S": "user"}, "user_id": {"S": "EkvQwpmmL6vzWtJunM5dCJ"}, "id": {"S": "3Gkayh3FBuCSMW5eE5NfH5"}, "create_date": {"S": "2024-02-08T07:48:56.154952-03:00"}}
|
||||
{"payment_method": {"S": "CREDIT_CARD"}, "status": {"S": "DECLINED"}, "assignee": {"M": {"name": {"S": "ALEXANDRE ROZIN"}}}, "total": {"N": "605"}, "installments": {"N": "2"}, "due_date": {"S": "2024-02-05T09:30:16.163998-03:00"}, "email": {"S": "alexandre@hartinst.com.br"}, "name": {"S": "HARTINST MANUTEN\u00c7\u00c3O INDUSTRIAL"}, "create_date": {"S": "2024-02-05T09:25:16.166315-03:00"}, "payment_date": {"NULL": true}, "phone_number": {"S": "+551936010400"}, "sk": {"S": "0"}, "cnpj": {"S": "31958303000145"}, "id": {"S": "mFERanksjRywrAniUdVmoV"}, "update_date": {"S": "2024-02-05T09:25:28.662366-03:00"}}
|
||||
{"city": {"S": "Americana"}, "postcode": {"S": "13467600"}, "neighborhood": {"S": "Parque Novo Mundo"}, "complement": {"S": ""}, "sk": {"S": "address"}, "id": {"S": "mFERanksjRywrAniUdVmoV"}, "street_number": {"S": "3929"}, "street": {"S": "Avenida de Cillo"}, "state": {"S": "SP"}}
|
||||
@@ -41,3 +40,4 @@
|
||||
{"sk": {"S": "user"}, "user_id": {"S": "mESNbpk4pMTASxtnstd37B"}, "id": {"S": "bTRW6w69aYYKjKy3FZeVjt"}, "create_date": {"S": "2024-02-06T10:28:06.906367-03:00"}}
|
||||
{"id": {"S": "QV4sXY3DvSTUMGJ4QqsrwJ"},"sk": {"S": "0"},"assignee": {"M": {"name": {"S": "Sérgio Rafael de Siqueira"}}},"cnpj": {"S": "15608435000190"},"create_date": {"S": "2024-11-02T19:39:57.174669-03:00"},"due_date": {"S": "2024-11-03T19:39:09.897000-03:00"},"email": {"S": "sergio@somosbeta.com.br"},"name": {"S": "Beta Educação"},"payment_date": {"S": "2024-11-02T19:40:12.143000-03:00"},"payment_method": {"S": "MANUAL"},"status": {"S": "PAID"},"total": {"N": "10"},"update_date": {"S": "2024-11-02T19:40:12.831120-03:00"}}
|
||||
{"id": {"S": "QV4sXY3DvSTUMGJ4QqsrwJ"},"sk": {"S": "generated_items#7bzQhaPEB9uR8jRkzkFe2h"},"create_date": {"S": "2025-04-11T15:08:00.256341-03:00"},"status": {"S": "SUCCESS"}}
|
||||
{"id": {"S": "123"}, "sk": {"S": "0"}}
|
||||
@@ -40,7 +40,7 @@ Globals:
|
||||
COURSE_TABLE: !Ref CourseTable
|
||||
ELASTIC_CLOUD_ID: "{{resolve:ssm:/betaeducacao/elastic/cloud_id/str}}"
|
||||
ELASTIC_AUTH_PASS: "{{resolve:ssm:/betaeducacao/elastic/auth_pass/str}}"
|
||||
KONVIVA_API_URL: https://saladeaula.digital
|
||||
KONVIVA_API_URL: https://lms.saladeaula.digital
|
||||
KONVIVA_SECRET_KEY: "{{resolve:ssm:/betaeducacao/konviva/secret_key/str}}"
|
||||
MEILISEARCH_HOST: https://meili.eduseg.com.br
|
||||
MEILISEARCH_API_KEY: "{{resolve:ssm:/saladeaula/meili_api_key}}"
|
||||
|
||||
@@ -21,6 +21,7 @@ def pytest_configure():
|
||||
os.environ['USER_TABLE'] = PYTEST_TABLE_NAME
|
||||
os.environ['COURSE_TABLE'] = PYTEST_TABLE_NAME
|
||||
os.environ['ENROLLMENT_TABLE'] = PYTEST_TABLE_NAME
|
||||
os.environ['ORDER_TABLE'] = PYTEST_TABLE_NAME
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -40,6 +41,7 @@ class HttpApiProxy:
|
||||
*,
|
||||
headers: dict = {},
|
||||
auth_flow_type: str = 'USER_AUTH',
|
||||
queryStringParameters: dict = {},
|
||||
**kwargs,
|
||||
) -> dict:
|
||||
return {
|
||||
@@ -49,10 +51,7 @@ class HttpApiProxy:
|
||||
'rawQueryString': 'parameter1=value1¶meter1=value2¶meter2=value',
|
||||
'cookies': ['cookie1', 'cookie2'],
|
||||
'headers': headers,
|
||||
'queryStringParameters': {
|
||||
'parameter1': 'value1,value2',
|
||||
'parameter2': 'value',
|
||||
},
|
||||
'queryStringParameters': queryStringParameters,
|
||||
'requestContext': {
|
||||
'accountId': '123456789012',
|
||||
'apiId': 'api-id',
|
||||
|
||||
48
http-api/tests/routes/test_orders.py
Normal file
48
http-api/tests/routes/test_orders.py
Normal file
@@ -0,0 +1,48 @@
|
||||
import json
|
||||
import urllib.parse as parse
|
||||
from http import HTTPMethod, HTTPStatus
|
||||
|
||||
from ..conftest import HttpApiProxy, LambdaContext
|
||||
|
||||
|
||||
def test_orders(
|
||||
mock_app,
|
||||
dynamodb_seeds,
|
||||
http_api_proxy: HttpApiProxy,
|
||||
lambda_context: LambdaContext,
|
||||
):
|
||||
# This data was added from seeds
|
||||
r = mock_app.lambda_handler(
|
||||
http_api_proxy(
|
||||
raw_path='/orders',
|
||||
queryStringParameters={
|
||||
'filter': parse.quote('status = PENDING AND due_date >= 202025-07-09'),
|
||||
},
|
||||
method=HTTPMethod.GET,
|
||||
headers={'x-tenant': 'cJtK9SsnJhKPyxESe7g3DG'},
|
||||
),
|
||||
lambda_context,
|
||||
)
|
||||
|
||||
assert r['statusCode'] == HTTPStatus.OK
|
||||
|
||||
|
||||
def test_get_order(
|
||||
mock_app,
|
||||
dynamodb_seeds,
|
||||
http_api_proxy: HttpApiProxy,
|
||||
lambda_context: LambdaContext,
|
||||
):
|
||||
# This data was added from seeds
|
||||
r = mock_app.lambda_handler(
|
||||
http_api_proxy(
|
||||
raw_path='/orders/KpZTYvu4RzgMJW3A2DF6cC',
|
||||
method=HTTPMethod.GET,
|
||||
),
|
||||
lambda_context,
|
||||
)
|
||||
|
||||
assert r['statusCode'] == HTTPStatus.OK
|
||||
|
||||
body = json.loads(r['body'])
|
||||
print(body)
|
||||
@@ -23,3 +23,12 @@
|
||||
{"id": {"S": "cpf"}, "sk": {"S": "08679004901"}}
|
||||
{"id": {"S": "lock"}, "sk": {"S": "c2116a43f8f1aed659a10c83dab17ed3"}}
|
||||
{"id": {"S": "vacancies#cJtK9SsnJhKPyxESe7g3DG"}, "sk": {"S": "3CNrFB9dy2RLit2pdeUWy4#8c9b55ef-e988-43ee-b2da-8594850605d7"}}
|
||||
{"payment_method": {"S": "CREDIT_CARD"}, "status": {"S": "PAID"}, "assignee": {"M": {"name": {"S": "Alessandra Larivia"}}}, "total": {"N": "149"}, "installments": {"N": "1"}, "due_date": {"S": "2024-02-08T08:46:59.771233-03:00"}, "email": {"S": "financeiro@aquanobile.com.br"}, "name": {"S": "AQUA NOBILE SERVI\u00c7OS LTDA"}, "create_date": {"S": "2024-02-08T08:41:59.773135-03:00"}, "payment_date": {"S": "2024-02-08T08:42:10.910170-03:00"}, "phone_number": {"S": "+553130705599"}, "sk": {"S": "0"}, "cnpj": {"S": "11278500000106"}, "id": {"S": "KpZTYvu4RzgMJW3A2DF6cC"}, "update_date": {"S": "2024-02-08T08:42:10.910170-03:00"}, "tenant": {"S": "cJtK9SsnJhKPyxESe7g3DG"}}
|
||||
{"city": {"S": "Nova Lima"}, "postcode": {"S": "34006065"}, "neighborhood": {"S": "Vila da Serra"}, "complement": {"S": "sala 211"}, "sk": {"S": "address"}, "id": {"S": "KpZTYvu4RzgMJW3A2DF6cC"}, "street_number": {"S": "1021"}, "street": {"S": "Alameda Oscar Niemeyer"}, "state": {"S": "MG"}}
|
||||
{"last4": {"S": "6188"}, "brand": {"S": "Mastercard"}, "sk": {"S": "credit_card"}, "id": {"S": "KpZTYvu4RzgMJW3A2DF6cC"}}
|
||||
{"sk": {"S": "fees"}, "fees": {"N": "5"}, "id": {"S": "KpZTYvu4RzgMJW3A2DF6cC"}, "create_date": {"S": "2024-02-08T08:42:13.440374-03:00"}}
|
||||
{"sk": {"S": "invoice"}, "pdf": {"S": "https://faturas.iugu.com/cdc228a0-bd1a-44aa-aa0d-1b47bad74aed-873a.pdf"}, "id": {"S": "KpZTYvu4RzgMJW3A2DF6cC"}, "create_date": {"S": "2024-02-08T08:42:03.041809-03:00"}, "invoice_id": {"S": "cdc228a0-bd1a-44aa-aa0d-1b47bad74aed-873a"}}
|
||||
{"sk": {"S": "items"}, "id": {"S": "KpZTYvu4RzgMJW3A2DF6cC"}, "items": {"L": [{"M": {"name": {"S": "NR-33 Supervisor em Espa\u00e7o Confinado"}, "id": {"S": "57"}, "quantity": {"N": "1"}, "unit_price": {"N": "149"}}}]}}
|
||||
{"sk": {"S": "lock"}, "id": {"S": "KpZTYvu4RzgMJW3A2DF6cC"}, "lock_type": {"S": "CNPJ"}, "create_date": {"S": "2024-02-08T08:42:13.058916-03:00"}}
|
||||
{"sk": {"S": "nfse"}, "nfse": {"S": "10384"}, "id": {"S": "KpZTYvu4RzgMJW3A2DF6cC"}, "create_date": {"S": "2024-02-08T09:05:03.879692-03:00"}}
|
||||
{"sk": {"S": "user"}, "user_id": {"S": "5AZXXXCWa2bU4spsxfLznx"}, "id": {"S": "KpZTYvu4RzgMJW3A2DF6cC"}, "create_date": {"S": "2024-02-08T08:42:05.190415-03:00"}}
|
||||
|
||||
9
http-api/tests/test_filter.py
Normal file
9
http-api/tests/test_filter.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import json
|
||||
import urllib.parse as parse
|
||||
|
||||
|
||||
def test_filter():
|
||||
f = '[{%22attr%22%3A%22status%22%2C%22op%22%3A%22%3D%22%2C%22value%22%3A%22PENDING%22}]'
|
||||
decoded = parse.unquote(f)
|
||||
filtros = json.loads(decoded)
|
||||
print(filtros)
|
||||
11
http-api/tests/test_meili.py
Normal file
11
http-api/tests/test_meili.py
Normal file
@@ -0,0 +1,11 @@
|
||||
import meili
|
||||
|
||||
|
||||
def test_parse():
|
||||
s = 'payment_date >= 2025-07-01 AND status = PAID'
|
||||
r = meili.parse(s)
|
||||
|
||||
assert r == [
|
||||
{'attr': 'payment_date', 'op': '>=', 'value': '2025-07-01'},
|
||||
{'attr': 'status', 'op': '=', 'value': 'PAID'},
|
||||
]
|
||||
Reference in New Issue
Block a user