SAP ABAP Tutorials and Materials, SAP ABAP Certifications, SAP ABAP Live

ABAP Programming Model for SAP Fiori

Part 1 – OData service with existing tables, DDIC data source, referenced CDS view data source and custom action

Let’s assume that you have an existing custom OData service, which is to be extended with a referenced CDS view data source, and a custom action (i.e. function import). All tables already exist.

DDIC data source

Create the ‘existing’ OData service:

  1. SEGW, create new project ‘Zx_SADL_PART_1’
  2. Add entity type ‘Customers’
    1. Right click ‘Data Model’ / Import / DDIC Structure
      1. Name = Customers
      2. ABAP Structure = SCUSTOM
      3. Select properties = [ID, NAME, CITY, EMAIL]
      4. Key = ID
    2. Optional: change type of property ‘Id’ to ‘Edm.Decimal’ with precision = 8 and ignore the warning/error
  3. Map implementation of CustomerSet to DDIC data source ‘SCUSTOM’
    1. Right click service implementation / CustomerSet / Map to Data Source / Type = Business Entity / Launch value help on ‘Name’ (F4)
    2. SADL Model Type = ‘DDIC’, SADL Model = ‘SCUSTOM’
  4. Generate, register and test the OData service
    1. /sap/opu/odata/sap/Zx_SADL_PART_1_SRV/CustomersSet?$filter=Email eq ’’&$orderby=Id desc&$top=20
    2. Optional: try to update or create an entity, and notice how the response is ‘The requested service is not supported by entity ~SCUSTOM’

CDS interface view

Extend the existing OData service with a referenced CDS view data source.

Create the base CDS interface view:

  1. Launch Eclipse on perspective ABAP, connect to your back-end system
  2. Create interface (aka. base) view for carrier (i.e. airline) data
    1. Right click project / New / Other… / Data Definition
    2. Name = ‘Zx_I_SADL_PART_1_CARR’, Description = ‘CARR interface view for Zx_SADL’
      Note: ‘_I_’ is for interface view
    3. Template = ‘Define View’
    4. Change to:
      @AbapCatalog.sqlViewName: ‘zx_i_carr1’
      @AbapCatalog.compiler.compareFilter: true
      @AccessControl.authorizationCheck: #NOT_REQUIRED
      @EndUserText.label: ‘CARR interface view for Zx_SADL’
      @VDM.viewType: #BASIC
      define view Zx_I_SADL_PART_1_CARR
      as select from scarr as ca
      {
      key ca.carrid,
      ca.carrname,
      ca.currcode,
      ca.url
      }
    5. Activate
  3. Create interface view for flight data
    1. New Data Definition, Name = ‘Zx_I_SADL_PART_1_FLI’, Description = ‘FLIGHT interface view for Zx_SADL’, template = ‘Define View’
    2. Change to:
      @AbapCatalog.sqlViewName: ‘zx_i_fli1’
      @AbapCatalog.compiler.compareFilter: true
      @AccessControl.authorizationCheck: #NOT_REQUIRED
      @EndUserText.label: ‘FLIGHT interface view for Zx_SADL’
      @VDM.viewType: #BASIC
      define view ZX_I_SADL_PART_1_FLI
      as select from sflight as fl
      {
      key fl.carrid,
      key fl.connid,
      key fl.fldate,
      fl.planetype,
      fl.price,
      fl.currency,
      fl.seatsmax
      }​
    3. Activate
  4. Create associations between carriers and flights
    1. Change ‘Zx_I_SADL_PART_1_CARR’ to:
      define view Zx_I_SADL_PART_1_CARR
      as select from scarr as ca
      association [0..*] to Zx_I_SADL_PART_1_FLI as _fl on $projection.carrid = _fl.carrid
      {
      key ca.carrid,
      ca.carrname,
      ca.url,
      /* Associations */
      _fl
      }​
    2. Change ‘Zx_I_SADL_PART_1_FLI’ to:
      as select from sflight as fl
      association [1..1] to Zx_I_SADL_PART_1_CARR as _ca on $projection.carrid = _ca.carrid
      {
      […]
      fl.seatsmax,
      /* Associations */
      _ca
      }​
    3. Visualize the entities
      1. Right click DDL data definition / Open With / Graphical Editor

