PC SOFT
DEPOT EN LIGNE
POUR  WINDEVWEBDEV ET  WINDEV MOBILE

Api Rest OOP com Crud automático a partir da tabela e com swagger
Publié par Boller
dans la catégorie Outils
Nouveautés



Description


API Server v6.4 – Documentação de Métodos (Endpoints)

Visão geral
-----------
A versão 6.4 do servidor expõe endpoints REST organizados em 4 grupos:
1) Administração / OpenAPI (importação e metadados)
2) Swagger & utilitários (docs, versão, teste)
3) CRUD genérico por recurso (tabelas autorizadas)
4) Arquivos (BLOB em HFSQL)

Autenticação
------------
- Autenticação normal (obrigatória para quase todos os endpoints):
- API Key: header `X-API-Key: <sua-chave>`
- ou Bearer/JWT: header `Authorization: Bearer <token>`
- Chave Admin (apenas para endpoints de administração):
- Header adicional `X-API-Key: <SUA_CHAVE_ADMIN_FORTE>` configurada por `Enable_Admin_OpenAPI()`
- Autorização por tabela/método (v6.1):
- Definida em `api_allowed_tables.json`, controla quais métodos cada recurso pode usar.

Padrões de cabeçalhos
---------------------
- `Content-Type: application/json` para POST/PATCH com JSON.
- `Accept: application/json` recomendado para respostas JSON.
- Para upload de arquivo BLOB, enviar JSON Base64 (ver seção Arquivos).

Códigos de erro (geral)
-----------------------
- 200 OK
- 201 Created (POST bem sucedido)
- 204 No Content (DELETE sem corpo)
- 400 Bad Request (parâmetros/JSON inválidos)
- 401 Unauthorized (sem autenticação)
- 403 Forbidden (método não autorizado, escopo insuficiente ou admin key inválida)
- 404 Not Found (recurso/rota/arquivo não encontrado)
- 409 Conflict (violação de PK/índice; conforme cenário)
- 500 Import failed / erro interno

====================================================================
1) Endpoints de Administração / OpenAPI (v6.4)
====================================================================

POST /api/v6/admin/openapi/import
---------------------------------
Importa um documento OpenAPI/Swagger e gera automaticamente:
- tabelas HFSQL (com base em components.schemas)
- autorização `api_allowed_tables.json`
- metadados `openapi_client_meta.json` (enums, polimorfismo, descrições)

Segurança:
- Requer autenticação normal (API Key ou Bearer)
- Requer header extra: `X-API-Key: <SUA_CHAVE_ADMIN_FORTE>`

Body (3 formatos aceitos):
A) JSON com URL remota:
{
"openapi_url": "https://seu.dominio/openapi.json"
}

B) JSON com o documento diretamente:
{
"openapi_json": "{ ... JSON OpenAPI ... }"
}

C) JSON bruto do OpenAPI no body (sem wrapper).

Resposta de sucesso (200):
{ "ok": true, "message": "OpenAPI importado com sucesso (v6.4). Recursos recarregados." }

Erros comuns:
- 403 Forbidden: admin import desligado ou X-API-Key inválida
- 400 Bad Request: URL inválida / JSON ausente / documento sem components.schemas
- 500 Import failed: erro no parser/geração (verificar logs)

GET /api/v6/admin/openapi/meta
------------------------------
Retorna o conteúdo de `openapi_client_meta.json`, gerado após a importação.
Inclui informações de enum, polimorfismo (oneOf/anyOf/discriminator) e descrição de campos.

Segurança:
- Requer autenticação normal (API Key ou Bearer)
- Chave Admin não é obrigatória (configurável)

Resposta (200, application/json):
{
"resources": [
{
"name": "pedidos",
"pk": "id",
"poly": "discriminator:tipo|...",
"fields": [
{"name":"status","type":"string","enum":"NOVO,PAGO,CANCELADO","descr":"..."}
]
}
],
"debug": ["Swagger 2.0 detectado – conversão básica aplicada.", "..."]
}

====================================================================
2) Swagger & Utilitários
====================================================================

GET /api/v6/docs
----------------
Exibe a UI do Swagger (documentação interativa).
- Pode exigir autenticação/assinar o token conforme sua configuração.

GET /api/v6/swagger.json
------------------------
Retorna o documento OpenAPI que representa os endpoints publicados (auto-gerado).

GET /api/v6/version
-------------------
Retorna informações do servidor (versão, hora de inicialização, recursos publicados).
Exemplo de resposta:
{
"version": "6.4",
"serverUp": "2025-08-15T10:10:10Z",
"tables": ["pedidos", "pedidos_itens", ...]
}

GET /api/v6/test
----------------
Endpoint de saúde (health check). Útil para monitoramento/self-test.

====================================================================
3) CRUD Genérico (recursos/tabelas autorizadas)
====================================================================

