Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copie para .env e preencha com seus valores
# Obtenha o token em: https://app.clicksign.com/configuracoes/integracoes

CLICKSIGN_API_KEY=seu-access-token-aqui
CLICKSIGN_ENVIRONMENT=sandbox # sandbox | production
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ htmlcov/
.claude/
.idea/
docs/CONSIDERACOES.md
scripts/sandbox/.env
scripts/sandbox/.last_run.json
2 changes: 2 additions & 0 deletions scripts/sandbox/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CLICKSIGN_API_KEY=seu-access-token-aqui
CLICKSIGN_ENVIRONMENT=sandbox
59 changes: 59 additions & 0 deletions scripts/sandbox/01_list_collections.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
"""Lista collections principais no sandbox (somente leitura)."""

from __future__ import annotations

import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "src"))

from _config import get_client # noqa: E402


def _print_block(title: str, items: list, limit: int = 5) -> None:
print(f"\n=== {title} ({len(items)} total, mostrando até {limit}) ===")
for item in items[:limit]:
label = getattr(item, "name", None) or getattr(item, "email", None) or item.id
extra = getattr(item, "status", None)
suffix = f" [{extra}]" if extra else ""
print(f" - {item.id} {label}{suffix}")
if len(items) > limit:
print(f" ... +{len(items) - limit} itens")


def main() -> None:
import os

from clicksign.configuration import _ENVIRONMENTS

client = get_client()
env = os.environ.get("CLICKSIGN_ENVIRONMENT", "sandbox")
print(f"Ambiente: {env} → {_ENVIRONMENTS.get(env, env)}")

envelopes = client.notarial.envelopes.list()
_print_block("Envelopes", envelopes)

try:
folders = client.folders.list()
_print_block("Folders", folders)
except Exception as exc:
print(f"\n=== Folders (erro: {exc}) ===")

try:
webhooks = client.webhooks.list()
_print_block("Webhooks", webhooks)
except Exception as exc:
print(f"\n=== Webhooks (erro: {exc}) ===")

try:
users = client.users.list()
_print_block("Users", users)
except Exception as exc:
print(f"\n=== Users (erro: {exc}) ===")

print("\nOK — consultas concluídas.")


if __name__ == "__main__":
main()
89 changes: 89 additions & 0 deletions scripts/sandbox/02_create_notarial_draft.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/env python3
"""
Cria envelope + documento + signatário + requisitos no sandbox (permanece em draft).

Não ativa nem notifica — use 03_consult_envelope.py depois.
"""

from __future__ import annotations

import sys
import uuid
from datetime import UTC, datetime
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "src"))

from _config import get_client, pdf_content_base64, save_last_run # noqa: E402


def main() -> None:
client = get_client()
stamp = datetime.now(UTC).strftime("%Y%m%d-%H%M%S")
suffix = uuid.uuid4().hex[:8]

print("1/4 Criando envelope...")
envelope = client.notarial.envelopes.create(
name=f"SDK sandbox {stamp}",
locale="pt-BR",
auto_close=True,
)
print(f" envelope.id = {envelope.id} status={envelope.status}")

print("2/4 Criando documento...")
doc = client.notarial.documents.create(
envelope.id,
filename=f"sandbox-{stamp}.pdf",
content_base64=pdf_content_base64(),
)
print(f" document.id = {doc.id} filename={doc.filename}")

print("3/4 Criando signatário...")
email = f"sandbox+{suffix}@mailinator.com"
signer = client.notarial.signers.create(
envelope.id,
name="Maria Sandbox Silva",
email=email,
has_documentation=False,
)
print(f" signer.id = {signer.id} email={email}")

print("4/4 Criando requisitos (bulk)...")
bulk = client.notarial.bulk_requirements.create(
envelope.id,
block=lambda ops: (
ops.add_agree(signer_id=signer.id, document_id=doc.id, role="sign"),
ops.add_provide_evidence(
signer_id=signer.id,
document_id=doc.id,
auth="email",
),
),
)
if bulk.success():
print(" bulk: sucesso")
else:
print(" bulk: falhas parciais")
for f in bulk.failures:
print(f" - slot {f.index} op={f.op} errors={f.errors}")
sys.exit(1)

# Reconsulta via API
envelope2 = client.notarial.envelopes.retrieve(envelope.id)
print(f"\nEnvelope após bulk: status={envelope2.status}")

save_last_run(
{
"envelope_id": envelope.id,
"document_id": doc.id,
"signer_id": signer.id,
}
)

print("\nPróximo passo:")
print(f" python scripts/sandbox/03_consult_envelope.py {envelope.id}")
print(" # ou sem argumento para usar .last_run.json")


if __name__ == "__main__":
main()
70 changes: 70 additions & 0 deletions scripts/sandbox/03_consult_envelope.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env python3
"""Consulta envelope e listagens aninhadas no sandbox."""

from __future__ import annotations

import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "src"))

from _config import get_client, load_last_run # noqa: E402


def main() -> None:
envelope_id = (sys.argv[1] if len(sys.argv) > 1 else "").strip()
if not envelope_id:
envelope_id = load_last_run().get("envelope_id", "")
if not envelope_id:
print("Uso: python 03_consult_envelope.py <envelope_id>", file=sys.stderr)
sys.exit(1)

client = get_client()
last = load_last_run()

