225 lines
7.1 KiB
TypeScript
225 lines
7.1 KiB
TypeScript
import {
|
|
codigosResposta,
|
|
type tipoResposta,
|
|
type tipoRespostaErro,
|
|
} from "./respostas"
|
|
|
|
// ─── Tipagem ELicencie ──────────────────────────────────────────────
|
|
|
|
/**
|
|
* Mapa de códigos HTTP de erro usados pelo sistema ELicencie.
|
|
*
|
|
* Single source of truth para derivar `codigoErroELicencie`.
|
|
*/
|
|
export const codigosErros = {
|
|
400: "Erro de requisição",
|
|
403: "Não autenticado",
|
|
500: "Erro interno",
|
|
504: "Tempo de resposta excedido",
|
|
} as const
|
|
|
|
/** Códigos HTTP de erro reconhecidos pelo ELicencie. */
|
|
export type codigoErroELicencie = keyof typeof codigosErros
|
|
|
|
export interface tipoRespostaSucessoELicencie<T> {
|
|
codigo: 200
|
|
eErro: false
|
|
valor: T
|
|
erro: undefined
|
|
detalhes?: Record<string, any>
|
|
}
|
|
|
|
export interface tipoRespostaErroELicencie {
|
|
codigo: codigoErroELicencie
|
|
eErro: true
|
|
erro: string
|
|
valor: undefined
|
|
detalhes?: Record<string, any>
|
|
}
|
|
|
|
export type tipoRespostaELicencie<T> =
|
|
| tipoRespostaSucessoELicencie<T>
|
|
| tipoRespostaErroELicencie
|
|
|
|
// ─── Tipo de entrada (transição) ────────────────────────────────────
|
|
|
|
/**
|
|
* Durante a transição de sistema, a entrada pode chegar em qualquer um
|
|
* dos dois formatos. O conversor detecta e normaliza para P-Resposta.
|
|
*/
|
|
export type tipoEntradaTransicao<T> = tipoRespostaELicencie<T> | tipoResposta<T>
|
|
|
|
// ─── Mapeamento bidirecional de códigos ─────────────────────────────
|
|
|
|
/** ELicencie → P-Resposta (403 → 401) */
|
|
const mapaCodigoELicencieParaPResposta: Record<
|
|
codigoErroELicencie,
|
|
tipoRespostaErro["cod"]
|
|
> = {
|
|
400: codigosResposta.erroConhecido,
|
|
403: codigosResposta.erroPermissao,
|
|
500: codigosResposta.erroDesconhecido,
|
|
504: codigosResposta.tempoEsgotado,
|
|
}
|
|
|
|
/** P-Resposta → ELicencie (401 → 403, 404 → 400) */
|
|
const mapaCodigoPRespostaParaELicencie: Record<
|
|
tipoRespostaErro["cod"],
|
|
codigoErroELicencie
|
|
> = {
|
|
[codigosResposta.erroConhecido]: 400,
|
|
[codigosResposta.erroPermissao]: 403,
|
|
[codigosResposta.erroDesconhecido]: 500,
|
|
[codigosResposta.erroNaoEncontrado]: 400,
|
|
[codigosResposta.tempoEsgotado]: 504,
|
|
}
|
|
|
|
// ─── Type guard ─────────────────────────────────────────────────────
|
|
|
|
/** Detecta se a entrada já está no formato P-Resposta (possui campo `cod`). */
|
|
function eRespostaPadrao<T>(
|
|
entrada: tipoEntradaTransicao<T>,
|
|
): entrada is tipoResposta<T> {
|
|
return "cod" in entrada
|
|
}
|
|
|
|
// ─── Conversores de detalhes ────────────────────────────────────────
|
|
|
|
/** `Record<string, any>` → `string[]` (passthrough se já for array). */
|
|
function detalhesParaArray(
|
|
detalhes?: Record<string, any> | string[],
|
|
): string[] | undefined {
|
|
if (!detalhes) return undefined
|
|
if (Array.isArray(detalhes)) return detalhes
|
|
|
|
const entries = Object.entries(detalhes)
|
|
if (entries.length === 0) return undefined
|
|
|
|
return entries.map(([chave, valor]) => {
|
|
const valorStr = typeof valor === "string" ? valor : JSON.stringify(valor)
|
|
return `${chave}: ${valorStr}`
|
|
})
|
|
}
|
|
|
|
// ─── Builder de resposta dual ───────────────────────────────────────
|
|
|
|
/**
|
|
* Monta um objeto compatível com **ambos** os formatos em runtime.
|
|
*
|
|
* Tipado como `tipoResposta<T>`, mas em runtime também contém
|
|
* `codigo`, `erro` e `detalhes` (Record) do formato ELicencie.
|
|
*/
|
|
function montarRespostaDual<T>(op: {
|
|
cod: tipoResposta<T>["cod"]
|
|
valor: T | undefined
|
|
mensagem: string | undefined
|
|
eErro: boolean
|
|
codigoELicencie: 200 | codigoErroELicencie
|
|
erroELicencie: string | undefined
|
|
detalhesOriginal?: Record<string, any> | string[]
|
|
}): tipoResposta<T> {
|
|
const detalhesArray = detalhesParaArray(op.detalhesOriginal)
|
|
const detalhesRecord =
|
|
op.detalhesOriginal && !Array.isArray(op.detalhesOriginal)
|
|
? op.detalhesOriginal
|
|
: undefined
|
|
|
|
return {
|
|
// P-Resposta
|
|
cod: op.cod,
|
|
valor: op.valor,
|
|
mensagem: op.mensagem,
|
|
eErro: op.eErro,
|
|
eCerto: !op.eErro,
|
|
detalhes: detalhesArray,
|
|
|
|
// ELicencie (runtime)
|
|
codigo: op.codigoELicencie,
|
|
erro: op.erroELicencie,
|
|
...(detalhesRecord ? { detalhes: detalhesRecord } : {}),
|
|
} as tipoResposta<T>
|
|
}
|
|
|
|
// ─── Conversor principal ────────────────────────────────────────────
|
|
|
|
/**
|
|
* Converte uma resposta para o formato **P-Resposta** (padrão do pacote),
|
|
* mantendo em runtime os campos de **ambos** os formatos para
|
|
* compatibilidade durante a transição de sistema.
|
|
*
|
|
* **Tipo de retorno:** `tipoResposta<T>`
|
|
*
|
|
* **Conteúdo em runtime:** campos P-Resposta + campos ELicencie
|
|
*
|
|
* | P-Resposta | ELicencie | Relação |
|
|
* |-------------|-------------|----------------------|
|
|
* | `cod` | `codigo` | mapeamento de código |
|
|
* | `mensagem` | `erro` | mesmo conteúdo |
|
|
* | `valor` | `valor` | direto |
|
|
* | `eErro` | `eErro` | direto |
|
|
* | `eCerto` | — | derivado |
|
|
* | `detalhes` | `detalhes` | string[] / Record |
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* const res = converterRespostaELicencieParaPResposta(respostaQualquer)
|
|
*
|
|
* // Consumidor P-Resposta
|
|
* if (res.eCerto) console.log(res.valor)
|
|
*
|
|
* // Consumidor ELicencie (lê campo extra em runtime)
|
|
* const resAny = res as any
|
|
* if (!resAny.eErro) console.log(resAny.valor)
|
|
* ```
|
|
*/
|
|
export const converterRespostaELicencieParaPResposta = <T>(
|
|
entrada: tipoEntradaTransicao<T>,
|
|
): tipoResposta<T> => {
|
|
// ── Entrada já é P-Resposta: enriquecer com campos ELicencie ──
|
|
if (eRespostaPadrao(entrada)) {
|
|
if (!entrada.eErro) {
|
|
return montarRespostaDual<T>({
|
|
cod: codigosResposta.sucesso,
|
|
valor: entrada.valor,
|
|
mensagem: undefined,
|
|
eErro: false,
|
|
codigoELicencie: 200,
|
|
erroELicencie: undefined,
|
|
detalhesOriginal: entrada.detalhes,
|
|
})
|
|
}
|
|
|
|
return montarRespostaDual<T>({
|
|
cod: entrada.cod,
|
|
valor: undefined,
|
|
mensagem: entrada.mensagem,
|
|
eErro: true,
|
|
codigoELicencie: mapaCodigoPRespostaParaELicencie[entrada.cod],
|
|
erroELicencie: entrada.mensagem,
|
|
detalhesOriginal: entrada.detalhes,
|
|
})
|
|
}
|
|
|
|
// ── Entrada é ELicencie: construir P-Resposta + preservar campos ──
|
|
if (!entrada.eErro) {
|
|
return montarRespostaDual<T>({
|
|
cod: codigosResposta.sucesso,
|
|
valor: entrada.valor,
|
|
mensagem: undefined,
|
|
eErro: false,
|
|
codigoELicencie: entrada.codigo,
|
|
erroELicencie: undefined,
|
|
detalhesOriginal: entrada.detalhes,
|
|
})
|
|
}
|
|
|
|
return montarRespostaDual<T>({
|
|
cod: mapaCodigoELicencieParaPResposta[entrada.codigo],
|
|
valor: undefined,
|
|
mensagem: entrada.erro,
|
|
eErro: true,
|
|
codigoELicencie: entrada.codigo,
|
|
erroELicencie: entrada.erro,
|
|
detalhesOriginal: entrada.detalhes,
|
|
})
|
|
}
|