Observações importantes:
- O nome do recurso normalmente corresponde ao nome da tabela (em minúsculas).
- A PK padrão é "id" (autoID). Pode ser detectada automaticamente pela análise quando diferente.
- Autorização por método é aplicada: GET/POST/PATCH/DELETE/HEAD/COUNT/EXPORT conforme `api_allowed_tables.json`.
- Filtros, ordenação e paginação podem ser passados via query string (ver abaixo).

Listar (coleção)
GET /api/v6/{recurso}
---------------------
Query params suportados (implementação típica):
- select=campo1,campo2,... -> projeção
- where=expressao -> filtro simples (ex: where=status='PAGO')
- order=campo1,-campo2 -> ordenação (desc com prefixo "-")
- limit=50 -> máximo de registros
- offset=0 -> deslocamento para paginação
- q=texto -> busca rápida (quando disponível)

Resposta (200): JSON array com os registros selecionados.

Obter por ID
GET /api/v6/{recurso}/{id}
--------------------------
Resposta (200): JSON do registro.
Erros: 404 se não encontrado.

Cabeçalhos
HEAD /api/v6/{recurso}/{id}
---------------------------
Retorna apenas cabeçalhos (útil para verificar existência/etag).

Criar
POST /api/v6/{recurso}
----------------------
Body (application/json): objeto com os campos do registro.
Resposta (201 ou 200): objeto criado (pode retornar o ID gerado) ou JSON com { "id": <novo_id> }.

Atualizar (parcial)
PATCH /api/v6/{recurso}/{id}
----------------------------
Body (application/json): somente campos a atualizar.
Resposta (200): objeto atualizado ou { "ok": true }.

Excluir
DELETE /api/v6/{recurso}/{id}
-----------------------------
Resposta (204): sem corpo (ou { "ok": true }).

Contagem
GET /api/v6/{recurso}/count
---------------------------
Retorna a contagem total (considerando filtros aplicáveis via query string).
Resposta (200): { "count": 123 }

Exportação CSV
GET /api/v6/{recurso}/export.csv
--------------------------------
Exporta o resultado (com filtros) em CSV.
Headers: `Content-Type: text/csv`

Exemplo (Pedidos)
-----------------
1) Criar pedido
POST /api/v6/pedidos
{
"cliente_id": 1,
"data": "2025-08-15T12:00:00",
"status": "NOVO",
"valor_total": 0
}

2) Inserir item
POST /api/v6/pedidos_itens
{
"pedido_id": 10,
"produto_id": 2001,
"quantidade": 2,
"valor_unit": 50.0
}

3) Recalcular total (quando houver hooks/lambdas configurados)
PATCH /api/v6/pedidos/10
{ "status": "PAGO" }

====================================================================
4) Arquivos (BLOB em HFSQL)
====================================================================

Upload de Arquivo (BLOB)
POST /api/v6/files
------------------
Body (application/json):
{
"table": "pedidos",
"record_id": 10,
"field": "contrato_blob",
"filename": "contrato.pdf",
"mimetype": "application/pdf",
"content_base64": "<BASE64_DO_ARQUIVO>"
}

Resposta (200): { "ok": true, "file_id": "uuid-ou-interno" }

Download de Arquivo
GET /api/v6/files/{file_id}
---------------------------
Resposta (200): corpo binário ou JSON com metadados + stream
Headers: `Content-Type` conforme `mimetype` armazenado.

Excluir Arquivo
DELETE /api/v6/files/{file_id}
------------------------------
Resposta (204): sem corpo (ou { "ok": true }).

====================================================================
Autorização por Tabela/Método (api_allowed_tables.json)
====================================================================

Formato v6.1 (recomendado)
--------------------------
{
"wildcard": false,
"default": { "methods": ["GET"], "readScope": "*:read" },
"tables": {
"pedidos": { "methods": ["GET","POST"], "readScope":"pedidos:read", "writeScope":"pedidos:write" },
"pedidos_itens": { "methods": ["GET","POST","PATCH","DELETE"] }
}
}

Regras:
- Se "wildcard": true ? todas as tabelas da análise são expostas (cuidado em produção).
- "default.methods" ? métodos liberados quando a tabela não possui regra específica.
- Métodos válidos: GET, POST, PUT, PATCH, DELETE, HEAD, COUNT, EXPORT.

====================================================================
Notas Avançadas (v6.4)
====================================================================
- Importador universal:
- Aceita Swagger 2.0 e OpenAPI 3.0/3.1.
- Converte 2.0 ? 3.x básico (definitions ? components.schemas).
- Resolve $ref com cache e detecção de ciclos.
- Suporta allOf, anyOf, oneOf e discriminator (polimorfismo);
nos casos ambíguos, registra "poly" nos metadados para a UI.
- Captura enums e adiciona em metadados (útil para combos).
- Tipagem HFSQL:
- string/date/date-time/binary, integer/number ? mapeados para tipos nativos.
- arrays/objects: por padrão serializados como TEXT (JSON) na v6.4 (pode customizar).
- Segurança:
- Use HTTPS em produção.
- Restrinja /admin por IP/Firewall + X-API-Key Admin.
- Evite wildcard em produção; defina explicitamente as tabelas e métodos.
- Performance:
- Ajuste índices e FKs conforme necessidade real.
- Use paginação (`limit`/`offset`) em listas grandes.