with client.use():
print(f"=== Envelope {envelope_id} ===")
envelope = client.notarial.envelopes.retrieve(envelope_id)
print(
f" name={envelope.name!r} status={envelope.status!r} locale={envelope.locale!r}"
)

print("\n--- Documentos (envelope.list_documents) ---")
for doc in client.notarial.envelopes.list_documents(envelope_id):
print(f" {doc.id} filename={doc.filename!r} status={doc.status!r}")

print("\n--- Signatários (envelope.list_signers) ---")
for signer in client.notarial.envelopes.list_signers(envelope_id):
print(f" {signer.id} name={signer.name!r} email={signer.email!r}")

print("\n--- Requisitos (envelope.list_requirements) ---")
for req in client.notarial.envelopes.list_requirements(envelope_id):
print(
f" {req.id} action={req.action!r} role={getattr(req, 'role', None)!r}"
)

print("\n--- Document.list_for_envelope (atalho) ---")
for doc in client.notarial.documents.list_for_envelope(envelope_id):
print(f" Document.list_for_envelope → {doc.id}")

print("\n--- Eventos do envelope ---")
events = client.notarial.envelopes.list_events(envelope_id)
print(f" {len(events)} evento(s)")
for ev in events[:5]:
print(f" {ev.id} name={getattr(ev, 'name', None)!r}")

doc_id = last.get("document_id")
if not doc_id:
docs = client.notarial.envelopes.list_documents(envelope_id)
doc_id = docs[0].id if docs else ""
if doc_id:
doc_events = client.notarial.documents.list_events(
doc_id, envelope_id=envelope_id
)
print(f" eventos do documento {doc_id}: {len(doc_events)}")

print("\nOK.")


if __name__ == "__main__":
main()
79 changes: 79 additions & 0 deletions scripts/sandbox/04_activate_optional.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/env python3
"""
Opcional: ativa envelope (status running) e envia notificação.

Uso: python 04_activate_optional.py [envelope_id]
Requer IDs em .last_run.json (rode 02_create_notarial_draft.py antes).

Nota: alguns tokens sandbox retornam 403 em POST /activate; neste caso
usamos PATCH status=running (documentado em docs/WORKFLOW.md).
"""

from __future__ import annotations

import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "src"))

from _config import get_client, load_last_run # noqa: E402


def _activate_envelope(client, envelope_id: str):
"""Ativa o envelope; tenta POST /activate e faz fallback para PATCH."""
from clicksign.errors import AuthenticationError

envelope = client.notarial.envelopes.retrieve(envelope_id)
if envelope.status == "running":
print(" envelope já está em running")
return envelope

try:
return client.notarial.envelopes.activate(envelope_id)
except AuthenticationError as exc:
if exc.status_code != 403:
raise
print(" POST /activate retornou 403 (token sem permissão) — usando update(status='running')")
envelope.update(status="running")
return envelope


def main() -> None:
last = load_last_run()
envelope_id = (sys.argv[1] if len(sys.argv) > 1 else last.get("envelope_id", "")).strip()
signer_id = last.get("signer_id", "")
if not envelope_id:
print("Precisa de envelope_id (.last_run.json ou argumento)", file=sys.stderr)
sys.exit(1)

client = get_client()

with client.use():
print(f"Ativando envelope {envelope_id}...")
envelope = _activate_envelope(client, envelope_id)
print(f" status={envelope.status}")

print("Notificando signatários (envelope.notify)...")
envelope.notify(
message="Teste SDK Python — documento disponível para assinatura (sandbox).",
)
print(" envelope.notify: OK")

if signer_id:
print(f"Notificando signatário {signer_id} (Signer.notify, sem subject)...")
try:
client.notarial.signers.notify(
envelope_id,
signer_id,
message="Lembrete: seu documento aguarda assinatura (sandbox).",
)
print(" Signer.notify: OK")
except Exception as exc:
# 429 ou restrições de conta — envelope.notify já disparou
print(f" Signer.notify ignorado: {type(exc).__name__}: {exc}")

print("\nOK — envelope ativo e notificação enviada.")


if __name__ == "__main__":
main()
35 changes: 35 additions & 0 deletions scripts/sandbox/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Scripts sandbox (local)

Testes manuais contra **sandbox** com o SDK do repositório. **Não commitar** `.env` nem `.last_run.json`.

## Setup

```bash
cd /caminho/clicksign-python-sdk
cp scripts/sandbox/.env.example scripts/sandbox/.env
# edite .env se necessário (já existe .env local com token)
```

## Rodar

```bash
# da raiz do repo
export PYTHONPATH=src

python scripts/sandbox/01_list_collections.py
python scripts/sandbox/02_create_notarial_draft.py
python scripts/sandbox/03_consult_envelope.py
# opcional — ativa e notifica (cuidado: e-mail real do signatário no passo 2)
python scripts/sandbox/04_activate_optional.py
```

## O que cada script faz

| Script | Ação |
|--------|------|
| `01_list_collections.py` | Lista envelopes, folders, webhooks, users |
| `02_create_notarial_draft.py` | Cria envelope + PDF mínimo + signatário + bulk (draft) |
| `03_consult_envelope.py` | Retrieve + listagens aninhadas + eventos |
| `04_activate_optional.py` | Ativa (`update` se `/activate` der 403) + `envelope.notify` |

IDs do último `02_*` ficam em `scripts/sandbox/.last_run.json`.
Loading
Loading