Introduction
OData (Open Data Protocol) services are widely used in SAP to expose data and business logic to external applications in a standardized way. While OData provides metadata for entities and properties, custom annotations can add additional context or metadata to enhance client applications. This blog demonstrates how to enhance an OData service with custom annotations, providing a complete explanation, coding steps, and expected output.
What are Custom Annotations?
Custom annotations are user-defined metadata extensions added to the standard metadata of an OData service. They provide additional information about entities, properties, or operations, enabling client applications to:
- Understand the context and rules of data.
- Enhance the UI dynamically based on backend-provided information.
- Implement application-specific logic seamlessly.
In SAP, annotations are often used to control aspects such as field visibility, data validation, or dynamic behavior, offering greater flexibility for client applications.
Scenario Overview
In this example, we will enhance an OData service to add a custom annotation that specifies the visibility of certain fields based on the user role. This annotation dynamically informs the client whether a field should be displayed.
The visibility of these fields will vary depending on the user’s role. For instance:
- Admin Role: All fields are visible.
- Viewer Role: Only Product ID, Product Name and Category are visible.
- Restricted Role: No fields are visible.
Steps to Enhance OData Service
Step 1: Create an OData Service
Create a new project in SEGW (Transaction: SEGW)
- Project Name: ZODATA_CUSTOM_ANN
- Description: OData Service with Custom Annotations
Define the Data Model:
- Entity Type: Product
Properties: Productid, ProductName, Category, Price
- Entity Set: Products
Step 2: Implement the Data Provider Class (DPC)
Redefine the GET_ENTITYSET method in the DPC_EXT class to provide data.
METHOD zproductset_get_entityset.
DATA: lt_products TYPE TABLE OF zrv_t_product,
ls_product TYPE zrv_t_product.
" Sample data
ls_product-productid = 'P001'.
ls_product-productname = 'Laptop'.
ls_product-category = 'Electronics'.
ls_product-price = '1500.00'.
APPEND ls_product TO lt_products.
ls_product-productid = 'P002'.
ls_product-productname = 'Table'.
ls_product-category = 'Furniture'.
ls_product-price = '500.00'.
APPEND ls_product TO lt_products.
" Transfer data to the framework
copy_data_to_ref( EXPORTING is_data = lt_products
CHANGING cr_data = et_entityset ).
ENDMETHOD.
Step 3: Define Custom Annotations.
Redefine the DEFINE method in the MPC_EXT class to add custom annotations.
method DEFINE.
SUPER->define( ).
" Access the entity type
data(lo_entity_type) = model->get_entity_type( iv_entity_name = 'ZProduct' ).
" Add a custom annotation
DATA(lo_annotation) = lo_entity_type->/iwbep/if_mgw_odata_annotatabl~create_annotation( iv_annotation_namespace = 'Custom.Visibility' ).
lo_annotation->add( iv_key = 'FieldVisibility'
iv_value = 'Dynamic based on user role' ).
" Assign annotation to a property
DATA(lo_property) = lo_entity_type->get_property( iv_property_name = 'Price' ).
lo_property->/iwbep/if_mgw_odata_annotatabl~create_annotation( iv_annotation_namespace = lo_annotation ).
endmethod.
In this step, we define a custom annotation called Custom Visibility to specify visibility rules based on the user's role. We then assign this annotation to the Price property.
Step 4: Add Dynamic Behavior
Modify the GET_ENTITYSET method in the DPC_EXT class to provide dynamic annotations based on user roles.
METHOD zproductset_get_entityset.
DATA: lt_products TYPE TABLE OF zrv_t_product,
ls_product TYPE zrv_t_product,
lv_user_role TYPE string.
" Determine user role (fetch from user context or session)
lv_user_role = me->get_user_role( ).
" Sample data
ls_product-productid = 'P001'.
ls_product-productname = 'Laptop'.
ls_product-category = 'Electronics'.
ls_product-price = '1500.00'.
APPEND ls_product TO lt_products.
ls_product-productid = 'P002'.
ls_product-productname = 'Table'.
ls_product-category = 'Furniture'.
ls_product-price = '500.00'.
APPEND ls_product TO lt_products.
" Apply role-based filtering
LOOP AT lt_products ASSIGNING FIELD-SYMBOL(<ls_product>).
CASE lv_user_role.
WHEN 'Admin'.
" Admin sees all fields
CONTINUE.
WHEN 'Viewer'.
CLEAR: <ls_product>-price.
WHEN OTHERS.
CLEAR: <ls_product>.
ENDCASE.
ENDLOOP.
" Transfer data to the framework
copy_data_to_ref( EXPORTING is_data = lt_products
CHANGING cr_data = et_entityset ).
ENDMETHOD.
In this method, we dynamically filter out fields based on the user's role. For instance, the Viewer role will only see Productid, ProductName and Category while the Restricted role will not see any product data.
Expected Output
Once the service is implemented and tested, the following behavior should be observed:
Metadata with Custom Annotation
The $metadata document will include custom annotations as follows:
Dynamic Data Behavior
Admin Role:
All fields (Productid, ProductName, Category, and Price) will be visible.
For a Viewer Role:
Product Price will be hidden.
Restricted Role:
No fields will be visible.
Conclusion
Custom annotations in OData services provide a powerful way to enhance metadata, allowing you to define specific behaviors and rules that can be utilized by client applications. In this blog, we demonstrated how to implement custom annotations to dynamically control field visibility based on the user's role. By using the power of custom annotations, you can tailor your OData service to meet specific business needs and provide a more personalized user experience.
This approach can be extended to other use cases such as data validation, dynamic formatting, and even triggering specific actions based on user roles.