Capítulo 5: Características Avanzadas de OData

Más allá de las operaciones CRUD básicas y las opciones de consulta estándar, OData ofrece un conjunto de características avanzadas diseñadas para abordar escenarios más complejos, mejorar el rendimiento y aumentar la robustez de las interacciones cliente-servidor. Este capítulo explora algunas de las capacidades más importantes: solicitudes por lotes ($batch), control de concurrencia mediante ETags, definición y uso de funciones y acciones personalizadas, el poder de las anotaciones y las consultas delta para la sincronización de datos.

Solicitudes por Lotes ($batch)

Las solicitudes por lotes ($batch) permiten agrupar múltiples operaciones OData (lecturas, escrituras, acciones) en una única solicitud HTTP POST.55 Esto ofrece ventajas significativas:

  • Rendimiento: Reduce drásticamente el número de viajes de red entre el cliente y el servidor, mejorando la latencia y la eficiencia general, especialmente en redes con alta latencia o cuando se realizan muchas operaciones pequeñas.2

  • Atomicidad (con Changesets): Permite agrupar múltiples operaciones de modificación (Crear, Actualizar, Eliminar, Acciones) en "Conjuntos de Cambios" (Changesets) que se ejecutan como una unidad atómica (todo o nada).56

  • Seguridad: Las URLs de las solicitudes individuales, que podrían contener referencias a datos, forman parte del cuerpo de la solicitud HTTP POST, lo que puede ofrecer ventajas de seguridad en comparación con enviar cada URL individualmente como GET.55

Estructura de una Solicitud $batch

Una solicitud $batch se envía como una HTTP POST a la URI $batch relativa a la raíz del servicio (ej. serviceRoot/$batch).57

  • Cabecera Content-Type: Debe ser multipart/mixed y debe incluir un parámetro boundary que define la cadena utilizada para separar las partes individuales dentro del cuerpo del lote.57

  • Cuerpo de la Solicitud: Es un mensaje MIME multipart. Consiste en una secuencia ordenada de partes, donde cada parte representa una operación OData individual (como una solicitud GET) o un Changeset.57 Cada parte está separada por la cadena de límite definida en la cabecera Content-Type.57

  • Cada parte individual que representa una operación debe incluir sus propias cabeceras, como Content-Type: application/http y Content-Transfer-Encoding: binary, seguidas de la línea de solicitud HTTP (ej. GET /service/Products(1) HTTP/1.1) y cualquier cabecera específica de esa solicitud.57

Conjuntos de Cambios (Changesets)

Un Changeset es una construcción especial dentro de una solicitud $batch que agrupa una o más operaciones de modificación (POST, PUT, PATCH, DELETE, Acciones).57

  • Atomicidad: Todas las operaciones dentro de un Changeset se tratan como una única unidad lógica de trabajo. El servidor debe garantizar que todas las operaciones dentro del Changeset se completen con éxito, o ninguna de ellas se aplique permanentemente (semántica "todo o nada").57 El servidor es responsable de implementar la lógica de rollback necesaria si una operación falla dentro del Changeset.57

  • Estructura: Un Changeset se representa como una parte MIME dentro de la solicitud $batch. El contenido de esta parte MIME es, a su vez, un documento multipart/mixed, con su propio límite (boundary) distinto al del lote principal.57 Cada operación dentro del Changeset es una parte MIME separada dentro de este multipart anidado.58

  • Orden: El orden de las operaciones dentro de un Changeset no es significativo; el servidor puede ejecutarlas en cualquier orden.57 Sin embargo, el orden de las operaciones de consulta y los Changesets fuera de los Changesets (en el nivel superior del $batch) sí es significativo y el servidor debe procesarlos en el orden recibido.57

  • Restricciones: Los Changesets no pueden contener operaciones de consulta (GET) y no pueden anidarse.57

Referenciación dentro de un Changeset

