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 atributo | value |
|---|---|
string, text | string |
integer, float, real | number, convertido a partir da forma de string da API |
image, file com um arquivo | o próprio objeto do arquivo |
image, file com vários arquivos | um array de objetos de arquivo |
groupOfImages | sempre um array (é uma coleção por definição) |
list | um array |
timeInterval | um array de grupos; veja Intervalos de tempo |
| nenhum valor definido | sempre 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 chaveattributeValues. 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 osattributesde formulários, os campos de form-data e osadditionalFieldsaninhados 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/floatnão definido não é mais0.Number(null)é0, então umnullexplícito da API era informado como um zero real, indistinguível de um0configurado.
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
- Intervalos de tempo: expandir atributos
timeIntervalem horários - Importação de tipos:
IAttributeValue,IAttributeValuese os demais - Módulo AttributesSets: como os atributos são configurados