biatoolkit 1.2.0__tar.gz → 1.2.2__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: biatoolkit
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: Biblioteca para desenvolvedores que utilizam o BiaAgentBuilder
5
5
  Author: Bia Platform Team
6
6
  Author-email: data.platform@sankhya.com.br
@@ -462,9 +462,58 @@ O Bia Toolkit oferece integração pronta para consumo de serviços HTTP da plat
462
462
 
463
463
  - **SankhyaSettings**: Dataclass de configuração de timeouts/retries/SSL. Use `SankhyaSettings.from_env()` para obter as configurações do ambiente.
464
464
  - **Sankhya**: Classe principal de integração. Permite instanciar com contexto MCP (`FastMCP`) ou usar métodos estáticos.
465
- - `call_json(...)`: Realiza chamada HTTP autenticada, retorna JSON.
466
- - `load_view(...)`: Helper para consultas a views Sankhya (`CRUDServiceProvider.loadView`).
467
- - `Call(...)`: Método estático compatível com scaffolds legados.
465
+ - `call_json(...)`: Realiza chamada HTTP autenticada, retorna JSON. Veja detalhes dos parâmetros abaixo.
466
+ - `load_view(...)`: Helper para consultas a views Sankhya (`CRUDServiceProvider.loadView`). Veja detalhes dos parâmetros abaixo.
467
+ - `Call(...)`: Método estático compatível com scaffolds legados. Veja detalhes dos parâmetros abaixo.
468
+ ### Parâmetros dos métodos principais
469
+
470
+ #### `call_json`
471
+
472
+ | Parâmetro | Obrigatório | Descrição |
473
+ |-------------------|-------------|-----------|
474
+ | payload | Não | Dicionário enviado como corpo da requisição (POST). Pode ser None. |
475
+ | jsessionid | Não | Token de sessão JSESSIONID. Se não informado, tenta extrair do header do runtime (via MCP/BiaUtil). |
476
+ | url | Não | URL completa para chamada. Se informado, ignora base_url. |
477
+ | base_url | Sim* | Base URL do serviço Sankhya (ex: https://meu.sankhya.com.br). Obrigatório se não estiver em contexto MCP (header). |
478
+ | query | Não | Querystring adicional (ex: serviceName=...&outputType=json). |
479
+ | method | Não | "POST" (default) ou "GET". |
480
+ | extra_headers | Não | Dicionário de headers adicionais. |
481
+ | timeout | Não | Tupla (connect, read) para sobrescrever timeout padrão. |
482
+ | raise_for_http_error | Não | Se True (default), lança SankhyaHTTPError em erro HTTP. |
483
+
484
+ *Se não passar base_url, o método tentará extrair automaticamente do header do runtime (MCP) se disponível. Caso contrário, será obrigatório.
485
+
486
+ #### `load_view`
487
+
488
+ | Parâmetro | Obrigatório | Descrição |
489
+ |-------------------|-------------|-----------|
490
+ | view_name | Sim | Nome da view no Sankhya. |
491
+ | where_sql | Sim | Cláusula WHERE (string). |
492
+ | fields | Não | Campos a retornar (string). Default: "*". |
493
+ | jsessionid | Não | Token de sessão JSESSIONID. Se não informado, tenta extrair do header do runtime (via MCP/BiaUtil). |
494
+ | url | Não | URL completa opcional (override). |
495
+ | base_url | Não* | Base URL do serviço Sankhya. Se não informado, tenta extrair do header do runtime (current_host). |
496
+ | output_type | Não | "json" (default). |
497
+ | extra_headers | Não | Dicionário de headers adicionais. |
498
+
499
+ *Se não passar base_url, o método tentará extrair automaticamente do header do runtime (MCP) via BiaUtil. Caso não consiga, lança erro.
500
+
501
+ #### `Call`
502
+
503
+ | Parâmetro | Obrigatório | Descrição |
504
+ |-------------------|-------------|-----------|
505
+ | jsessionID | Não | Token de sessão JSESSIONID. Se não informado, tenta extrair do header do runtime (via MCP/BiaUtil). |
506
+ | payload | Não | Dicionário enviado como corpo da requisição. |
507
+ | mcp | Não | Instância opcional do FastMCP para contexto do runtime. |
508
+ | url | Não | URL completa (opcional). |
509
+ | base_url | Sim* | Base URL do serviço Sankhya. Obrigatório se não estiver em contexto MCP (header). |
510
+ | query | Não | Querystring adicional (opcional). |
511
+ | method | Não | "POST" (default) ou "GET". |
512
+ | extra_headers | Não | Dicionário de headers adicionais. |
513
+
514
+ *Se não passar base_url, o método tentará extrair automaticamente do header do runtime (MCP) se disponível. Caso contrário, será obrigatório.
515
+
516
+ **Resumo:** Sempre que possível, o toolkit resolve automaticamente base_url e jsessionid do contexto MCP (headers). Se não estiver rodando em MCP, passe base_url explicitamente.
468
517
  - **SankhyaHTTPError**: Exceção lançada em caso de erro HTTP (status != 200), contendo status_code e response_text.
469
518
 
470
519
  ### Exemplo de uso (instanciado)
@@ -364,13 +364,15 @@ class Sankhya:
364
364
  },