Una característica útil de los Changesets es la capacidad de referenciar entidades creadas en una operación anterior dentro del mismo Changeset.

  • Content-ID: Si una operación POST (creación) dentro de un Changeset incluye una cabecera Content-ID (ej. Content-ID: 1), las operaciones posteriores dentro del mismo Changeset pueden referirse a la URI de la entidad recién creada utilizando un alias formado por $ seguido del valor del Content-ID (ej. $1).57

  • Ejemplo: Se podría tener una operación POST para crear un nuevo Pedido con Content-ID: order1, seguida de una operación POST para crear una LineaDePedido cuya URI de solicitud sea $order1/LineItems.58 Esto permite crear entidades relacionadas en una única transacción atómica.

  • Limitación: Esta referencia solo es válida dentro del mismo Changeset; no se puede referenciar un Content-ID de un Changeset en otro Changeset diferente dentro del mismo $batch.63

Ejemplo de Estructura $batch (Conceptual):

HTTP

POST /service/$batch HTTP/1.1 Host: example.com Content-Type: multipart/mixed; boundary=batch_123 --batch_123 Content-Type: application/http Content-Transfer-Encoding: binary GET Products(1) HTTP/1.1 --batch_123 Content-Type: multipart/mixed; boundary=changeset_abc Content-Transfer-Encoding: binary --changeset_abc Content-Type: application/http Content-Transfer-Encoding: binary Content-ID: 1 POST Orders HTTP/1.1 Content-Type: application/json { "CustomerID": "ALFKI",... } --changeset_abc Content-Type: application/http Content-Transfer-Encoding: binary POST $1/OrderDetails HTTP/1.1 Content-Type: application/json { "ProductID": 5, "Quantity": 10 } --changeset_abc-- --batch_123 Content-Type: application/http Content-Transfer-Encoding: binary GET Customers('ALFKI') HTTP/1.1 --batch_123--

57

El manejo de $batch en SAP Gateway está soportado, y SAPUI5 (especialmente el ODataModel) lo utiliza internamente para agrupar solicitudes, ofreciendo control sobre cuándo se envían los lotes (especialmente los Changesets diferidos).14

ETags y Control de Concurrencia

En entornos multiusuario, existe el riesgo de que varios usuarios intenten modificar el mismo recurso simultáneamente, lo que puede llevar a "actualizaciones perdidas" (un usuario sobrescribe los cambios de otro sin saberlo). OData utiliza el mecanismo estándar de HTTP de ETags (Entity Tags) para implementar el control de concurrencia optimista y prevenir estos problemas.38

¿Qué son los ETags?

  • Un ETag es un identificador opaco (una cadena) generado por el servidor que representa una versión específica de un recurso (una entidad OData en este contexto).39

  • Cuando un cliente recupera una entidad que soporta ETags, el servidor incluye el ETag de esa versión en la respuesta (ya sea en la cabecera ETag para una sola entidad o como una propiedad @odata.etag dentro del payload para colecciones).39

  • Los ETags pueden ser "fuertes" (indican identidad bit a bit) o "débiles" (prefijados con W/, indican equivalencia semántica).39 Dataverse, por ejemplo, usa ETags débiles.40 SAP Gateway también soporta ETags.39

Cabeceras If-Match e If-None-Match

El cliente utiliza el ETag recibido en solicitudes posteriores de modificación (PUT, PATCH, DELETE) o recuperación condicional (GET) mediante cabeceras HTTP condicionales:

  • If-Match: Se usa en solicitudes de modificación (PUT, PATCH, DELETE). El cliente incluye la cabecera If-Match con el valor ETag de la versión que espera modificar o eliminar.39

  • El servidor compara el ETag recibido en If-Match con el ETag actual del recurso en el servidor.

  • Si coinciden, el servidor procesa la solicitud (la modificación se aplica a la versión esperada).

  • Si no coinciden (el recurso ha sido modificado por otro usuario desde que el cliente lo leyó), el servidor rechaza la solicitud con un código de estado 412 Precondition Failed.39

  • Si el cliente no envía If-Match para un recurso que tiene ETag, el servidor debería rechazar la solicitud (posiblemente con 412 o 428 Precondition Required).39

  • If-Match: * indica al servidor que realice la operación sin importar el ETag actual (sobrescritura incondicional), lo cual debe usarse con precaución.39

  • If-None-Match: Se usa principalmente en solicitudes GET para recuperación condicional (caching).39 El cliente envía el ETag de la versión que tiene en caché.

  • Si el ETag en el servidor coincide con el valor en If-None-Match (el recurso no ha cambiado), el servidor responde con 304 Not Modified y un cuerpo vacío, ahorrando ancho de banda.40

  • Si el ETag no coincide (el recurso ha cambiado), el servidor devuelve 200 OK con la nueva representación del recurso y el nuevo ETag.40

  • También se puede usar en otros métodos (como PUT para "crear si no existe") donde la operación solo debe proceder si el recurso no tiene un ETag que coincida.39