//———


API Client v6.4 – Documentação de Métodos
=========================================

Visão geral
-----------
A classe **clsApiClientV64** é um *wrapper* moderno para consumir a API Server v6.4.
Ela incorpora o cliente v6.3 e v6.2 internamente para:
- Importar OpenAPI por **URL** ou **JSON bruto** (método Admin)
- Autenticar por **API Key** ou **Bearer/JWT**
- Consumir o **CRUD genérico** com *chain methods* (via `clsApiClientV60`)
- Abrir **Swagger UI** e executar **self-test**
- Suportar **upload/download** de arquivos (BLOB)
- Usar metadados gerados pelo OpenAPI (`openapi_client_meta.json`)

Estrutura
---------
- `clsApiClientV64` ? camada mais nova (helper AdminImportOpenAPI URL/JSON)
- `clsApiClientV63` ? inclui os métodos Admin da v6.3 (import por URL)
- `clsApiClientV62` ? inclui AdminImportOpenAPIFromJSON + GetOpenAPIMeta
- `clsApiClientV60` ? base CRUD/Chain/Files/SelfTest/Swagger

Requisitos
----------
- BaseURL do servidor (`http://host:porta`)
- Autenticação (API Key ou Bearer/JWT) configurada
- Para endpoints admin: `X-API-Key` de **admin** definida no servidor com `Enable_Admin_OpenAPI()`

--------------------------------------------------------------------------------
clsApiClientV64 – Métodos
--------------------------------------------------------------------------------

Constructor(base: string="")
---------------------------
**O que faz:** Instancia o cliente com a URL base opcional.
**Ex.:**
```wl
c is clsApiClientV64("http://localhost:8080")
```

SetBaseURL(u: string)
---------------------
**O que faz:** Define a URL base do servidor.
**Ex.:**
```wl
c.SetBaseURL("http://api.meusistema:8080")
```

SetAuthApiKey(k: string, header: string="X-API-Key")
----------------------------------------------------
**O que faz:** Configura autenticação por **API Key**.
- Parâmetro `header` permite customizar o nome do cabeçalho (default: `X-API-Key`).
**Ex.:**
```wl
c.SetAuthApiKey("MINHA_CHAVE")
// ou se seu servidor usar um header diferente:
c.SetAuthApiKey("MINHA_CHAVE", "x-api-token")
```

SetAuthBearer(tok: string)
--------------------------
**O que faz:** Configura autenticação por **Bearer/JWT**.
- Enviado como `Authorization: Bearer <tok>`.
**Ex.:**
```wl
c.SetAuthBearer("eyJhbGciOi...")
```

SetAdminKey(k: string)
----------------------
**O que faz:** Define a **chave Admin** usada nos endpoints de administração (`/api/v6/admin/...`).
**Ex.:**
```wl
c.SetAdminKey("SUA_CHAVE_ADMIN_FORTE")
```

AdminImportOpenAPI(urlOrJSON: string) : stClientV64Response
-----------------------------------------------------------
**O que faz:** Importa um OpenAPI/Swagger no servidor v6.4 e aciona geração de tabelas, permissões e metadados.
- Se o parâmetro começar com `http://` ou `https://`, tratará como **URL remota**.
- Caso contrário, será considerado **JSON bruto**.
**Retorno:** registro com `status` (HTTP) e `body` (texto da resposta).
**Ex.:**
```wl
// Por URL remota
r is stClientV64Response = c.AdminImportOpenAPI("https://seu.dominio/openapi.json")
Info(r.status + " -> " + r.body)

// Por JSON bruto (carregado de arquivo)
r = c.AdminImportOpenAPI(fLoadText("openapi.json"))
```

Table(res: string) : clsApiQueryV60
-----------------------------------
**O que faz:** Inicia um *query builder* para o recurso/tabela informado.
**Retorna:** objeto `clsApiQueryV60` com métodos de *chain* (Select/Where/Order/Limit/…).
**Ex.:**
```wl
q is clsApiQueryV60 = c.Table("pedidos")
```

OpenSwagger()
-------------
**O que faz:** Abre a UI do Swagger no navegador padrão (URL `/api/v6/docs`).
**Ex.:**
```wl
c.OpenSwagger()
```

SelfTest() : string
-------------------
**O que faz:** Executa um teste rápido de conectividade/saúde (health check) e retorna a resposta textual.
**Ex.:**
```wl
Trace(c.SelfTest())
```

--------------------------------------------------------------------------------
clsApiQueryV60 – Métodos de Chain/CRUD (retornado por Table())
--------------------------------------------------------------------------------
> Observação: estes métodos fazem a chamada HTTP quando você finaliza com `List()`, `Get()`, `Insert...`, `Update...`, `Delete()`, etc.

