Pular para o conteúdo principal

Valores de atributos

O OneEntry descreve o conteúdo com atributos: uma página, um produto, um bloco, um usuário, um pedido ou um campo de formulário traz um mapa de valores de atributo indexado por marcador. O SDK normaliza todos os atributos de todas as respostas para o mesmo formato, de modo que um campo tem a mesma aparência, qualquer que seja o módulo que o retornou.

const page = await Pages.getPageByUrl('catalog');
if ('statusCode' in page) throw new Error(page.message);

page.attributeValues.title.value; // "Catalog" — string
page.attributeValues.amount.value; // 5 — number
page.attributeValues.cover.value; // { downloadLink } — the file object itself
page.attributeValues.notes.value; // null — no value set

O formato normalizado​

Um valor de atributo é um IAttributeValue: { type, value, position?, additionalFields? }. O conteúdo de value depende de type:

Tipo de atributovalue
string, textstring
integer, float, realnumber, convertido a partir da forma de string da API
image, file com um arquivoo próprio objeto do arquivo
image, file com vários arquivosum array de objetos de arquivo
groupOfImagessempre um array (é uma coleção por definição)
listum array
timeIntervalum array de grupos; veja Intervalos de tempo
nenhum valor definidosempre null

Atributos com um único arquivo são desembrulhados​

Quando um atributo image ou file contém exatamente um arquivo, o seu value é o próprio objeto do arquivo. Apenas os valores com dois ou mais arquivos continuam sendo arrays.

const block = await Blocks.getBlockByMarker('promo');

// before: block.attributeValues.img.value[0].downloadLink
// now: block.attributeValues.img.value.downloadLink

Isso se aplica em todos os módulos.

Migração: versões anteriores desempacotavam arquivos únicos apenas em products, menus, forms, forms-data, attribute-sets, integration-collections e Pages.searchPage, e apenas sob a chave attributeValues. Em todos os outros lugares (blocks, todos os outros métodos de pages, Products.getProductsEmptyPage, Products.getProductBlockById, admins, discounts, templates, orders, users), o mesmo atributo chegava como um array de um elemento, e os attributes de formulários, os campos de form-data e os additionalFields aninhados nunca eram desempacotados. O código para products ou menus já lê o objeto e não é afetado. O código que lê value[0] de blocks, pages, users ou orders precisa remover o índice.

groupOfImages permanece sempre um array. Os envios seguem regras próprias: IBodyTypeFile.value aceita um File/Blob (ou um FileList), o descritor que FileUploading.upload retorna ou um IFileValue, e eles podem ser misturados em um mesmo array. Um File/Blob isolado funciona, mas um descritor ou um IFileValue precisa ser enviado dentro de um array, ou a API responde com um erro 400.

Números são números​

Os valores integer, float e real são convertidos em números. (As versões anteriores deixavam real como string, então o mesmo campo numérico chegava como 10 ou "10", dependendo de qual dos três tipos tinha sido usado na declaração.)

const page = await Pages.getPageByUrl('catalog');

// before: page.attributeValues.amount.value // "5"
// now: page.attributeValues.amount.value // 5

A normalização numérica também é aplicada aos atributos de formulário e aos campos de form-data: um campo rating de um atributo de formulário integer é um number, e não uma string. As versões anteriores os ignoravam.

Ao enviar dados, envie uma string: IBodyTypeStringNumberFloat.value é string | number | null, e as respostas voltam normalizadas.

Um valor vazio é sempre null​

A API retorna um mapa de localização vazio para um valor não definido, e o SDK o transforma em null para todos os tipos. (As versões anteriores repassavam o mapa nos tipos de texto e retornavam null nos numéricos, então "sem valor" tinha três representações.)

if (page.attributeValues.notes.value === null) {
// nothing configured for this attribute
}

Migração: um integer/float não definido não é mais 0. Number(null) é 0, então um null explícito da API era informado como um zero real, indistinguível de um 0 configurado.

Tudo é ordenado por posição​

attributeValues e os attributes de formulários são retornados na ordem de position. A API retorna os campos de formulário sem ordem (um campo com position: 10 pode chegar depois de position: 14); o SDK os ordena, então você não precisa fazer isso.

Um formulário sem atributos retorna attributes: []. Nesse caso, a API envia um objeto vazio, e o SDK o normaliza para um array vazio, de modo que attributes é sempre IFormAttribute[] e form.attributes.map(...) é seguro em qualquer formulário.

Campos aninhados: additionalFields​

Valores de atributos aninhados chegam em additionalFields. Por padrão, o SDK converte o array retornado pela API em um objeto com chaves marker; defina rawData: true na configuração para manter o array original. Veja Formato dos campos adicionais.