Implementación en SAP Gateway

SAP Gateway ofrece soporte genérico para ETags.39

  • Definición: Se marca una o más propiedades de la entidad en SEGW como parte del token de concurrencia (propiedad Concurrency Mode = Fixed en el modelo subyacente).67 A menudo, se utiliza un campo de timestamp o un campo específico para almacenar un hash.65

  • Generación: En los métodos DPC que devuelven entidades (GET_ENTITY, GET_ENTITYSET, CREATE_ENTITY, UPDATE_ENTITY), la implementación debe calcular/recuperar el valor del ETag para la entidad/versión actual.

  • Se puede usar un timestamp de última modificación.

  • Se puede calcular un hash (ej. SHA1) del contenido de la entidad usando FMs como CALCULATE_HASH_FOR_CHAR o CALCULATE_HASH_FROM_RAW.65

  • Este valor ETag debe asignarse a la propiedad marcada como ETag en la estructura de la entidad devuelta (ej. er_entity-Etag = lv_calculated_etag).65 El framework de Gateway se encarga de ponerlo en la cabecera o payload según corresponda.39

  • Verificación: Para UPDATE_ENTITY y DELETE_ENTITY, el framework de Gateway generalmente realiza la verificación If-Match automáticamente si la entidad está habilitada para concurrencia.39

  • Se puede obtener la información condicional (valor de If-Match/If-None-Match) usando io_tech_request_context->get_conditional_info() o io_data_provider->get_header_field('If-Match').39

  • Se puede implementar una verificación manual comparando el ETag recibido con el ETag actual calculado/almacenado para el registro y lanzando una excepción /IWBEP/CX_MGW_BUSI_EXCEPTION con código PRECONDITION_FAILED si no coinciden.65

  • Function Imports (Actions): Para acciones implementadas como Function Imports que modifican datos, la verificación ETag debe implementarse manualmente dentro del método EXECUTE_ACTION, obteniendo la información condicional de io_tech_request_context y lanzando las excepciones apropiadas (412 o 428) si es necesario.39 Se debe redefinir GET_IS_CONDI_IMPLE_FOR_ACTION para indicar que la acción soporta verificación ETag.39

Funciones y Acciones

Mientras que las operaciones CRUD cubren la manipulación básica de entidades, OData también permite definir operaciones personalizadas con lógica de negocio más compleja a través de Funciones (Functions) y Acciones (Actions).2

Funciones (Function Imports / Bound Functions)

  • Propósito: Representan operaciones que no tienen efectos secundarios observables en el sistema; esencialmente, realizan cálculos o recuperan datos que no se ajustan fácilmente al modelo CRUD estándar.2 Son comparables a métodos GET con lógica adicional.

  • Tipos:

  • Function Imports: Se definen en el contenedor de entidades y se invocan directamente desde la raíz del servicio (ej. serviceRoot/GetSalesByRegion?Region='North').8

  • Bound Functions: Están vinculadas (bound) a un tipo de entidad o colección específico y se invocan en el contexto de una instancia o colección de ese tipo (ej. serviceRoot/Products(1)/MostRecentSale() o serviceRoot/Products/TopSelling()).18

  • Parámetros: Pueden aceptar cero o más parámetros (generalmente tipos primitivos, complejos o enumeraciones).7 Los parámetros se pasan en la URL para GET (ej. ?param=value) o en el cuerpo para POST.

  • Valor de Retorno: Devuelven datos (un tipo primitivo, tipo complejo, una entidad individual o una colección de entidades/tipos complejos).7

  • Método HTTP: Generalmente se invocan con GET.72