Select(cols: string) : clsApiQueryV60
-------------------------------------
**O que faz:** Define projeção (colunas a retornar).
**Ex.:**
```wl
c.Table("pedidos").Select("id,data,status,valor_total").List()
```

Where(expr: string) : clsApiQueryV60
------------------------------------
**O que faz:** Define filtro (expressão suportada pelo servidor).
**Ex.:**
```wl
c.Table("pedidos").Where("status='PAGO' AND data>='2025-01-01'").List()
```

Order(expr: string) : clsApiQueryV60
------------------------------------
**O que faz:** Define ordenação (ex.: `campo,-campo2` para DESC).
**Ex.:**
```wl
c.Table("pedidos").Order("data,-id").Limit(50).List()
```

Limit(n: int) : clsApiQueryV60
------------------------------
**O que faz:** Define limite de registros (paginações).
**Ex.:**
```wl
c.Table("pedidos").Limit(20).List()
```

Offset(n: int) : clsApiQueryV60
-------------------------------
**O que faz:** Define deslocamento para paginação.
**Ex.:**
```wl
c.Table("pedidos").Limit(20).Offset(20).List()
```

List() : stClientResponseV60
----------------------------
**O que faz:** Executa a consulta e retorna um array JSON de registros.
**Ex.:**
```wl
r is stClientResponseV60 = c.Table("pedidos").Where("status='NOVO'").List()
IF r.status=200 THEN Trace(r.body)
```

Get(id: string|int) : stClientResponseV60
-----------------------------------------
**O que faz:** Obtém um registro por **ID**.
**Ex.:**
```wl
r is stClientResponseV60 = c.Table("pedidos").Get(10)
```

InsertVariant(v: Variant) : stClientResponseV60
-----------------------------------------------
**O que faz:** Cria um registro usando um **Variant** (campos dinâmicos).
**Ex.:**
```wl
p is Variant
p.cliente_id = 1
p.data = DateToString(Today(), "YYYY-MM-DD") + "T12:00:00"
p.status = "NOVO"
p.valor_total = 0
r is stClientResponseV60 = c.Table("pedidos").InsertVariant(p)
```

Insert(jsonBody: string) : stClientResponseV60
----------------------------------------------
**O que faz:** Cria um registro enviando o **JSON** manualmente.
**Ex.:**
```wl
r is stClientResponseV60 = c.Table("pedidos").Insert('{"cliente_id":1,"status":"NOVO","data":"2025-08-15T12:00:00"}')
```

UpdateVariant(id: any, v: Variant) : stClientResponseV60
--------------------------------------------------------
**O que faz:** Atualiza parcialmente um registro por **ID** via `PATCH`.
**Ex.:**
```wl
u is Variant
u.status = "PAGO"
r is stClientResponseV60 = c.Table("pedidos").UpdateVariant(10, u)
```

Update(id: any, jsonBody: string) : stClientResponseV60
-------------------------------------------------------
**O que faz:** Atualiza parcialmente um registro por **ID** enviando o **JSON** manualmente.
**Ex.:**
```wl
r is stClientResponseV60 = c.Table("pedidos").Update(10, '{"status":"PAGO"}')
```

Delete(id: any) : stClientResponseV60
-------------------------------------
**O que faz:** Exclui um registro por **ID**.
**Ex.:**
```wl
r is stClientResponseV60 = c.Table("pedidos").Delete(10)
```

Count() : stClientResponseV60
-----------------------------
**O que faz:** Retorna a contagem de registros considerando filtros.
**Ex.:**
```wl
r is stClientResponseV60 = c.Table("pedidos").Where("status='NOVO'").Count()
```

ExportCSV() : stClientResponseV60
---------------------------------
**O que faz:** Exporta a consulta atual como CSV.
**Ex.:**
```wl
r is stClientResponseV60 = c.Table("pedidos").Where("status='PAGO'").ExportCSV()
IF r.status=200 THEN fSaveText("pedidos_pago.csv", r.body)
```

--------------------------------------------------------------------------------
Arquivos (BLOB) – via clsApiClientV60
--------------------------------------------------------------------------------

UploadFile(localPath: string, table: string, recordID: any, field: string) : stClientResponseV60
------------------------------------------------------------------------------------------------
**O que faz:** Faz upload de arquivo (convertendo para Base64) e armazena no campo **BLOB**.
**Ex.:**
```wl
r is stClientResponseV60 = c.V63.V62.V60.UploadFile("C:\docs\contrato.pdf", "pedidos", 10, "contrato_blob")
```

DownloadFile(table: string, recordID: any, field: string, saveAsPath: string) : stClientResponseV60
---------------------------------------------------------------------------------------------------
**O que faz:** Faz download do arquivo BLOB do registro/campo e salva em disco.
**Ex.:**
```wl
r is stClientResponseV60 = c.V63.V62.V60.DownloadFile("pedidos", 10, "contrato_blob", "C:\tmp\contrato.pdf")
```

