positiva-consultas-python-sdk 1.0.0__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.
- positiva_consultas_python_sdk-1.0.0/LICENSE +5 -0
- positiva_consultas_python_sdk-1.0.0/PKG-INFO +114 -0
- positiva_consultas_python_sdk-1.0.0/README.md +89 -0
- positiva_consultas_python_sdk-1.0.0/pyproject.toml +34 -0
- positiva_consultas_python_sdk-1.0.0/setup.cfg +4 -0
- positiva_consultas_python_sdk-1.0.0/src/positiva/consultas/__init__.py +12 -0
- positiva_consultas_python_sdk-1.0.0/src/positiva/consultas/api.py +106 -0
- positiva_consultas_python_sdk-1.0.0/src/positiva/consultas/apis/__init__.py +4 -0
- positiva_consultas_python_sdk-1.0.0/src/positiva/consultas/apis/juridico360.py +169 -0
- positiva_consultas_python_sdk-1.0.0/src/positiva/consultas/dtos/__init__.py +40 -0
- positiva_consultas_python_sdk-1.0.0/src/positiva/consultas/dtos/auth.py +173 -0
- positiva_consultas_python_sdk-1.0.0/src/positiva/consultas/dtos/enums.py +28 -0
- positiva_consultas_python_sdk-1.0.0/src/positiva/consultas/dtos/juridico360.py +676 -0
- positiva_consultas_python_sdk-1.0.0/src/positiva/consultas/dtos/serde.py +62 -0
- positiva_consultas_python_sdk-1.0.0/src/positiva_consultas_python_sdk.egg-info/PKG-INFO +114 -0
- positiva_consultas_python_sdk-1.0.0/src/positiva_consultas_python_sdk.egg-info/SOURCES.txt +20 -0
- positiva_consultas_python_sdk-1.0.0/src/positiva_consultas_python_sdk.egg-info/dependency_links.txt +1 -0
- positiva_consultas_python_sdk-1.0.0/src/positiva_consultas_python_sdk.egg-info/requires.txt +3 -0
- positiva_consultas_python_sdk-1.0.0/src/positiva_consultas_python_sdk.egg-info/top_level.txt +1 -0
- positiva_consultas_python_sdk-1.0.0/tests/test_auth_api.py +120 -0
- positiva_consultas_python_sdk-1.0.0/tests/test_juridico360_api.py +106 -0
- positiva_consultas_python_sdk-1.0.0/tests/test_juridico360_dtos.py +113 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: positiva-consultas-python-sdk
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Client HTTP de comunicacao com a API da Positiva Consultas (PositivaConnect)
|
|
5
|
+
Author-email: Filipe Coelho <filipe@fmconsult.com.br>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Seu Nome
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy...
|
|
11
|
+
|
|
12
|
+
Project-URL: Homepage, https://github.com/FMConsult/positiva-consultas-python-sdk
|
|
13
|
+
Project-URL: Bug Tracker, https://github.com/FMConsult/positiva-consultas-python-sdk/issues
|
|
14
|
+
Project-URL: Documentation, https://positivaconnect.positivaconsultas.com.br/developers/documentation
|
|
15
|
+
Keywords: sdk,api,positiva,consultas,juridico360
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest; extra == "dev"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# positiva-consultas-python-sdk
|
|
27
|
+
|
|
28
|
+
Client HTTP Python para a [API Positiva Consultas (PositivaConnect)](https://positivaconnect.positivaconsultas.com.br/developers/documentation).
|
|
29
|
+
|
|
30
|
+
Escopo inicial:
|
|
31
|
+
|
|
32
|
+
- Autenticacao OAuth 2.0 Client Credentials (`POST /client/token`)
|
|
33
|
+
- Positiva Juridico 360 (`POST /api/client/provider/juridico360` e `/pdf`)
|
|
34
|
+
|
|
35
|
+
## Instalacao
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install positiva-consultas-python-sdk
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Requer Python >= 3.11 e o ecossistema `fmconsult` (`ApiBase`).
|
|
42
|
+
|
|
43
|
+
## Credenciais
|
|
44
|
+
|
|
45
|
+
Configure as variaveis de ambiente:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
export positiva.consultas.api.client_id="seu_client_id"
|
|
49
|
+
export positiva.consultas.api.client_secret="seu_client_secret"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Uso
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from positiva.consultas import Juridico360Api, Juridico360ProductCode
|
|
56
|
+
from positiva.consultas.dtos.juridico360 import Juridico360Request
|
|
57
|
+
|
|
58
|
+
api = Juridico360Api() # autentica automaticamente via /client/token
|
|
59
|
+
|
|
60
|
+
# Consulta processual (um ou mais produtos)
|
|
61
|
+
response = api.consultar(
|
|
62
|
+
Juridico360Request(
|
|
63
|
+
document="11111111111",
|
|
64
|
+
product_code=[
|
|
65
|
+
Juridico360ProductCode.CRIMINAL,
|
|
66
|
+
Juridico360ProductCode.CIVIL,
|
|
67
|
+
],
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
print(response.success, response.query_date)
|
|
72
|
+
criminal = response.results.get("CRIMINAL")
|
|
73
|
+
if criminal and criminal.success and criminal.data:
|
|
74
|
+
print(criminal.data.nome, len(criminal.data.processos or []))
|
|
75
|
+
|
|
76
|
+
# PDF do relatorio (Base64)
|
|
77
|
+
pdf = api.gerar_pdf(
|
|
78
|
+
document="11111111111",
|
|
79
|
+
product_code=["CRIMINAL", "CIVIL"],
|
|
80
|
+
)
|
|
81
|
+
print(pdf.mime_type, len(pdf.pdf_base64 or ""))
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Autenticacao utilitaria
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from positiva.consultas import PositivaConsultasApi
|
|
88
|
+
|
|
89
|
+
api = PositivaConsultasApi()
|
|
90
|
+
me = api.me()
|
|
91
|
+
print(me.client.name, me.client.cnpj)
|
|
92
|
+
|
|
93
|
+
api.refresh() # renova o Bearer
|
|
94
|
+
api.logout() # invalida o token atual
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Produtos Juridico 360
|
|
98
|
+
|
|
99
|
+
| Codigo | Descricao |
|
|
100
|
+
| ------ | --------- |
|
|
101
|
+
| `CRIMINAL` | Processos criminais |
|
|
102
|
+
| `CIVIL` | Processos civis |
|
|
103
|
+
| `TRABALHISTA` | Processos trabalhistas |
|
|
104
|
+
| `MP` | Registros do Ministerio Publico |
|
|
105
|
+
| `BNMP` | Banco Nacional de Mandados de Prisao |
|
|
106
|
+
| `EMPREGADOR_IRREGULAR` | Empregadores irregulares |
|
|
107
|
+
|
|
108
|
+
Homologacao (credencial em ambiente `homolog`): use documentos de teste `11111111111` … `55555555555` — a API devolve dados ficticios determinísticos.
|
|
109
|
+
|
|
110
|
+
## Documentacao oficial
|
|
111
|
+
|
|
112
|
+
- [Visao geral](https://positivaconnect.positivaconsultas.com.br/developers/documentation)
|
|
113
|
+
- [Authentication](https://positivaconnect.positivaconsultas.com.br/developers/docs/authentication)
|
|
114
|
+
- [Juridico 360](https://positivaconnect.positivaconsultas.com.br/developers/docs/providers/juridico360)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# positiva-consultas-python-sdk
|
|
2
|
+
|
|
3
|
+
Client HTTP Python para a [API Positiva Consultas (PositivaConnect)](https://positivaconnect.positivaconsultas.com.br/developers/documentation).
|
|
4
|
+
|
|
5
|
+
Escopo inicial:
|
|
6
|
+
|
|
7
|
+
- Autenticacao OAuth 2.0 Client Credentials (`POST /client/token`)
|
|
8
|
+
- Positiva Juridico 360 (`POST /api/client/provider/juridico360` e `/pdf`)
|
|
9
|
+
|
|
10
|
+
## Instalacao
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install positiva-consultas-python-sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Requer Python >= 3.11 e o ecossistema `fmconsult` (`ApiBase`).
|
|
17
|
+
|
|
18
|
+
## Credenciais
|
|
19
|
+
|
|
20
|
+
Configure as variaveis de ambiente:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
export positiva.consultas.api.client_id="seu_client_id"
|
|
24
|
+
export positiva.consultas.api.client_secret="seu_client_secret"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Uso
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from positiva.consultas import Juridico360Api, Juridico360ProductCode
|
|
31
|
+
from positiva.consultas.dtos.juridico360 import Juridico360Request
|
|
32
|
+
|
|
33
|
+
api = Juridico360Api() # autentica automaticamente via /client/token
|
|
34
|
+
|
|
35
|
+
# Consulta processual (um ou mais produtos)
|
|
36
|
+
response = api.consultar(
|
|
37
|
+
Juridico360Request(
|
|
38
|
+
document="11111111111",
|
|
39
|
+
product_code=[
|
|
40
|
+
Juridico360ProductCode.CRIMINAL,
|
|
41
|
+
Juridico360ProductCode.CIVIL,
|
|
42
|
+
],
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
print(response.success, response.query_date)
|
|
47
|
+
criminal = response.results.get("CRIMINAL")
|
|
48
|
+
if criminal and criminal.success and criminal.data:
|
|
49
|
+
print(criminal.data.nome, len(criminal.data.processos or []))
|
|
50
|
+
|
|
51
|
+
# PDF do relatorio (Base64)
|
|
52
|
+
pdf = api.gerar_pdf(
|
|
53
|
+
document="11111111111",
|
|
54
|
+
product_code=["CRIMINAL", "CIVIL"],
|
|
55
|
+
)
|
|
56
|
+
print(pdf.mime_type, len(pdf.pdf_base64 or ""))
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Autenticacao utilitaria
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from positiva.consultas import PositivaConsultasApi
|
|
63
|
+
|
|
64
|
+
api = PositivaConsultasApi()
|
|
65
|
+
me = api.me()
|
|
66
|
+
print(me.client.name, me.client.cnpj)
|
|
67
|
+
|
|
68
|
+
api.refresh() # renova o Bearer
|
|
69
|
+
api.logout() # invalida o token atual
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Produtos Juridico 360
|
|
73
|
+
|
|
74
|
+
| Codigo | Descricao |
|
|
75
|
+
| ------ | --------- |
|
|
76
|
+
| `CRIMINAL` | Processos criminais |
|
|
77
|
+
| `CIVIL` | Processos civis |
|
|
78
|
+
| `TRABALHISTA` | Processos trabalhistas |
|
|
79
|
+
| `MP` | Registros do Ministerio Publico |
|
|
80
|
+
| `BNMP` | Banco Nacional de Mandados de Prisao |
|
|
81
|
+
| `EMPREGADOR_IRREGULAR` | Empregadores irregulares |
|
|
82
|
+
|
|
83
|
+
Homologacao (credencial em ambiente `homolog`): use documentos de teste `11111111111` … `55555555555` — a API devolve dados ficticios determinísticos.
|
|
84
|
+
|
|
85
|
+
## Documentacao oficial
|
|
86
|
+
|
|
87
|
+
- [Visao geral](https://positivaconnect.positivaconsultas.com.br/developers/documentation)
|
|
88
|
+
- [Authentication](https://positivaconnect.positivaconsultas.com.br/developers/docs/authentication)
|
|
89
|
+
- [Juridico 360](https://positivaconnect.positivaconsultas.com.br/developers/docs/providers/juridico360)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "positiva-consultas-python-sdk"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Client HTTP de comunicacao com a API da Positiva Consultas (PositivaConnect)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = {file = "LICENSE"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Filipe Coelho", email = "filipe@fmconsult.com.br"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["sdk", "api", "positiva", "consultas", "juridico360"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
dependencies = []
|
|
23
|
+
|
|
24
|
+
[project.optional-dependencies]
|
|
25
|
+
dev = ["pytest"]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
"Homepage" = "https://github.com/FMConsult/positiva-consultas-python-sdk"
|
|
29
|
+
"Bug Tracker" = "https://github.com/FMConsult/positiva-consultas-python-sdk/issues"
|
|
30
|
+
"Documentation" = "https://positivaconnect.positivaconsultas.com.br/developers/documentation"
|
|
31
|
+
|
|
32
|
+
[tool.setuptools.packages.find]
|
|
33
|
+
where = ["src"]
|
|
34
|
+
namespaces = true
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""SDK Python para a API Positiva Consultas (PositivaConnect)."""
|
|
3
|
+
from positiva.consultas.api import PositivaConsultasApi
|
|
4
|
+
from positiva.consultas.apis.juridico360 import Juridico360Api
|
|
5
|
+
from positiva.consultas.dtos.enums import Juridico360ProductCode, MpKind
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
'PositivaConsultasApi',
|
|
9
|
+
'Juridico360Api',
|
|
10
|
+
'Juridico360ProductCode',
|
|
11
|
+
'MpKind',
|
|
12
|
+
]
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Cliente base Positiva Consultas — autenticacao OAuth 2.0 Client Credentials."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import base64
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
import jsonpickle
|
|
10
|
+
from fmconsult.http.api import ApiBase
|
|
11
|
+
from fmconsult.utils.url import UrlUtil
|
|
12
|
+
from http import HTTPMethod
|
|
13
|
+
|
|
14
|
+
from positiva.consultas.dtos.auth import (
|
|
15
|
+
ClientMeResponse,
|
|
16
|
+
LogoutResponse,
|
|
17
|
+
TokenResponse,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PositivaConsultasApi(ApiBase):
|
|
22
|
+
"""API base com login automatico via ``POST /client/token`` (Basic Auth).
|
|
23
|
+
|
|
24
|
+
Variaveis de ambiente:
|
|
25
|
+
- ``positiva.consultas.api.client_id``
|
|
26
|
+
- ``positiva.consultas.api.client_secret``
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
API_BASE_URL = 'https://positivaconnect.positivaconsultas.com.br/api'
|
|
30
|
+
|
|
31
|
+
def __init__(self):
|
|
32
|
+
self.api_client_id = os.environ['positiva.consultas.api.client_id']
|
|
33
|
+
self.api_client_secret = os.environ['positiva.consultas.api.client_secret']
|
|
34
|
+
self.base_url = self.API_BASE_URL
|
|
35
|
+
self.access_token: str | None = None
|
|
36
|
+
self.token_expires_in: int | None = None
|
|
37
|
+
self.headers: dict[str, str] = {
|
|
38
|
+
'Accept': 'application/json',
|
|
39
|
+
}
|
|
40
|
+
self.__login()
|
|
41
|
+
|
|
42
|
+
def __basic_auth_header(self) -> str:
|
|
43
|
+
raw = f'{self.api_client_id}:{self.api_client_secret}'.encode()
|
|
44
|
+
return f'Basic {base64.b64encode(raw).decode()}'
|
|
45
|
+
|
|
46
|
+
def __apply_bearer(self, access_token: str, expires_in: int | None = None) -> None:
|
|
47
|
+
self.access_token = access_token
|
|
48
|
+
self.token_expires_in = expires_in
|
|
49
|
+
self.headers['Authorization'] = f'Bearer {access_token}'
|
|
50
|
+
|
|
51
|
+
def __login(self) -> TokenResponse:
|
|
52
|
+
"""POST /client/token — Authorization Basic (client_id:client_secret)."""
|
|
53
|
+
logging.info('authenticating via Positiva Consultas /client/token...')
|
|
54
|
+
self.headers = {
|
|
55
|
+
'Accept': 'application/json',
|
|
56
|
+
'Authorization': self.__basic_auth_header(),
|
|
57
|
+
}
|
|
58
|
+
url = UrlUtil().make_url(self.base_url, ['client', 'token'])
|
|
59
|
+
res = self.call_request(http_method=HTTPMethod.POST, request_url=url)
|
|
60
|
+
data = jsonpickle.decode(res)
|
|
61
|
+
token = TokenResponse.from_dict(data)
|
|
62
|
+
if token is None or not token.access_token:
|
|
63
|
+
raise Exception(
|
|
64
|
+
data.get('error_description')
|
|
65
|
+
or data.get('message')
|
|
66
|
+
or 'Falha ao obter access_token'
|
|
67
|
+
)
|
|
68
|
+
self.__apply_bearer(token.access_token, token.expires_in)
|
|
69
|
+
return token
|
|
70
|
+
|
|
71
|
+
def me(self) -> ClientMeResponse:
|
|
72
|
+
"""GET /client/me — dados do cliente autenticado."""
|
|
73
|
+
logging.info('fetching Positiva Consultas /client/me...')
|
|
74
|
+
url = UrlUtil().make_url(self.base_url, ['client', 'me'])
|
|
75
|
+
res = self.call_request(http_method=HTTPMethod.GET, request_url=url)
|
|
76
|
+
parsed = ClientMeResponse.from_dict(jsonpickle.decode(res))
|
|
77
|
+
if parsed is None or parsed.client is None:
|
|
78
|
+
raise Exception('Resposta inesperada de /client/me')
|
|
79
|
+
return parsed
|
|
80
|
+
|
|
81
|
+
def refresh(self) -> TokenResponse:
|
|
82
|
+
"""POST /client/refresh — renova o Bearer atual."""
|
|
83
|
+
logging.info('refreshing Positiva Consultas token...')
|
|
84
|
+
url = UrlUtil().make_url(self.base_url, ['client', 'refresh'])
|
|
85
|
+
res = self.call_request(http_method=HTTPMethod.POST, request_url=url)
|
|
86
|
+
data = jsonpickle.decode(res)
|
|
87
|
+
token = TokenResponse.from_dict(data)
|
|
88
|
+
if token is None or not token.access_token:
|
|
89
|
+
raise Exception(
|
|
90
|
+
data.get('error_description')
|
|
91
|
+
or data.get('message')
|
|
92
|
+
or 'Falha ao renovar access_token'
|
|
93
|
+
)
|
|
94
|
+
self.__apply_bearer(token.access_token, token.expires_in)
|
|
95
|
+
return token
|
|
96
|
+
|
|
97
|
+
def logout(self) -> LogoutResponse:
|
|
98
|
+
"""POST /client/logout — invalida o token atual."""
|
|
99
|
+
logging.info('logging out Positiva Consultas token...')
|
|
100
|
+
url = UrlUtil().make_url(self.base_url, ['client', 'logout'])
|
|
101
|
+
res = self.call_request(http_method=HTTPMethod.POST, request_url=url)
|
|
102
|
+
parsed = LogoutResponse.from_dict(jsonpickle.decode(res))
|
|
103
|
+
self.access_token = None
|
|
104
|
+
self.token_expires_in = None
|
|
105
|
+
self.headers.pop('Authorization', None)
|
|
106
|
+
return parsed or LogoutResponse()
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Cliente HTTP Positiva Juridico 360."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Sequence
|
|
7
|
+
|
|
8
|
+
import jsonpickle
|
|
9
|
+
from fmconsult.utils.url import UrlUtil
|
|
10
|
+
from http import HTTPMethod
|
|
11
|
+
|
|
12
|
+
from positiva.consultas.api import PositivaConsultasApi
|
|
13
|
+
from positiva.consultas.dtos.enums import Juridico360ProductCode
|
|
14
|
+
from positiva.consultas.dtos.juridico360 import (
|
|
15
|
+
Juridico360Options,
|
|
16
|
+
Juridico360PdfResponse,
|
|
17
|
+
Juridico360Request,
|
|
18
|
+
Juridico360Response,
|
|
19
|
+
)
|
|
20
|
+
from positiva.consultas.dtos.serde import digits_only, to_wire
|
|
21
|
+
|
|
22
|
+
_VALID_PRODUCTS = {p.value for p in Juridico360ProductCode}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _normalize_product_codes(
|
|
26
|
+
product_code: Sequence[str | Juridico360ProductCode],
|
|
27
|
+
) -> list[str]:
|
|
28
|
+
if not product_code:
|
|
29
|
+
raise ValueError('product_code é obrigatório e não pode ser vazio')
|
|
30
|
+
normalized: list[str] = []
|
|
31
|
+
for code in product_code:
|
|
32
|
+
value = str(to_wire(code))
|
|
33
|
+
if value not in _VALID_PRODUCTS:
|
|
34
|
+
raise ValueError(
|
|
35
|
+
f'product_code inválido: {value!r}. '
|
|
36
|
+
f'Valores aceitos: {sorted(_VALID_PRODUCTS)}'
|
|
37
|
+
)
|
|
38
|
+
if value not in normalized:
|
|
39
|
+
normalized.append(value)
|
|
40
|
+
return normalized
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _require_document(document: str | None) -> str:
|
|
44
|
+
digits = digits_only(document)
|
|
45
|
+
if len(digits) not in (11, 14):
|
|
46
|
+
raise ValueError(
|
|
47
|
+
'document deve ser CPF (11 dígitos) ou CNPJ (14 dígitos)'
|
|
48
|
+
)
|
|
49
|
+
return digits
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _coerce_request(
|
|
53
|
+
request: Juridico360Request | dict,
|
|
54
|
+
) -> Juridico360Request:
|
|
55
|
+
if isinstance(request, Juridico360Request):
|
|
56
|
+
return request
|
|
57
|
+
parsed = Juridico360Request.from_dict(request)
|
|
58
|
+
if parsed is None:
|
|
59
|
+
raise ValueError('request inválido')
|
|
60
|
+
return parsed
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class Juridico360Api(PositivaConsultasApi):
|
|
64
|
+
"""Consulta processual Positiva Juridico 360."""
|
|
65
|
+
|
|
66
|
+
def consultar(
|
|
67
|
+
self, request: Juridico360Request | dict
|
|
68
|
+
) -> Juridico360Response:
|
|
69
|
+
"""POST /api/client/provider/juridico360."""
|
|
70
|
+
logging.info('consulting Positiva Juridico 360...')
|
|
71
|
+
req = _coerce_request(request)
|
|
72
|
+
payload = {
|
|
73
|
+
'document': _require_document(req.document),
|
|
74
|
+
'product_code': _normalize_product_codes(req.product_code),
|
|
75
|
+
}
|
|
76
|
+
if req.options is not None:
|
|
77
|
+
options = req.options.to_dict() if isinstance(req.options, Juridico360Options) else dict(req.options)
|
|
78
|
+
if options:
|
|
79
|
+
if options.get('page_size') is not None and int(options['page_size']) > 100:
|
|
80
|
+
raise ValueError('options.page_size máximo é 100')
|
|
81
|
+
payload['options'] = options
|
|
82
|
+
|
|
83
|
+
url = UrlUtil().make_url(
|
|
84
|
+
self.base_url, ['client', 'provider', 'juridico360']
|
|
85
|
+
)
|
|
86
|
+
res = self.call_request(
|
|
87
|
+
http_method=HTTPMethod.POST,
|
|
88
|
+
request_url=url,
|
|
89
|
+
payload=payload,
|
|
90
|
+
)
|
|
91
|
+
parsed = Juridico360Response.from_dict(jsonpickle.decode(res))
|
|
92
|
+
if parsed is None:
|
|
93
|
+
raise Exception('Resposta inesperada de Juridico 360')
|
|
94
|
+
return parsed
|
|
95
|
+
|
|
96
|
+
def consultar_por_documento(
|
|
97
|
+
self,
|
|
98
|
+
document: str,
|
|
99
|
+
product_code: Sequence[str | Juridico360ProductCode],
|
|
100
|
+
*,
|
|
101
|
+
options: Juridico360Options | dict | None = None,
|
|
102
|
+
) -> Juridico360Response:
|
|
103
|
+
"""Atalho tipado para ``consultar``."""
|
|
104
|
+
opts = None
|
|
105
|
+
if isinstance(options, Juridico360Options):
|
|
106
|
+
opts = options
|
|
107
|
+
elif isinstance(options, dict):
|
|
108
|
+
opts = Juridico360Options.from_dict(options)
|
|
109
|
+
return self.consultar(
|
|
110
|
+
Juridico360Request(
|
|
111
|
+
document=document,
|
|
112
|
+
product_code=list(product_code),
|
|
113
|
+
options=opts,
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
def gerar_pdf(
|
|
118
|
+
self,
|
|
119
|
+
request: Juridico360Request | dict | None = None,
|
|
120
|
+
*,
|
|
121
|
+
document: str | None = None,
|
|
122
|
+
product_code: Sequence[str | Juridico360ProductCode] | None = None,
|
|
123
|
+
options: Juridico360Options | dict | None = None,
|
|
124
|
+
) -> Juridico360PdfResponse:
|
|
125
|
+
"""POST /api/client/provider/juridico360/pdf — retorna PDF em Base64."""
|
|
126
|
+
logging.info('generating Positiva Juridico 360 PDF...')
|
|
127
|
+
if request is not None:
|
|
128
|
+
req = _coerce_request(request)
|
|
129
|
+
else:
|
|
130
|
+
if document is None or product_code is None:
|
|
131
|
+
raise ValueError(
|
|
132
|
+
'informe request ou (document + product_code) para gerar PDF'
|
|
133
|
+
)
|
|
134
|
+
opts = None
|
|
135
|
+
if isinstance(options, Juridico360Options):
|
|
136
|
+
opts = options
|
|
137
|
+
elif isinstance(options, dict):
|
|
138
|
+
opts = Juridico360Options.from_dict(options)
|
|
139
|
+
req = Juridico360Request(
|
|
140
|
+
document=document,
|
|
141
|
+
product_code=list(product_code),
|
|
142
|
+
options=opts,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
payload = {
|
|
146
|
+
'document': _require_document(req.document),
|
|
147
|
+
'product_code': _normalize_product_codes(req.product_code),
|
|
148
|
+
}
|
|
149
|
+
if req.options is not None:
|
|
150
|
+
options_dict = (
|
|
151
|
+
req.options.to_dict()
|
|
152
|
+
if isinstance(req.options, Juridico360Options)
|
|
153
|
+
else dict(req.options)
|
|
154
|
+
)
|
|
155
|
+
if options_dict:
|
|
156
|
+
payload['options'] = options_dict
|
|
157
|
+
|
|
158
|
+
url = UrlUtil().make_url(
|
|
159
|
+
self.base_url, ['client', 'provider', 'juridico360', 'pdf']
|
|
160
|
+
)
|
|
161
|
+
res = self.call_request(
|
|
162
|
+
http_method=HTTPMethod.POST,
|
|
163
|
+
request_url=url,
|
|
164
|
+
payload=payload,
|
|
165
|
+
)
|
|
166
|
+
parsed = Juridico360PdfResponse.from_dict(jsonpickle.decode(res))
|
|
167
|
+
if parsed is None:
|
|
168
|
+
raise Exception('Resposta inesperada de Juridico 360 PDF')
|
|
169
|
+
return parsed
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from positiva.consultas.dtos.auth import (
|
|
3
|
+
AvailableProduct,
|
|
4
|
+
ClientInfo,
|
|
5
|
+
ClientMeResponse,
|
|
6
|
+
LogoutResponse,
|
|
7
|
+
ProductFeature,
|
|
8
|
+
ProviderProduct,
|
|
9
|
+
TokenResponse,
|
|
10
|
+
)
|
|
11
|
+
from positiva.consultas.dtos.enums import DocumentType, Juridico360ProductCode, MpKind
|
|
12
|
+
from positiva.consultas.dtos.juridico360 import (
|
|
13
|
+
Juridico360Options,
|
|
14
|
+
Juridico360PdfResponse,
|
|
15
|
+
Juridico360ProductData,
|
|
16
|
+
Juridico360ProductResult,
|
|
17
|
+
Juridico360Request,
|
|
18
|
+
Juridico360Response,
|
|
19
|
+
PenaPrevista,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
'AvailableProduct',
|
|
24
|
+
'ClientInfo',
|
|
25
|
+
'ClientMeResponse',
|
|
26
|
+
'DocumentType',
|
|
27
|
+
'Juridico360Options',
|
|
28
|
+
'Juridico360PdfResponse',
|
|
29
|
+
'Juridico360ProductCode',
|
|
30
|
+
'Juridico360ProductData',
|
|
31
|
+
'Juridico360ProductResult',
|
|
32
|
+
'Juridico360Request',
|
|
33
|
+
'Juridico360Response',
|
|
34
|
+
'LogoutResponse',
|
|
35
|
+
'MpKind',
|
|
36
|
+
'PenaPrevista',
|
|
37
|
+
'ProductFeature',
|
|
38
|
+
'ProviderProduct',
|
|
39
|
+
'TokenResponse',
|
|
40
|
+
]
|