Acciones (Action Imports / Bound Actions)

  • Propósito: Representan operaciones que sí pueden tener efectos secundarios observables en el sistema, como modificar datos, desencadenar procesos de negocio, etc..2 Son comparables a métodos POST con lógica personalizada.

  • Tipos:

  • Action Imports: Se definen en el contenedor de entidades y se invocan desde la raíz del servicio (ej. serviceRoot/ActivateWorkflow).70

  • Bound Actions: Están vinculadas a un tipo de entidad o colección y se invocan en el contexto de una instancia o colección (ej. serviceRoot/Orders(123)/CancelOrder o serviceRoot/Users/LockAccounts).18

  • Parámetros: Pueden aceptar parámetros en el cuerpo de la solicitud.70

  • Valor de Retorno: Pueden devolver datos (similar a las funciones) o ningún contenido (204 No Content).

  • Método HTTP: Siempre se invocan con POST.72

Implementación en SAP Gateway (SEGW, DPC)

  • Definición en SEGW:

  • Las Function Imports y Action Imports se crean directamente bajo el nodo Data Model del proyecto SEGW.36

  • Las Bound Functions y Bound Actions se definen asociadas a un Entity Type específico.

  • Se definen los parámetros de entrada (Importing) y el tipo de retorno (Return Type) para funciones y acciones.

  • Implementación en DPC:

  • Todas las Function Imports y Action Imports (tanto unbound como bound) se implementan redefiniendo un único método en la clase DPC_EXT: /IWBEP/IF_MGW_APPL_SRV_RUNTIME~EXECUTE_ACTION.39

  • Dentro de EXECUTE_ACTION, se utiliza el parámetro de importación iv_action_name para determinar qué función o acción específica se está invocando.71

  • Se utiliza una sentencia CASE iv_action_name para dirigir el flujo a la lógica específica de cada función/acción.

  • Los parámetros de entrada se leen usando io_tech_request_context->get_parameters().

  • El resultado (si lo hay) se devuelve utilizando el método copy_data_to_ref para asignar los datos al parámetro de exportación er_data.71

  • Es una buena práctica encapsular la lógica de cada función/acción en métodos o clases separadas y llamar a esas desde el CASE en EXECUTE_ACTION.75

Anotaciones (Annotations)

Las anotaciones son una forma de añadir metadatos adicionales o semántica a los elementos del modelo OData (como entidades, propiedades, conjuntos de entidades, etc.).34 Proporcionan información que va más allá de la estructura básica definida en el EDM, permitiendo a los clientes interpretar y utilizar los datos de manera más inteligente.

Propósito y Tipos (Vocabularios)

  • Propósito: Las anotaciones pueden describir:

  • Semántica: El significado de los datos (ej. si una propiedad es un número de teléfono, una dirección de correo electrónico, parte de una dirección).34

  • Capacidades: Qué operaciones están permitidas en un recurso (ej. si un conjunto de entidades es actualizable, insertable, requiere filtro).34

  • Presentación UI: Cómo deben mostrarse los datos en una interfaz de usuario (ej. qué campos mostrar en una tabla, en un formulario, etiquetas, formato).37

  • Validaciones y Comportamiento: Restricciones, valores predeterminados, campos obligatorios, etc.

  • Vocabularios: Las anotaciones se organizan en Vocabularios. Un vocabulario es un espacio de nombres (namespace) que define un conjunto de Términos (Terms). Cada anotación aplica un Término específico de un Vocabulario a un elemento del modelo OData.77

  • Términos: Un Término define el nombre, el tipo de dato esperado (primitivo, complejo, colección) y a qué tipo de elementos del modelo se puede aplicar.78

Vocabularios Comunes (Core, Capabilities, UI, Common, Communication)