--------------------------------------------------------------------------------
Dicas e Boas Práticas
--------------------------------------------------------------------------------
- Sempre defina **BaseURL** e método de **autenticação** antes de chamar os endpoints.
- Para endpoints **Admin**, lembre-se de setar **SetAdminKey()**.
- Prefira `InsertVariant/UpdateVariant` para montar objetos de forma tipada no WL.
- Use `openapi_client_meta.json` para auto-montar **combos de enum** e para orientar a UI em casos com **oneOf/anyOf**.
- Trate respostas: verifique `status` (200, 201, 204, 4xx, 5xx) e exiba `body` em caso de erro.
- Faça paginação (`Limit/Offset`) para listas grandes e use `Select` para reduzir payload.

--------------------------------------------------------------------------------
Exemplos Rápidos
--------------------------------------------------------------------------------

1) Listar pedidos pagos (top 20)
```wl
c is clsApiClientV64("http://localhost:8080")
c.SetAuthApiKey("SUA_CHAVE")
r is stClientResponseV60 = c.Table("pedidos")
.Select("id,data,status,valor_total")
.Where("status='PAGO'")
.Order("data,-id")
.Limit(20)
.List()
Trace(r.status + " -> " + r.body)
```

2) Criar pedido e itens
```wl
c is clsApiClientV64("http://localhost:8080")
c.SetAuthApiKey("SUA_CHAVE")

p is Variant
p.cliente_id = 1
p.data = DateToString(Today(), "YYYY-MM-DD") + "T12:00:00"
p.status = "NOVO"
p.valor_total = 0
rp is stClientResponseV60 = c.Table("pedidos").InsertVariant(p)

i1 is Variant
i1.pedido_id = JSONToVariant(rp.body)["id"]
i1.produto_id = 101
i1.quantidade = 2
i1.valor_unit = 50
ri1 is stClientResponseV60 = c.Table("pedidos_itens").InsertVariant(i1)
```

3) Importar OpenAPI via URL e abrir Swagger
```wl
c is clsApiClientV64("http://localhost:8080")
c.SetAuthApiKey("SUA_CHAVE_NORMAL")
c.SetAdminKey("SUA_CHAVE_ADMIN_FORTE")
resp is stClientV64Response = c.AdminImportOpenAPI("https://seu.dominio/openapi.json")
IF resp.status=200 THEN c.OpenSwagger()
```

4) Upload/Download de arquivo BLOB
```wl
c is clsApiClientV64("http://localhost:8080")
c.SetAuthApiKey("SUA_CHAVE")

// Upload
up is stClientResponseV60 = c.V63.V62.V60.UploadFile("C:\docs\contrato.pdf","pedidos",10,"contrato_blob")

// Download
dw is stClientResponseV60 = c.V63.V62.V60.DownloadFile("pedidos",10,"contrato_blob","C:\tmp\contrato.pdf")
```

Fim.


//———



API v6.4 – Guia Prático com Exemplos
====================================

Escopo
------
Exemplos completos (server + client + HTTP) usando as tabelas **pedidos** e **pedidos_itens**:
- Criar pedido e itens
- Consultar pedido e itens
- Alterar quantidade de item e recalcular total
- Excluir item
- Listar, contar e exportar
- Upload/Download de arquivo (BLOB) vinculado ao pedido
- Boas práticas e erros comuns

Pré-requisitos
--------------
1) Servidor iniciado com a v6.4:
```wl
Init_Server_v64()
Enable_Admin_OpenAPI("SUA_CHAVE_ADMIN_FORTE")
```
2) OpenAPI importado (pelo menos o `openapi_sample_pedidos.json` do pacote) para gerar `pedidos` e `pedidos_itens`.
3) `api_allowed_tables.json` autoriza os métodos usados nos exemplos.

Estrutura (modelo sugerido)
---------------------------
- **pedidos**: id (PK), cliente_id, data (datetime), status (enum: NOVO,PAGO,CANCELADO), valor_total (number).
- **pedidos_itens**: id (PK), pedido_id (FK), produto_id, quantidade, valor_unit (number).

============================================================
A) Exemplos com o Client (WLanguage) – clsApiClientV64
============================================================

1) Criar um pedido e adicionar itens
------------------------------------
```wl
c is clsApiClientV64("http://localhost:8080")
c.SetAuthApiKey("SUA_CHAVE")

// 1) Pedido
pedido is Variant
pedido.cliente_id = 1
pedido.data = DateToString(Today(), "YYYY-MM-DD") + "T12:00:00"
pedido.status = "NOVO"
pedido.valor_total = 0

rp is stClientResponseV60 = c.Table("pedidos").InsertVariant(pedido)
// Captura o ID criado (o server pode retornar {"id":...} ou o objeto completo)
idPedido is int
IF rp.status=200 OR rp.status=201 THEN
v is variant
IF JSONToVariant(v, rp.body) THEN
IF VariantExists(v,"id") THEN idPedido = v["id"]
END
END
Trace("Pedido criado: " + idPedido)

// 2) Itens
i1 is Variant
i1.pedido_id = idPedido
i1.produto_id = 101
i1.quantidade = 2
i1.valor_unit = 50.00
c.Table("pedidos_itens").InsertVariant(i1)

i2 is Variant
i2.pedido_id = idPedido
i2.produto_id = 102
i2.quantidade = 1
i2.valor_unit = 75.50
c.Table("pedidos_itens").InsertVariant(i2)
```