Business object annotations (BOPF)

Add business object (BO) @ObjectModel annotations for BOPF:

  1. Change ‘Zx_I_SADL_PART_1_CARR’ to:
    @EndUserText.label: ‘CARR interface view for Zx_SADL’
    @ObjectModel: {
    compositionRoot: true,
    modelCategory: #BUSINESS_OBJECT,
    representativeKey: ‘carrid’,
    semanticKey: [ ‘carrid’ ],
    transactionalProcessingEnabled: true,
    writeActivePersistence: ‘scarr’,
    createEnabled: false,
    updateEnabled: true,
    deleteEnabled: false
    }
    […]
    ca.url,
    /* Associations */
    @ObjectModel.association.type: [#TO_COMPOSITION_CHILD]
    _fl
    }​
  2. Change ‘Zx_I_SADL_PART_1_FLI’ to:
    @EndUserText.label: ‘FLIGHT interface view for Zx_SADL’
    @ObjectModel: {
    representativeKey: ‘fldate’,
    semanticKey: [ ‘carrid’, ‘connid’, ‘fldate’ ],
    writeActivePersistence: ‘sflight’,
    createEnabled: true,
    deleteEnabled: true,
    updateEnabled: true
    }
    […]
    $projection.carrid = _ca.carrid
    {
    @ObjectModel.readOnly: true
    @ObjectModel.foreignKey.association: ‘_ca’
    key fl.carrid,
    […]
    /* Associations */
    @ObjectModel.association.type: [#TO_COMPOSITION_ROOT, #TO_COMPOSITION_PARENT]
    _ca
    }​
  3. Activate both views in one step
  4. Observe the generated artifacts
    1. SAP GUI / BOBX
      1. Find and open new local business object ‘Zx_I_SADL_PART_1_CARR’
      2. Observe the node structure and the actions of the nodes
      3. Test the business object: F8
        1. Load node instance by alternative key ‘*_CARR – DB KEY’ = ‘AA’
        2. Navigate the CARR object to its flights through the association
    2. Test the CDS views, follow association
      1. Right click CDS view definition of Zx_I_SADL_PART_1_CARR in Eclipse / Open With / Data Preview
      2. Place cursor on ‘carrid’ = ‘AA’, click triangle after ‘Zx_I_SADL_PART_1_CARR’ in title to get a list of associations, navigate to ‘_fl’
    3. Observe the list of flights retrieved via the association

Custom business object action

Define a new action (i.e. function import) ‘ZTOGL_URL_SLASH’:

  1. BOBX, open Zx_I_SADL_PART_1_CARR, enter ‘Edit’ mode
  2. New action on Zx_I_SADL_PART_1_CARR
    1. Action Name = ‘ZTOGL_URL_SLASH’
    2. Description = ‘Toggle slash at end of URL’
    3. Action cardinality = ‘Single Node Instance’
    4. Implementing Class = ‘ZCL_x_I_SADL_PART_1’
    5. Exporting Parameter = ‘Node’
    6. Exporting Parameter BO = ‘Zx_I_SADL_PART_1_CARR’
    7. Exporting Parameter Node = ‘Zx_I_SADL_PART_1_CARR’
    8. Exporting Cardinality = ‘1’
    9. Save
    10. Double click implementing class, create it
    11. Activate the implementing class without changes, (re)generate the business object
  3. Implement action ‘ZTOGL_URL_SLASH’
    1. Edit ‘ZCL_x_I_SADL_PART_1’ (in SE80 / Eclipse)
    2. Change superclass to ‘/BOBF/CL_LIB_A_SUPERCL_SIMPLE’, don’t keep redefinitions
    3. Description = ‘Action class for BO node Zx_I_SADL_PART_1_CARR’
    4. Look up the ‘Constants Interface’ of node ‘Zx_I_SADL_PART_1_CARR’ in BOBX: ‘ZIF_x_I_SADL_PART_1_CAR_C’
    5. Redefine ‘/BOBF/IF_FRW_ACTION~EXECUTE’:

CASE is_ctx-act_key.
WHEN zif_x_i_sadl_part_1_car_c=>sc_action-zx_i_sadl_part_1_carr-ztogl_url_slash.
” Declare output table of Action
DATA lt_carr TYPE ztx_i_sadl_part_1_carr.

” Read input carrier instance
io_read->retrieve(
EXPORTING
iv_node = is_ctx-node_key ” Node Name
it_key = it_key ” Key Table
IMPORTING
et_data = lt_carr ” Data Return Structure
).

” Assuming single instance for the action
READ TABLE lt_carr ASSIGNING FIELD-SYMBOL(<fs_carr>) INDEX 1.
IF sy-subrc = 0.
” Make changes to the business object:
” * Set new URL
IF substring( val = <fs_carr>-url off = numofchar( <fs_carr>-url ) – 1 len = 1 ) EQ ‘/’.
<fs_carr>-url = substring( val = <fs_carr>-url off = 0 len = numofchar( <fs_carr>-url ) – 1 ).
ELSE.
<fs_carr>-url = |{ <fs_carr>-url }/|.
ENDIF.

” Now update the BO instance
io_modify->update(
EXPORTING
iv_node = is_ctx-node_key ” Node
iv_key = <fs_carr>-key ” Key
iv_root_key = <fs_carr>-root_key ” NodeID
is_data = REF #( <fs_carr>-node_data )” Data
it_changed_fields = VALUE #( ( zif_x_i_sadl_part_1_car_c=>sc_node_attribute-zx_i_sadl_part_1_carr-url ) ) ).

et_data = lt_carr.
ENDIF.
WHEN OTHERS.
ENDCASE.​

6. Activate

  • Test action ‘ZTOGL_URL_SLASH’:
    • BOBX / Test (F8) / Load node instance by alternative key ‘*_CARR – DB KEY’ = ‘AA’
    • Select CARRID ‘AA’, Execute Action ‘ZTOGL_URL_SLASH’
    • Observe how a trailing slash ‘/’ appears and disappears
    • You can save, or discard the changes made in the test suite

CDS consumption view

Create the CDS consumption view that is to be consumed by the referenced CDS view data source:

  1. Create carrier (airline) consumption view
    1. Eclipse / New Data Definition: Name = ‘Zx_C_SADL_PART_1_CARR’, Description = ‘CARRIER consumption view for Zx_SADL’, template = ‘Define View’
      Note: ‘_C_’ is for consumption view
    2. Change to:
      @AbapCatalog.sqlViewName: ‘zx_c_carr1’
      @AbapCatalog.compiler.compareFilter: true
      @AccessControl.authorizationCheck: #NOT_REQUIRED
      @EndUserText.label: ‘CARRIER consumption view for Zx_SADL’
      @ObjectModel: {
      compositionRoot: true,
      transactionalProcessingDelegated: true
  2. updateEnabled: true
    }
    @UI: {
    headerInfo: { typeNamePlural: ‘Airlines’ }
    }
    @VDM.viewType: #CONSUMPTION
    define view Zx_C_SADL_PART_1_CARR
    as select from Zx_I_SADL_PART_1_CARR as ca
    association [0..*] to Zx_C_SADL_PART_1_FLI as _fl on $projection.carrid = _fl.carrid
    {
    key ca.carrid,
    @EndUserText.label: ‘Airline Name’
    ca.carrname as CarrierName,
    @Semantics.url: true
    ca.url,
    /* Associations */
    _fl
    }​

    • Note how field aliases (‘as’), @Semantics and @UI annotations are placed into the consumption view
    • Note how e.g. ‘updateEnabled’ is repeated. These annotations affect both the SADL and the BOPF level. Annotations on the interface view enforce the requested behavior. Annotations on the consumption view control the entity set metadata. Always keep these annotations in sync.
  3. Create flight consumption view
    1. Eclipse / New Data Definition: Name = ‘Zx_C_SADL_PART_1_FLI’, Description = ‘FLIGHT consumption view for Zx_SADL’, template = ‘Define View’
    2. Change to:
      @AbapCatalog.sqlViewName: ‘zx_c_fli1’
      @AbapCatalog.compiler.compareFilter: true
      @AccessControl.authorizationCheck: #NOT_REQUIRED
      @EndUserText.label: ‘FLIGHT consumption view for Zx_SADL’
      @ObjectModel: {
      createEnabled: true,
      updateEnabled: true,
      deleteEnabled: true
      }
      @VDM.viewType: #CONSUMPTION
      define view Zx_C_SADL_PART_1_FLI
      as select from Zx_I_SADL_PART_1_FLI as fl
      association [1..1] to Zx_C_SADL_PART_1_CARR as _ca on $projection.carrid = _ca.carrid
      {
      key fl.carrid,
      key fl.connid,
      key fl.fldate,
      @EndUserText.label: ‘Airplante Type’
      fl.planetype,
      @Semantics.amount.currencyCode: ‘CurrencyCode’
      fl.price,
      @Semantics.currencyCode: true
      fl.currency as CurrencyCode,
      fl.seatsmax,
      /* Association */
      _ca
      }​
    3. Activate both views at the same time
    4. Test the consumption views in Eclipse with data preview

Referenced CDS consumption view data source

Extend existing OData service by referencing the new CDS consumption view:

  1. SEGW / open project ‘Zx_SADL_PART_1’
  2. Right click ‘Data Model’ / ‘Reference’ / ‘Data Source’ / CDS-Entity = ‘Zx_C_SADL_PART_1_CARR’ / Next
    1. Check in all the check boxes in ‘Selected’ column, Finish
  3. Observe and explore the new ‘Data Source References’ node
    1. Note how ‘…FLIType.connid’ is type ‘Edm.String’, instead of the best-practices ‘Edm.Decimal’
    2. Check out the permitted operations of the ‘Entity Sets’
    3. Note how the BO action ‘ZTOGL_URL_SLASH’ is exposed as a ‘Function Import’, even though this is not requested anywhere in the CDS
  4. Save and generate the project

Test the OData service. Launch /IWFND/GW_CLIENT or a REST client e.g. Postman:

  1. GET /sap/opu/odata/sap/Zx_SADL_PART_1_SRV/$metadata?sap-ds-debug=true
    1. Postman: retrieve the CSRF token with:
      1. Header ‘x-csrf-token’ = ‘Fetch’
  2. Get carrier ‘AA’
    1. GET /sap/opu/odata/sap/Zx_SADL_PART_1_SRV/Zx_C_SADL_PART_1_CARR?$filter=carrid eq ‘AA’
      1. Use header ‘Accept’ = ‘application/json’
  3. Follow navigation properties
    1. GET /sap/opu/odata/sap/Zx_SADL_PART_1_SRV/Zx_C_SADL_PART_1_CARR(‘AA’)/to_fl?$top=3
    2. GET /sap/opu/odata/sap/Zx_C_SADL_PART_2_CARR_CDS/Zx_C_SADL_PART_2_FLI(carrid=’AA’,connid=’0017′,fldate=datetime’2016-08-17T00%3A00%3A00′)/to_ca
  4. Note down the function import name, e.g. ‘ACC0323FA175F5649EE81C514Ztogl_url_slash’
  5. Call function import
    1. POST /sap/opu/odata/sap/Zx_SADL_PART_1_SRV/…Ztogl_url_slash?carrid=’AA’
    2. Get carrier ‘AA’ again
  6. Update ‘url’ of carrier ‘AA’ (using PATCH or MERGE if available)
    1. PATCH /sap/opu/odata/sap/Zx_SADL_PART_1_SRV/Zx_C_SADL_PART_1_CARR(‘AA’)
      1. Header ‘Content-Type’ = ‘application/json’
      2. Body: copy from GET above, change the URL, e.g.:
        {
        “carrid”: “AA”,
        “CarrierName”: “American Airlines”,
        “url”: “http://www.aa.com/”
        }​
      3. Status 204 ‘No Content’ indicates success
  7. Attempt to create new airline ‘ZZ’
  8. POST /sap/opu/odata/sap/Zx_SADL_PART_1_SRV/Zx_C_SADL_PART_1_CARR
    1. Header ‘Content-Type’ = ‘application/json’
    2. Body: copy from GET above, change the URL, e.g.:
      {
      “carrid”: “ZZ”,
      “CarrierName”: “Zambian Zoom”,
      “url”: “http://www.zz.com”
      }​
    3. The request fails with status 500 with message ‘The ASSERT condition was violated’: create is not allowed on the BOPF level.
      1. Note: in case e.g. create is allowed on the BOPF level (interface view) but isn’t allowed on the OData metadata level (consumption view), entity creation will succeed.

Part 2 – CDS-BOPF-OData service exposure with @OData.publish: true

In this solution no SEGW gateway project is used. The CDS view is edited in Eclipse.

  1. Eclipse / ABAP perspective / Right click project / New / Other… / Data Definition
    1. Create (or reuse) interface CDS views ‘Zx_I_SADL_PART_1_CARR’ and ‘Zx_I_SADL_PART_1_FLI’ from the section above
    2. Add (or reuse) custom action ‘ZTOGL_URL_SLASH’ to BO ‘Zx_I_SADL_PART_1_CARR’ from the section above
    3. Create new consumption views ‘Zx_C_SADL_PART_2_CARR’ and ‘Zx_C_SADL_PART_1_FLI’ as below:
      1. ‘Zx_C_SADL_PART_2_CARR’: description ‘CARRIER consumption view for @OData.publish: true’
        1. Create it like ‘Zx_C_SADL_PART_1_CARR’ above
        2. @AbapCatalog.sqlViewName: ‘zx_c_carr2’
        3. Association to ‘Zx_C_SADL_PART_2_FLI’
        4. Add view-level annotation ‘@OData.publish: true’
      2. ‘Zx_C_SADL_PART_2_FLI’: description ‘FLIGHT consumption view for @OData.publish: true’
        1. Create it like ‘Zx_C_SADL_PART_1_FLI’ above
      3. @AbapCatalog.sqlViewName: ‘zx_c_fli2’
      4. Association to ‘Zx_C_SADL_PART_2_CARR’
  2. Activate consumption views together
  3. Note the warning on the line with ‘@OData.publish: true’: “Service … is not active”
  4. Go to ‘/IWFND/MAINT_SERVICE’ and add service ‘Zx_C_SADL_PART_2_CARR’
    1. System alias = LOCAL

Test the OData service ‘/sap/opu/odata/sap/Zx_C_SADL_PART_2_CARR_CDS’:

  1. GET /sap/opu/odata/sap/Zx_C_SADL_PART_2_CARR_CDS/$metadata?sap-ds-debug=true
    1. Postman: retrieve the CSRF token with:
      1. Header ‘x-csrf-token’ = ‘Fetch’
  2. Continue the test like for ‘Zx_SADL_PART_1_SRV’ above
    1. GET /sap/opu/odata/sap/Zx_C_SADL_PART_2_CARR_CDS/Zx_C_SADL_PART_2_CARR?$filter=carrid eq ‘AA’
    2. POST /sap/opu/odata/sap/Zx_C_SADL_PART_2_CARR_CDS/A5393E1DDD898C21B108F25FCZtogl_url_slash?carrid=’AA’
    3. PATCH /sap/opu/odata/sap/Zx_C_SADL_PART_2_CARR_CDS/Zx_C_SADL_PART_2_CARR(‘AA’)
    4. POST /sap/opu/odata/sap/Zx_C_SADL_PART_2_CARR_CDS/Zx_C_SADL_PART_2_CARR

Leave a Reply

Your email address will not be published. Required fields are marked *