OASIS y SAP han definido varios vocabularios estándar:

  • OASIS Vocabularies: 77

  • Org.OData.Core.V1 (Core): Términos fundamentales y descriptivos (ej. Core.Description, Core.OptimisticConcurrency, Core.IsLanguageDependent). Se usa para anotar otros vocabularios también.

  • Org.OData.Capabilities.V1 (Capabilities): Describe las capacidades de un servicio o recurso (ej. Capabilities.InsertRestrictions, Capabilities.UpdateRestrictions, Capabilities.DeleteRestrictions, Capabilities.FilterRestrictions, Capabilities.SortRestrictions, Capabilities.ExpandRestrictions).

  • Org.OData.Measures.V1 (Measures): Anota propiedades numéricas con unidades de medida (ej. Measures.Unit, Measures.ISOCurrency).

  • Org.OData.Validation.V1 (Validation): Define restricciones de validación (ej. Validation.Pattern, Validation.Maximum).

  • SAP Vocabularies: 77

  • com.sap.vocabularies.Common.v1 (Common): Términos semánticos comunes (ej. Common.Label para etiquetas legibles, Common.Text para asociar un texto descriptivo a un código, Common.ValueList para ayudas de búsqueda, Common.FieldControl para habilitar/deshabilitar/ocultar campos dinámicamente).79

  • com.sap.vocabularies.UI.v1 (UI): Define cómo deben presentarse los datos en interfaces de usuario, crucial para Fiori Elements (ej. UI.LineItem para columnas de tabla, UI.Identification y UI.FieldGroup para campos de formulario/objeto, UI.HeaderInfo para cabeceras de objeto, UI.SelectionFields para filtros, UI.Hidden para ocultar campos).37

  • com.sap.vocabularies.Communication.v1 (Communication): Describe información de contacto basada en vCard (ej. Communication.Contact, Communication.Address).78

  • Otros: Analytics, CodeList, PersonalData, Session, etc..79

Sintaxis (XML, CDS)