2) Buscar o pedido e seus itens
-------------------------------
```wl
// Pedido
rPed is stClientResponseV60 = c.Table("pedidos").Get(idPedido)
Trace("Pedido: " + rPed.body)

// Itens
rItens is stClientResponseV60 = c.Table("pedidos_itens").Where("pedido_id="+idPedido).List()
Trace("Itens: " + rItens.body)
```

3) Alterar quantidade de um item e recalcular total
---------------------------------------------------
```wl
// Pegue um item do pedido
arr is variant
JSONToVariant(arr, rItens.body)
idItem is int = arr[1]["id"] // exemplo: 1º item

upd is Variant
upd.quantidade = 3
rUp is stClientResponseV60 = c.Table("pedidos_itens").UpdateVariant(idItem, upd)
Trace("Update item: " + rUp.status + " -> " + rUp.body)

// (Opcional) Recalcular total forçando um PATCH no pedido ou confie no hook do server
recalc is Variant
recalc.status = "NOVO" // apenas para disparar hook, se aplicável
c.Table("pedidos").UpdateVariant(idPedido, recalc)

// Leia o pedido novamente
rPed2 is stClientResponseV60 = c.Table("pedidos").Get(idPedido)
Trace("Pedido após ajuste: " + rPed2.body)
```

4) Excluir um item do pedido
----------------------------
```wl
rDel is stClientResponseV60 = c.Table("pedidos_itens").Delete(idItem)
Trace("Delete item: " + rDel.status)
```

5) Paginação, contagem e exportação
-----------------------------------
```wl
// Lista paginada
rList is stClientResponseV60 = c.Table("pedidos")
.Select("id,data,status,valor_total")
.Where("status='NOVO'")
.Order("data,-id")
.Limit(20).Offset(0)
.List()

// Contagem
rCnt is stClientResponseV60 = c.Table("pedidos").Where("status='NOVO'").Count()

// Export CSV (salva em arquivo)
rCsv is stClientResponseV60 = c.Table("pedidos").Where("status='NOVO'").ExportCSV()
IF rCsv.status=200 THEN fSaveText(fExeDir()+"pedidos_novo.csv", rCsv.body)
```

6) Upload & Download de arquivo (BLOB) no pedido
------------------------------------------------
```wl
// Upload (campo BLOB "contrato_blob" em pedidos)
up is stClientResponseV60 = c.V63.V62.V60.UploadFile("C:\docs\contrato.pdf","pedidos",idPedido,"contrato_blob")

// Download
dw is stClientResponseV60 = c.V63.V62.V60.DownloadFile("pedidos", idPedido, "contrato_blob", fExeDir()+"contrato_"+idPedido+".pdf")
```

============================================================
B) Exemplos HTTP (cURL)
============================================================

1) Criar pedido
---------------
```bash
curl -X POST "http://localhost:8080/api/v6/pedidos" \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{"cliente_id":1,"data":"2025-08-15T12:00:00","status":"NOVO","valor_total":0}'
```

2) Inserir itens
----------------
```bash
curl -X POST "http://localhost:8080/api/v6/pedidos_itens" \
-H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
-d '{"pedido_id":<ID_PEDIDO>,"produto_id":101,"quantidade":2,"valor_unit":50.0}'

curl -X POST "http://localhost:8080/api/v6/pedidos_itens" \
-H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
-d '{"pedido_id":<ID_PEDIDO>,"produto_id":102,"quantidade":1,"valor_unit":75.5}'
```

3) Consultar pedido e itens
---------------------------
```bash
curl -H "Authorization: Bearer <TOKEN>" "http://localhost:8080/api/v6/pedidos/<ID_PEDIDO>"
curl -H "Authorization: Bearer <TOKEN>" "http://localhost:8080/api/v6/pedidos_itens?where=pedido_id=<ID_PEDIDO>"
```

4) Alterar quantidade de um item
--------------------------------
```bash
curl -X PATCH "http://localhost:8080/api/v6/pedidos_itens/<ID_ITEM>" \
-H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
-d '{"quantidade":3}'
```

5) Excluir item
---------------
```bash
curl -X DELETE "http://localhost:8080/api/v6/pedidos_itens/<ID_ITEM>" \
-H "Authorization: Bearer <TOKEN)"
```

6) Contar e exportar pedidos
----------------------------
```bash
curl -H "Authorization: Bearer <TOKEN>" "http://localhost:8080/api/v6/pedidos/count?where=status='NOVO'"
curl -H "Authorization: Bearer <TOKEN>" "http://localhost:8080/api/v6/pedidos/export.csv?where=status='NOVO'"
```