365
365
  }
366
366
 
367
-
368
- util = BiaUtil(self.mcp)
369
367
  # Se base_url não foi fornecido, tenta obter do util.get_header().current_host
370
368
  if not base_url:
371
- base_url = util.get_header().current_host
372
- if not base_url:
373
- raise ValueError("base_url não definida e não foi possível obter current_host do header.")
369
+ util = BiaUtil(self.mcp)
370
+ header = util.get_header()
371
+ base_url = getattr(header, "current_host", None)
372
+
373
+ if not base_url:
374
+ raise ValueError("base_url não definida e não foi possível obter current_host do header.")
375
+
374
376
 
375
377
  return self.call_json(
376
378
  payload=body,
File without changes
@@ -0,0 +1,175 @@
1
+ # biatoolkit/validation/coercion.py
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Iterable, Mapping, Optional
6
+ import math
7
+
8
+
9
+ def ensure_list(value: Any) -> list:
10
+ """
11
+ Normaliza um valor para sempre retornar uma lista.
12
+
13
+ Regras:
14
+ - None -> []
15
+ - list -> a própria lista
16
+ - tuple/set -> list(value)
17
+ - dict (ou Mapping) -> [value] (não "explode" dict em chaves)
18
+ - qualquer outro -> [value]
19
+
20
+ Por que existe:
21
+ - Muitas APIs retornam ora um item único (dict), ora uma lista.
22
+ - Este helper padroniza o consumo e reduz if/else espalhado.
23
+
24
+ Args:
25
+ value: qualquer valor.
26
+
27
+ Returns:
28
+ list: lista normalizada.
29
+ """
30
+ if value is None:
31
+ return []
32
+
33
+ if isinstance(value, list):
34
+ return value
35
+
36
+ if isinstance(value, (tuple, set)):
37
+ return list(value)
38
+
39
+ # Dict/Mapping deve ser tratado como item único, não iterável de chaves.
40
+ if isinstance(value, Mapping):
41
+ return [value]
42
+
43
+ return [value]
44
+
45
+
46
+ def unwrap_dollar_value(value: Any) -> Any:
47
+ """
48
+ "Desembrulha" valores no formato {"$": "..."} (comum em integrações legadas).
49
+
50
+ Exemplo:
51
+ {"$": "123"} -> "123"
52
+ {"$": 123} -> 123
53
+
54
+ Se não for dict com a chave "$", retorna o valor original.
55
+
56
+ Args:
57
+ value: valor de entrada.
58
+
59
+ Returns:
60
+ Any: valor desembrulhado ou original.
61
+ """
62
+ if isinstance(value, dict) and "$" in value:
63
+ return value.get("$")
64
+ return value
65
+
66
+
67
+ def to_int(value: Any, default: int = 0) -> int:
68
+ """
69
+ Converte um valor para int de forma segura.
70
+
71
+ Regras:
72
+ - None / "" -> default
73
+ - strings com espaços são aceitas (" 12 ")
74
+ - strings numéricas com sinal são aceitas ("-3")
75
+ - floats numéricos -> int(value) (trunca)
76
+ - NaN/inf -> default
77
+
78
+ Obs:
79
+ - Não tenta "extrair número do meio do texto" (isso é responsabilidade
80
+ de parse específico, ex: parse_int_list).
81
+
82
+ Args:
83
+ value: valor a converter.
84
+ default: fallback.
85
+
86
+ Returns:
87
+ int: convertido ou default.
88
+ """
89
+ try:
90
+ if value is None:
91
+ return default
92
+
93
+ if isinstance(value, bool):
94
+ # Evita True->1 / False->0 de forma "surpresa"
95
+ return default
96
+
97
+ if isinstance(value, str):
98
+ s = value.strip()
99
+ if s == "":
100
+ return default
101
+ return int(s)
102
+
103
+ if isinstance(value, float):
104
+ if math.isnan(value) or math.isinf(value):
105
+ return default
106
+ return int(value)
107
+
108
+ return int(value)
109
+ except (TypeError, ValueError):
110
+ return default
111
+
112
+
113
+ def to_float(value: Any, default: float = 0.0) -> float:
114
+ """
115
+ Converte um valor para float de forma segura.
116
+
117
+ Regras:
118
+ - None / "" -> default
119
+ - aceita strings com vírgula decimal ("12,34")
120
+ - NaN/inf -> default
121
+
122
+ Args:
123
+ value: valor a converter.
124
+ default: fallback.
125
+
126
+ Returns:
127
+ float: convertido ou default.
128
+ """
129
+ try:
130
+ if value is None:
131
+ return default
132
+
133
+ if isinstance(value, bool):
134
+ return default
135
+
136
+ if isinstance(value, str):
137
+ s = value.strip()
138
+ if s == "":
139
+ return default
140
+ s = s.replace(",", ".")
141
+ v = float(s)
142
+ else:
143
+ v = float(value)
144
+
145
+ if math.isnan(v) or math.isinf(v):
146
+ return default
147
+
148
+ return v
149
+ except (TypeError, ValueError):
150
+ return default
151
+
152
+
153
+
154
+
155
+ # Classe fachada para coerção
156
+ class BiaCoercion:
157
+ """
158
+ Fachada estática para funções de coerção do Bia Toolkit.
159
+ Permite referenciar e utilizar as utilidades de coerção de forma padronizada.
160
+ """
161
+ @staticmethod
162
+ def ensure_list(*args, **kwargs):
163
+ return ensure_list(*args, **kwargs)
164
+
165
+ @staticmethod
166
+ def unwrap_dollar_value(*args, **kwargs):
167
+ return unwrap_dollar_value(*args, **kwargs)
168
+
169
+ @staticmethod
170
+ def to_int(*args, **kwargs):
171
+ return to_int(*args, **kwargs)
172
+
173
+ @staticmethod
174
+ def to_float(*args, **kwargs):
175
+ return to_float(*args, **kwargs)
@@ -0,0 +1,135 @@
1
+ # biatoolkit/validation.py
2
+
3
+ from typing import Any, Iterable
4
+ import re
5
+
6
+
7
+ def parse_int_list(
8
+ value: Any,
9
+ *,
10
+ dedupe: bool = True,
11
+ keep_order: bool = True,
12
+ ) -> list[int]:
13
+ """
14
+ Normaliza um valor arbitrário em uma lista de inteiros.
15
+
16
+ Casos suportados:
17
+ - None -> []
18
+ - int -> [int]
19
+ - str -> extrai números (ex: "100, 200" / "SKU=300231 x2")
20
+ - list/tuple -> processa cada item recursivamente
21
+
22
+ Comportamento:
23
+ - Ignora valores inválidos
24
+ - Deduplica por padrão
25
+ - Mantém a ordem de aparição por padrão
26
+
27
+ Args:
28
+ value: Valor de entrada (None, int, str, list, etc.)
29
+ dedupe: Remove valores duplicados.
30
+ keep_order: Mantém a ordem original dos valores.
31
+
32
+ Returns:
33
+ list[int]: Lista normalizada de inteiros.
34
+ """
35
+ if value is None:
36
+ return []
37
+
38
+ # Normaliza para iterável
39
+ if isinstance(value, (list, tuple, set)):
40
+ raw: Iterable[Any] = value
41
+ else:
42
+ raw = [value]
43
+
44
+ numbers: list[int] = []
45
+
46
+ for item in raw:
47
+ if item is None:
48
+ continue
49
+
50
+ # Inteiro direto
51
+ if isinstance(item, int):
52
+ numbers.append(item)
53
+ continue
54
+
55
+ # String ou outros tipos
56
+ text = str(item)
57
+ matches = re.findall(r"\d+", text)
58
+ for m in matches:
59
+ try:
60
+ numbers.append(int(m))
61
+ except ValueError:
62
+ continue
63
+
64
+ if not dedupe:
65
+ return numbers
66
+
67
+ if keep_order:
68
+ seen = set()
69
+ ordered: list[int] = []
70
+ for n in numbers:
71
+ if n not in seen:
72
+ seen.add(n)
73
+ ordered.append(n)
74
+ return ordered
75
+
76
+ return list(set(numbers))
77
+
78
+ def sanitize_like(
79
+ value: str,
80
+ *,
81
+ max_len: int = 80,
82
+ upper: bool = True,
83
+ ) -> str:
84
+ """
85
+ Sanitiza um texto para uso seguro em filtros do tipo LIKE/search.
86
+
87
+ Regras aplicadas:
88
+ - Remove caracteres fora de uma allowlist básica
89
+ - Limita o tamanho do texto
90
+ - Escapa aspas simples
91
+ - Escapa curingas comuns (% e _)
92
+ - Converte para UPPER por padrão
93
+
94
+ Args:
95
+ value: Texto de entrada.
96
+ max_len: Tamanho máximo permitido.
97
+ upper: Converte o texto final para maiúsculas.
98
+
99
+ Returns:
100
+ str: Texto sanitizado.
101
+ """
102
+ if not value:
103
+ return ""
104
+
105
+ # Normaliza e corta tamanho
106
+ text = str(value).strip()[:max_len]
107
+
108
+ # Allowlist simples (letras, números, acentos, espaço e alguns símbolos, incluindo ', %, _)
109
+ # Mantém ', %, _ para escapá-los depois
110
+ text = re.sub(r"[^0-9A-Za-zÀ-ÿ\s\-\(\)\./'_%]", " ", text)
111
+
112
+ # Escapes básicos
113
+ text = text.replace("'", "''")
114
+ text = text.replace("%", r"\%" ).replace("_", r"\_")
115
+
116
+ # Normaliza espaços
117
+ text = re.sub(r"\s+", " ", text).strip()
118
+
119
+
120
+ return text.upper() if upper else text
121
+
122
+
123
+ # Classe fachada para validação
124
+ class BiaValidation:
125
+ """
126
+ Fachada estática para funções de validação do Bia Toolkit.
127
+ Permite referenciar e utilizar as utilidades de validação de forma padronizada.
128
+ """
129
+ @staticmethod
130
+ def parse_int_list(*args, **kwargs):
131
+ return parse_int_list(*args, **kwargs)
132
+
133
+ @staticmethod
134
+ def sanitize_like(*args, **kwargs):
135
+ return sanitize_like(*args, **kwargs)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: biatoolkit
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: Biblioteca para desenvolvedores que utilizam o BiaAgentBuilder
5
5
  Author: Bia Platform Team
6
6
  Author-email: data.platform@sankhya.com.br
@@ -462,9 +462,58 @@ O Bia Toolkit oferece integração pronta para consumo de serviços HTTP da plat
462
462
 
463
463
  - **SankhyaSettings**: Dataclass de configuração de timeouts/retries/SSL. Use `SankhyaSettings.from_env()` para obter as configurações do ambiente.
464
464
  - **Sankhya**: Classe principal de integração. Permite instanciar com contexto MCP (`FastMCP`) ou usar métodos estáticos.
465
- - `call_json(...)`: Realiza chamada HTTP autenticada, retorna JSON.
466
- - `load_view(...)`: Helper para consultas a views Sankhya (`CRUDServiceProvider.loadView`).
467
- - `Call(...)`: Método estático compatível com scaffolds legados.
465
+ - `call_json(...)`: Realiza chamada HTTP autenticada, retorna JSON. Veja detalhes dos parâmetros abaixo.
466
+ - `load_view(...)`: Helper para consultas a views Sankhya (`CRUDServiceProvider.loadView`). Veja detalhes dos parâmetros abaixo.
467
+ - `Call(...)`: Método estático compatível com scaffolds legados. Veja detalhes dos parâmetros abaixo.
468
+ ### Parâmetros dos métodos principais
469
+
470
+ #### `call_json`
471
+
472
+ | Parâmetro | Obrigatório | Descrição |
473
+ |-------------------|-------------|-----------|
474
+ | payload | Não | Dicionário enviado como corpo da requisição (POST). Pode ser None. |
475
+ | jsessionid | Não | Token de sessão JSESSIONID. Se não informado, tenta extrair do header do runtime (via MCP/BiaUtil). |
476
+ | url | Não | URL completa para chamada. Se informado, ignora base_url. |
477
+ | base_url | Sim* | Base URL do serviço Sankhya (ex: https://meu.sankhya.com.br). Obrigatório se não estiver em contexto MCP (header). |
478
+ | query | Não | Querystring adicional (ex: serviceName=...&outputType=json). |
479
+ | method | Não | "POST" (default) ou "GET". |
480
+ | extra_headers | Não | Dicionário de headers adicionais. |
481
+ | timeout | Não | Tupla (connect, read) para sobrescrever timeout padrão. |
482
+ | raise_for_http_error | Não | Se True (default), lança SankhyaHTTPError em erro HTTP. |
483
+
484
+ *Se não passar base_url, o método tentará extrair automaticamente do header do runtime (MCP) se disponível. Caso contrário, será obrigatório.
485
+
486
+ #### `load_view`
487
+
488
+ | Parâmetro | Obrigatório | Descrição |
489
+ |-------------------|-------------|-----------|
490
+ | view_name | Sim | Nome da view no Sankhya. |
491
+ | where_sql | Sim | Cláusula WHERE (string). |
492
+ | fields | Não | Campos a retornar (string). Default: "*". |
493
+ | jsessionid | Não | Token de sessão JSESSIONID. Se não informado, tenta extrair do header do runtime (via MCP/BiaUtil). |
494
+ | url | Não | URL completa opcional (override). |
495
+ | base_url | Não* | Base URL do serviço Sankhya. Se não informado, tenta extrair do header do runtime (current_host). |
496
+ | output_type | Não | "json" (default). |
497
+ | extra_headers | Não | Dicionário de headers adicionais. |
498
+
499
+ *Se não passar base_url, o método tentará extrair automaticamente do header do runtime (MCP) via BiaUtil. Caso não consiga, lança erro.
500
+
501
+ #### `Call`
502
+
503
+ | Parâmetro | Obrigatório | Descrição |
504
+ |-------------------|-------------|-----------|
505
+ | jsessionID | Não | Token de sessão JSESSIONID. Se não informado, tenta extrair do header do runtime (via MCP/BiaUtil). |
506
+ | payload | Não | Dicionário enviado como corpo da requisição. |
507
+ | mcp | Não | Instância opcional do FastMCP para contexto do runtime. |
508
+ | url | Não | URL completa (opcional). |
509
+ | base_url | Sim* | Base URL do serviço Sankhya. Obrigatório se não estiver em contexto MCP (header). |
510
+ | query | Não | Querystring adicional (opcional). |
511
+ | method | Não | "POST" (default) ou "GET". |
512
+ | extra_headers | Não | Dicionário de headers adicionais. |
513
+
514
+ *Se não passar base_url, o método tentará extrair automaticamente do header do runtime (MCP) se disponível. Caso contrário, será obrigatório.
515
+
516
+ **Resumo:** Sempre que possível, o toolkit resolve automaticamente base_url e jsessionid do contexto MCP (headers). Se não estiver rodando em MCP, passe base_url explicitamente.
468
517
  - **SankhyaHTTPError**: Exceção lançada em caso de erro HTTP (status != 200), contendo status_code e response_text.
469
518
 
470
519
  ### Exemplo de uso (instanciado)
@@ -11,4 +11,7 @@ biatoolkit.egg-info/SOURCES.txt
11
11
  biatoolkit.egg-info/dependency_links.txt
12
12
  biatoolkit.egg-info/top_level.txt
13
13
  biatoolkit/schema/__init__.py
14
- biatoolkit/schema/header.py
14
+ biatoolkit/schema/header.py
15
+ biatoolkit/validation/__init__.py
16
+ biatoolkit/validation/coercion.py
17
+ biatoolkit/validation/validation.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='biatoolkit',
5
- version='1.2.0',
5
+ version='1.2.2',
6
6
  packages=find_packages(),
7
7
  install_requires=[],
8
8
  author='Bia Platform Team',
File without changes
File without changes