Las anotaciones se pueden expresar de diferentes maneras:

  • XML (en $metadata): El formato estándar de OData V4. Las anotaciones se añaden dentro de elementos <Annotations Target="...">, utilizando elementos <Annotation Term="...".../>.37 El valor de la anotación se especifica mediante atributos (String, Bool, Int, etc.) o elementos anidados (Collection, Record, PropertyValue).37 XML <Annotations Target="CatalogService.Books/title"> <Annotation Term="Common.Label" String="Book Title"/> </Annotations> <Annotations Target="CatalogService.Books"> <Annotation Term="UI.LineItem"> <Collection> <Record Type="UI.DataField"> <PropertyValue Property="Value" Path="title"/> </Record> </Collection> </Annotation> </Annotations> 37

  • CDS (Core Data Services): En el entorno SAP (especialmente S/4HANA y CAP), las anotaciones se definen directamente en las vistas CDS utilizando una sintaxis prefijada con @.37 El framework (CAP o SADL en ABAP) traduce estas anotaciones CDS a las anotaciones XML OData correspondientes en el $metadata generado.37 Fragmento de código annotate CatalogService.Books with { @Common.Label: 'Book Title' // Anotación en la propiedad 'title' title; }; annotate CatalogService.Books with @( UI: { // Anotación a nivel de entidad LineItem: [ {Value: title} ] } ); 37

Uso en SAP Fiori Elements

Las anotaciones, especialmente las del vocabulario UI, son la base de SAP Fiori Elements (anteriormente Smart Templates).11 Fiori Elements utiliza plantillas de UI (List Report, Object Page, etc.) que leen las anotaciones del $metadata del servicio OData para generar dinámicamente la interfaz de usuario con un mínimo de código de UI.11

  • @UI.LineItem define las columnas de una tabla en un List Report.37

  • @UI.SelectionFields define los campos del área de filtro.37

  • @UI.HeaderInfo, @UI.Identification, @UI.FieldGroup definen la estructura y contenido de una Object Page.37

  • @Common.Label, @Common.Text proporcionan las etiquetas de los campos.37

  • Las anotaciones de Capabilities controlan la visibilidad de botones como Crear, Editar, Eliminar.37

Por lo tanto, al desarrollar servicios OData para Fiori Elements, definir las anotaciones correctas en el backend (idealmente en las Vistas CDS) es tan importante como definir la estructura de datos misma.81

Consultas Delta ($deltatoken)

Las consultas delta son una característica avanzada (principalmente V4, aunque con implementaciones V2 en SAP) que permite a los clientes recuperar eficientemente solo los cambios (creaciones, actualizaciones, eliminaciones) que han ocurrido en una colección desde la última vez que se consultó.1 Son especialmente cruciales para escenarios de sincronización offline en aplicaciones móviles.14

Concepto y Casos de Uso (Offline Sync)

  • Flujo Básico:

  1. El cliente realiza una solicitud GET inicial a un conjunto de entidades habilitado para delta (puede incluir Prefer: odata.track-changes en la cabecera).22

  2. El servidor responde con el conjunto completo de datos y un delta link (que contiene un $deltatoken) en la respuesta (ej. en @odata.deltaLink).22 El $deltatoken es un valor opaco que representa el estado de la colección en ese momento.

  3. El cliente almacena los datos y el $deltatoken.

  4. En la siguiente solicitud de sincronización, el cliente realiza una solicitud GET utilizando la URI del delta link (que incluye ?$deltatoken=...).22

  5. El servidor procesa el $deltatoken y devuelve solo las entidades que han sido creadas, actualizadas o eliminadas desde que se emitió ese token.22 La respuesta también incluye un nuevo delta link con un nuevo $deltatoken para la siguiente sincronización.

  6. Las entidades eliminadas se indican a menudo mediante una construcción especial en la respuesta (ej. @odata.context apuntando a $metadata#Collection/$deletedEntity o una sección <deleted-entities> en V2).14

  • Beneficios: Reduce enormemente la cantidad de datos transferidos después de la sincronización inicial, mejora el rendimiento y conserva el ancho de banda.1 Esencial para aplicaciones móviles offline que necesitan mantener una copia local de los datos sincronizada con el backend.14

Implementación en SAP Gateway

SAP Gateway proporciona mecanismos para implementar el soporte de consultas delta, a menudo utilizando el "Delta Query Result Log" o interactuando con el framework ODP (Operational Data Provisioning).

  • Habilitación: La entidad debe estar marcada como compatible con delta en SEGW o mediante anotaciones CDS.

  • Método DPC: La lógica principal reside en el método /IWBEP/IF_MGW_APPL_SRV_RUNTIME~GET_ENTITYSET_DELTA de la clase DPC_EXT.22 Este método se llama cuando la solicitud incluye un $deltatoken.

  • Lógica (GET_ENTITYSET_DELTA):

  1. Recuperar el $deltatoken de la solicitud (vía io_tech_request_context).

  2. Consultar el sistema backend o el log de cambios para identificar las entidades creadas, modificadas y eliminadas desde el estado representado por el token.

  3. Uso de /IWBEP/CL_QUERY_RESULT_LOG=>CREATE_UPDATE_LOG_ENTRY_HASH: Este método (o similar) a menudo se utiliza para comparar el estado actual de los datos con un estado anterior almacenado (basado en hashes) y determinar los cambios.22 Requiere pasar la tabla de datos actual (it_entityset), el contexto técnico (io_tech_request_context), la fachada del proveedor de datos (io_dp_facade), etc. Devuelve las entidades cambiadas (et_entityset), las entidades eliminadas (et_deleted_entityset) y el nuevo token delta (ev_delta_token).22

  4. Exportar Resultados:

  • Asignar el nuevo ev_delta_token a es_response_context-deltatoken.22

  • Asignar las entidades eliminadas (et_deleted_entityset) a er_deleted_entityset usando copy_data_to_ref.22

  • Asignar las entidades creadas/modificadas (et_entityset) a er_entityset usando copy_data_to_ref.22

  • ODP (Operational Data Provisioning): Para fuentes de datos como Extractores BW o Vistas CDS habilitadas para delta, Gateway puede generar servicios OData que exponen la funcionalidad delta de ODP.85 Estos servicios generados a menudo tienen entidades separadas para los datos (FactsOf..., AttrOf...) y para los delta links (DeltaLinksOf...).85 La solicitud inicial requiere la cabecera Prefer: odata.track-changes.85 Las solicitudes delta usan la URI de DeltaLinksOf... con el token.86

La implementación correcta de estas características avanzadas permite construir soluciones OData más potentes, eficientes y robustas, especialmente en el complejo entorno empresarial de SAP.

Last updated