7) Upload/Download de arquivo (BLOB)
------------------------------------
**Upload** (JSON com Base64):
```bash
curl -X POST "http://localhost:8080/api/v6/files" \
-H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
-d '{"table":"pedidos","record_id":<ID_PEDIDO>,"field":"contrato_blob","filename":"contrato.pdf","mimetype":"application/pdf","content_base64":"<BASE64>"}'
```
**Download** (usando `file_id` ou endpoint do servidor configurado):
```bash
curl -L -H "Authorization: Bearer <TOKEN>" "http://localhost:8080/api/v6/files/<FILE_ID>" -o contrato_baixado.pdf
```

============================================================
C) Boas Práticas e Erros Comuns
============================================================
- **Autorização por método/tabela**: confirme no `api_allowed_tables.json` que `pedidos` e `pedidos_itens` têm os métodos que você precisa (GET/POST/PATCH/DELETE/COUNT/EXPORT).
- **Recálculo de total**: configure **hooks/lambdas** no servidor para recalcular `valor_total` do pedido sempre que um item for incluído/alterado/excluído.
- **Enum de status**: use os metadados (`openapi_client_meta.json`) para montar combos e validar valores permitidos.
- **Data/hora**: envie `date-time` no padrão ISO (`YYYY-MM-DDTHH:mm:ss`).
- **Paginação**: use `.Limit/.Offset` para coleções grandes e sempre `Select()` para reduzir payload.
- **BLOB**: valide tamanho/mimetype do arquivo do lado do servidor; armazene em **BLOB HFSQL** conforme implementado.
- **Erros**: trate HTTP 400/401/403/404/409/500 e mostre o `body` da resposta para diagnóstico.


Avis des utilisateurs
(Pour noter la ressource, cliquez sur Ecrire un avis)
Vous devez d'abord
pour pouvoir poster un avis
Boller
show! completei o pacote com testes, README, checklist de segurança e validador rápido — tudo integrado ao rebuild com código real.

Downloads
• ?? Pacote com código + testes
API_v6_5_Rebuild_With_Code_and_Tests.zip
• ??? Server (arquivos soltos, caso queira abrir direto):
clsApiServerV65.txt •
clsOpenAPI_Importer_V65.txt •
clsApiConfigV65.txt •
clsApiLoggerV65.txt •
clsRateLimiterV65.txt •
clsInputValidatorV65.txt •
clsMetricsV65.txt
• ?? Client:
clsApiClientV65.txt •
Client_v65_QuickStart.txt •
Examples_ERP_Pedidos.wl
• ?? Testes:
Tests_v65_Smoke.wl •
Tests_v65_Security.wl •
Tests_v65_BLOB.wl •
Tests_v65_Fuzzer.wl •
Postman_Collection_v65.json •
tests_env.json
• ?? Docs:
Manual_v65_resumo.txt •
Checklist_Seguranca_v65.txt •
Quick_Validator.txt

como validar agora (rapidinho)
1. Importe /server/ no projeto e chame:

Init_Server_v65()
Enable_Admin_OpenAPI("SUA_CHAVE_ADMIN_FORTE")

2. Importe o sample_openapi_pedidos.json (via Client ou Postman).
3. Execute os testes: Test_Smoke_V65(), Test_Security_V65(), Test_BLOB_V65(), Test_Fuzzer_V65().
4. Confira /api/v6/admin/metrics e a tabela AUDIT_LOG.
Boller
Sim — essa versão v6.5 que geramos já inclui log de execução estruturado.

?? Como funciona o log
• A classe clsApiLoggerV65 grava todas as requisições e erros em JSON estruturado.
• Dois destinos de log:
1. Console/Trace ? útil em desenvolvimento.
2. Tabela AUDIT_LOG no HFSQL ? persistente, para auditoria.

?

?? O que é registrado
• ID da requisição (X-Request-Id)
• Timestamp (ts)
• IP de origem
• Usuário / API Key (quando disponível)
• Método HTTP (GET, POST etc.)
• Endpoint acessado
• Status HTTP retornado
• Tempo de execução (latency_ms)
• Tamanho da resposta (bytes_out)
• Mensagem de erro (se houver)

?

?? Consulta do log

Você pode consultar diretamente a tabela AUDIT_LOG:

HReadSeekFirst(AUDIT_LOG, ID, 1)
WHILE NOT HOut()
Trace(AUDIT_LOG.ts + " " + AUDIT_LOG.method + " " + AUDIT_LOG.path + " " + AUDIT_LOG.status)
HReadNext(AUDIT_LOG, ID)
END


?

Perfeito ??
Vou te mostrar na prática como a clsApiLoggerV65 registra e como ler esses logs.

?

?? Exemplo de gravação de log

Quando o servidor recebe uma requisição, algo assim acontece internamente:

// Exemplo: GET /api/v6/pedidos
reqId is string = GenerateUUID()
startTime is int = TimeSys()

// ... processamento da requisição ...

clsApiLoggerV65.LogInfo("Requisição atendida",
[
"req_id" : reqId,
"ts" : DateSys() + "T" + TimeSys(),
"ip" : "192.168.0.15",
"user" : "apiKey_123",
"method" : "GET",
"path" : "/api/v6/pedidos",
"status" : 200,
"latency_ms" : TimeSys() - startTime,
"bytes_out" : Length(responseJSON)
])

Isso vai para:
1. Console/Trace ? no formato JSON, fácil de importar no Kibana, Loki etc.
2. Tabela HFSQL AUDIT_LOG ? para persistência e auditoria posterior.

?

?? Como fica no HFSQL

Registro exemplo na AUDIT_LOG:

id ts ip user method path status latency_ms bytes_out req_id
101 2025-08-15T14:32:08 192.168.0.15 apiKey_123 GET /api/v6/pedidos 200 37 512 4f3a1d30-3bca-4b8e-8d2d-81c12345faab


?

?? Consulta via código

HReadFirst(AUDIT_LOG, ID)
WHILE NOT HOut()
Trace(
AUDIT_LOG.ts + " | " +
AUDIT_LOG.method + " " + AUDIT_LOG.path + " " +
StringBuild("[%1]", AUDIT_LOG.status) + " " +
AUDIT_LOG.latency_ms + "ms"
)
HReadNext(AUDIT_LOG, ID)
END


?

?? Saída em JSON (modo console)

{
"level": "INFO",
"message": "Requisição atendida",
"req_id": "4f3a1d30-3bca-4b8e-8d2d-81c12345faab",
"ts": "2025-08-15T14:32:08",
"ip": "192.168.0.15",
"user": "apiKey_123",
"method": "GET",
"path": "/api/v6/pedidos",
"status": 200,
"latency_ms": 37,
"bytes_out": 512
}

//——-
Boller
/api-v65/
+- /server/
¦ +- clsApiServerV65.txt
¦ +- clsOpenAPI_Importer_V65.txt
¦ +- clsApiConfigV65.txt
¦ +- clsApiLoggerV65.txt
¦ +- clsRateLimiterV65.txt
¦ +- clsInputValidatorV65.txt
¦ +- clsMetricsV65.txt
¦ +- README_v65_Hardening.txt
¦ +- (runtime) api_config_v65.json
¦ +- (gerado) api_allowed_tables.json
¦ +- (gerado) openapi_client_meta.json
¦ +- (dados) AUDIT_LOG.fic (+ índices)
¦
+- /client/
¦ +- clsApiClientV65.txt
¦ +- Client_v65_QuickStart.txt
¦
+- /tests/
+- tests_env.json
+- Tests_v65_Smoke.wl
+- Tests_v65_Security.wl
+- Tests_v65_BLOB.wl
+- Tests_v65_Fuzzer.wl
+- Postman_Collection_v65.json
+- curl_tests.sh / curl_tests.bat
+- k6_load_v65.js
+- README_Tests_v65.txt
Boller
API v6.5 FULL PACKAGE - CONTEÚDO

Este pacote contém:

1) SERVER (/server/)
- clsApiServerV65.txt
- clsOpenAPI_Importer_V65.txt
- clsApiConfigV65.txt
- clsApiLoggerV65.txt
- clsRateLimiterV65.txt
- clsInputValidatorV65.txt
- clsMetricsV65.txt
- README_v65_Hardening.txt
- Server_Bootstrap.wl
- api_config_v65.json
- sample_openapi_pedidos.json

2) CLIENT (/client/)
- clsApiClientV65.txt
- Client_v65_QuickStart.txt
- Examples_ERP_Pedidos.wl

3) TESTES (/tests/)
- tests_env.json
- Tests_v65_Smoke.wl
- Tests_v65_Security.wl
- Tests_v65_BLOB.wl
- Tests_v65_Fuzzer.wl
- Postman_Collection_v65.json
- curl_tests.sh / curl_tests.bat
- k6_load_v65.js
- README_Tests_v65.txt

4) DOCUMENTAÇÃO (/docs/)
- Manual_v65_Completo.md
- README_MASTER.txt

PASSOS DE USO:
1) Importe os arquivos de /server/ no projeto servidor WinDev/WebDev
2) Chame no início:
Init_Server_v65()
Enable_Admin_OpenAPI("SUA_CHAVE_ADMIN_FORTE")
3) Ajuste api_config_v65.json conforme necessário
4) Use o /client/ para importar o OpenAPI e realizar operações CRUD
5) Rode a bateria de testes de /tests/
A PROPOS
EVALUATION :
00Aucune évaluation
TELECHARGEMENTS :
22
PUBLIÉE :
15 août 2025
VERSION :
30
CONCERNE :
WINDEV, WEBDEV, WINDEV Mobile
Version minimale : 30
TAILLE :
11,6 Ko