// default (rawData: false)
attribute.additionalFields['my_field'].value;

// rawData: true
attribute.additionalFields.find((f) => f.marker === 'my_field').value;

Os additionalFields aninhados passam pela mesma normalização que os atributos de nível superior: os arquivos únicos são desembrulhados e os números são convertidos.

Tipando um valor de atributo​

IAttributeValue.value é tipado como unknown, porque o seu formato depende de type em tempo de execução. Isso também significa que o compilador não consegue detectar uma suposição errada sobre ele. A regra do arquivo único acima é onde isso pega: um helper escrito como

const file = Array.isArray(value) ? value[0] : undefined; // single image → undefined

compila, faz o build e exibe uma página sem imagens e sem nenhum erro em lugar algum. Em vez disso, leia o valor por meio dos guards e helpers.

Arquivos​

getAttributeFiles retorna todos os arquivos de um atributo image, file ou groupOfImages como uma única lista, seja o valor um único arquivo ou um array. Para qualquer outro atributo, retorna um array vazio, então é seguro chamá-lo em qualquer atributo:

import { getAttributeFile, getAttributeFiles } from 'oneentry';

const gallery = getAttributeFiles(product.attributeValues.images); // IAttributeFile[]

const cover = getAttributeFile(page.attributeValues.cover); // IAttributeFile | null
const blurDataURL = cover?.previewLink?.default?.[0]; // base64 placeholder

IAttributeFile é exportado. Ele tem filename, downloadLink, size e contentType, além dos opcionais previewLink (pré-visualizações indexadas por template, cada uma um par [base64 placeholder, url]) e defaultPreview.

Campos aninhados​

additionalFields é Record<string, IAttributeValue> | unknown[], porque a API retorna um array vazio quando um atributo não tem nenhum. Com essa união, toda leitura de campo é um erro de tipo. getAdditionalFields a reduz a um mapa (e indexa o array de rawData: true por marker):

import { getAdditionalFields } from 'oneentry';

const alt = getAdditionalFields(page.attributeValues.cover).alt?.value;

Guards​

isFileAttribute, isStringAttribute, isNumberAttribute, isListAttribute e isTimeIntervalAttribute restringem um IAttributeValue para que o seu value fique acessível sem conversão de tipo:

import { isStringAttribute } from 'oneentry';
import type { IAttributeValues } from 'oneentry';

function readText(values: IAttributeValues, marker: string): string {
const attr = values[marker];
return isStringAttribute(attr) ? (attr.value ?? '') : '';
}

A união discriminada​

ITypedAttributeValue é IAttributeValue como uma união sobre type: IFileAttributeValue, IStringAttributeValue, INumberAttributeValue, IListAttributeValue, ITimeIntervalAttributeValue. Ela é fechada de propósito: um membro genérico (type: string) corresponderia a todos os case e faria a restrição voltar a unknown. Os tipos que o SDK não modela (entity, date, os personalizados) permanecem em IAttributeValue.

A união é opcional. IAttributeValues mantém o IAttributeValue genérico, então restrinja para a união com os guards ou anote um valor que você controla:

import type { ITypedAttributeValue } from 'oneentry';

function render(attr: ITypedAttributeValue) {
switch (attr.type) {
case 'image':
case 'file':
return attr.value; // IAttributeFile | IAttributeFile[] | null
case 'integer':
return attr.value; // number | null
}
}

Quando a API muda​

Os tipos descrevem a API como ela era quando o SDK foi gerado; eles não conseguem perceber mudanças posteriores. Nos valores de atributo, a falha é silenciosa: se um campo de arquivo fosse renomeado, getAttributeFiles retornaria uma lista vazia e a página seria exibida sem imagens.

A validação de respostas torna isso visível:

const api = defineOneEntry('https://your-project.oneentry.cloud', {
token: 'your-app-token',
validation: { enabled: true, strictMode: false, logErrors: true },
});

A validação verifica attributeValues onde o contrato da API é fixo: o formato dos arquivos nos atributos image, file e groupOfImages, incluindo os campos aninhados. Tudo o que é definido pelo seu próprio conjunto de atributos permanece aberto. Um arquivo que perdeu ou teve um campo renomeado é informado como um problema de validação, com o marcador e o índice; os campos que a API acrescenta são aceitos. (As versões anteriores não validavam attributeValues de forma alguma.)

Com strictMode: false, os dados ainda chegam e o problema é registrado no log; com strictMode: true, a chamada retorna um IError com validationErrors.


Relacionados​