> For the complete documentation index, see [llms.txt](https://docs.tunnelhub.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tunnelhub.io/produto/sql-tables.md).

# Tabelas de Apoio

Tabelas de Apoio são tabelas relacionais gerenciadas pelo TunnelHub para guardar dados operacionais que precisam ser consultados ou alterados por automações.

Use esse recurso quando o dado tem estrutura própria, chave primária, colunas tipadas e precisa ir além de um simples de/para.

## Quando usar

Tabelas de Apoio fazem sentido para cenários como:

* cache operacional compartilhado por execuções;
* controle de processamento por entidade de negócio;
* tabelas auxiliares com várias colunas;
* dados importados por CSV e consumidos por automações;
* estado de integração que precisa ser visível e administrado no produto.

Se o caso for apenas traduzir um valor de origem para um valor de destino, prefira [Tabelas De/Para](/produto/data-stores.md).

## Modelo de dados

Cada Tabela de Apoio possui:

* `externalCode`, usado por automações e SDK;
* descrição funcional;
* pacote associado;
* lista opcional de usuários limitados;
* `schemaDefinition`, com colunas e tipos;
* `primaryKey`, com uma ou mais colunas;
* `indexesDefinition`, para índices adicionais.

Tipos de coluna suportados:

* `TEXT`;
* `INTEGER`;
* `REAL`;
* `NUMERIC`;
* `BOOLEAN`;
* `DATETIME`.

Além da chave primária definida no schema, o TunnelHub mantém um `rowId` técnico para operações sobre linhas.

## Operações disponíveis

No produto, Tabelas de Apoio permitem:

* criar e editar a definição da tabela;
* listar linhas com paginação, filtros e ordenação;
* inserir, editar e remover linhas;
* importar dados por CSV;
* exportar dados por CSV;
* controlar visibilidade para usuários limitados.

Alterações de schema são tratadas pela plataforma. Mudanças destrutivas ou troca de chave primária podem exigir recriação planejada da tabela.

## Uso em automações

Para usar Tabelas de Apoio em uma automação, associe a tabela à automação no produto e habilite o runtime compatível no `tunnelhub.yml`.

Exemplo:

```yaml
configuration:
  runtimeEngine: LAMBDA
  entrypoint: index.handler
  runtime: nodejs24.x
  memorySize: 1024
  timeout: 60
  runInVpc: true
  sqlTables:
    enabled: true
```

Requisitos quando `sqlTables.enabled` é `true`:

* `runtime` deve ser `nodejs24.x`;
* `runInVpc` deve ser `true`;
* `runtimeEngine` deve ser `LAMBDA` ou `ECS_FARGATE`.

## Uso no SDK

O SDK expõe o helper `SqlTables` para operar linhas durante a execução.

Exemplo conceitual:

```ts
const sqlTables = new SqlTables(event);

const inserted = await sqlTables.insertRow('customer-cache', {
  document: '12345678900',
  name: 'Cliente exemplo',
  active: true,
});

const page = await sqlTables.queryRows('customer-cache', {
  current: 1,
  pageSize: 20,
  filter: {
    document: ['12345678900'],
  },
});

await sqlTables.updateRow('customer-cache', inserted.rowId, {
  name: 'Cliente atualizado',
});
```

O helper acessa o arquivo SQLite montado pela plataforma durante a execução. Ele não deve ser usado fora do runtime gerenciado do TunnelHub.

## Testes locais

Para testes unitários com Jest, defina `SDK.testMode = true` antes de construir a integração. Nesse modo, o construtor de `SqlTables` não valida os requisitos do runtime gerenciado. As operações continuam precisando de mock explícito.

`testMode` não fornece persistência local e não simula SQLite, EFS ou DynamoDB. O exemplo a seguir usa fixtures por `externalCode` e mantém as inserções apenas em memória durante o processo do Jest:

```ts
import fs from 'node:fs';
import path from 'node:path';
import {ProcessorPayload, SDK, SqlTables} from '@tunnelhub/sdk';

type CustomerCacheRow = {
  document: string;
  name: string;
  active: boolean;
};

type MockRow = {
  rowId: string;
  values: CustomerCacheRow;
};

const rowsByTable = new Map<string, MockRow[]>();
let nextRowId = 1;

const testEvent = {
  tenantId: 'test-tenant',
  environmentId: 'test-environment',
  automationId: 'test-automation',
  executionId: 'test-execution',
  systems: [],
  parameters: {},
} as ProcessorPayload;

function getRows(externalCode: string): MockRow[] {
  const rows = rowsByTable.get(externalCode);
  if (rows) return rows;

  const fixturePath = path.join(__dirname, 'data', `${externalCode}.json`);
  const fixture = JSON.parse(fs.readFileSync(fixturePath, 'utf8')) as MockRow[];
  const clonedRows = structuredClone(fixture);
  rowsByTable.set(externalCode, clonedRows);
  return clonedRows;
}

describe('CustomerCacheIntegration', () => {
  beforeAll(() => {
    SDK.testMode = true;
  });

  beforeEach(() => {
    jest.spyOn(SqlTables.prototype, 'queryRows').mockImplementation(async (externalCode, request = {}) => {
      const current = request.current ?? 1;
      const pageSize = request.pageSize ?? 20;
      const rows = getRows(externalCode);

      return {
        success: true,
        current,
        pageSize,
        total: rows.length,
        data: rows.slice((current - 1) * pageSize, current * pageSize),
      };
    });

    jest.spyOn(SqlTables.prototype, 'insertRow').mockImplementation(async (externalCode, values) => {
      const row = {rowId: `test-row-${nextRowId++}`, values};
      getRows(externalCode).push(row as MockRow);
      return row;
    });
  });

  afterEach(() => {
    jest.restoreAllMocks();
    rowsByTable.clear();
    nextRowId = 1;
  });

  afterAll(() => {
    SDK.testMode = false;
  });

  test('insere cliente ausente do cache', async () => {
    const sqlTables = new SqlTables(testEvent);

    await sqlTables.insertRow('customer-cache', {document: '12345678900', name: 'Ana', active: true});

    expect(await sqlTables.queryRows('customer-cache')).toEqual({
      success: true,
      current: 1,
      pageSize: 20,
      total: 1,
      data: [
        {
          rowId: 'test-row-1',
          values: {document: '12345678900', name: 'Ana', active: true},
        },
      ],
    });
  });
});
```

Organize as fixtures em `__tests__/data/<externalCode>.json`. Cada arquivo deve conter uma lista de linhas no formato retornado por `queryRows`. Por exemplo, o arquivo `__tests__/data/customer-cache.json` usado no teste de inserção pode conter apenas:

```json
[]
```

Uma fixture vazia representa uma tabela existente sem registros.

O mock é apropriado para testar o fluxo da automação. Ele não reproduz schema, constraints, normalização de tipos nem o comportamento do SQLite. Teste esses cenários com `SqlTables` real, usando um arquivo SQLite preparado ou o runtime gerenciado do TunnelHub.

## Boas práticas

* escolha `externalCode` estável e orientado ao domínio;
* defina uma chave primária natural quando possível;
* modele colunas com tipos claros;
* use índices somente para consultas recorrentes;
* evite armazenar segredos ou credenciais;
* prefira Tabelas de Apoio para dados estruturados e Tabelas De/Para para mapeamentos simples.
