This post talks about adding fields to existing custom services in ABAP RESTful Application Programming. This will help you understand the steps that are required to add more fields without missing any steps.
This post refers to a RAP service created in the earlier post – Managed with unmanaged save.
Let us add two fields to the carrier entity.
- Data Complete – We will set this field if the name and currency are entered. Note that carrier id will always be available.
- The number of connections – This will be the number of connections created for the selected carrier.
The steps that we need to follow are as below
- Data Dictionary – Add fields to the base table and draft table. If the draft is not enabled, then the draft part can be ignored.
- CDS Entities – Add fields to the CDS Entity and also at the consumption/projection CDS Entity.
- Metadata Extension – Add fields in the metadata extension based on where the fields need to appear.
- Behavior Definition – Add fields in behavior definition mapping, also add field level code in case any properties like read-only need to be set up
- Update Save Method – This is required in case of unmanaged scenarios or managed scenarios with unmanaged save.
1. Add fields to the base table zjp_carrier
and draft table zjp_d_carrier
@EndUserText.label : 'Flight Reference Scenario: Carrier'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zjp_carrier {
key client : abap.clnt not null;
key carrier_id : /dmo/carrier_id not null;
name : /dmo/carrier_name;
currency_code : /dmo/currency_code;
is_complete : abap.char(1);
no_of_connections : abap.int1;
local_created_by : abp_creation_user;
local_created_at : abp_creation_tstmpl;
local_last_changed_by : abp_locinst_lastchange_user;
local_last_changed_at : abp_locinst_lastchange_tstmpl;
last_changed_at : abp_lastchange_tstmpl;
}
The draft table has fields matching with CDS Entities.
@EndUserText.label : 'Draft table for entity ZJP_I_CARRIER'
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zjp_d_carrier {
key mandt : mandt not null;
key carrierid : /dmo/carrier_id not null;
name : /dmo/carrier_name;
currencycode : /dmo/currency_code;
iscomplete : abap.char(1);
noofconnections : abap.int1;
localcreatedby : abp_creation_user;
localcreatedat : abp_creation_tstmpl;
locallastchangedby : abp_locinst_lastchange_user;
locallastchangedat : abp_locinst_lastchange_tstmpl;
lastchangedat : abp_lastchange_tstmpl;
"%admin" : include sych_bdl_draft_admin_inc;
}
2. Add the fields to CDS Entities ZJP_I_CARRIER and ZJP_C_CARRIER
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Carrier Entity'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define root view entity ZJP_I_CARRIER
as select from zjp_carrier
composition [0..*] of zjp_i_connection as _Connection
{
key carrier_id as CarrierId,
name as Name,
currency_code as CurrencyCode,
is_complete as IsComplete,
no_of_connections as NoOfConnections,
@Semantics.user.createdBy: true
local_created_by as LocalCreatedBy,
@Semantics.systemDateTime.createdAt: true
local_created_at as LocalCreatedAt,
@Semantics.user.lastChangedBy: true
local_last_changed_by as LocalLastChangedBy,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
local_last_changed_at as LocalLastChangedAt,
@Semantics.systemDateTime.lastChangedAt: true
last_changed_at as LastChangedAt,
_Connection
}
@AccessControl.authorizationCheck: #NOT_REQUIRED
@Metadata.allowExtensions: true
@EndUserText.label: 'Projection Entity for Carrier'
@ObjectModel.semanticKey: [ 'CarrierId' ]
@Search.searchable: true
define root view entity ZJP_C_CARRIER
provider contract transactional_query
as projection on ZJP_I_CARRIER
{
key CarrierId,
@Search.defaultSearchElement: true
Name,
CurrencyCode,
IsComplete,
NoOfConnections,
LocalCreatedBy,
LocalCreatedAt,
LocalLastChangedBy,
LocalLastChangedAt,
LastChangedAt,
/* Associations */
_Connection : redirected to composition child zjp_c_connection
}
3. Add the fields to the metadata extension ZJP_C_CARRIER
@Metadata.layer: #CORE
@UI: {
headerInfo: {
typeName: 'Carrier',
typeNamePlural: 'Carrier',
title: {
type: #STANDARD, value: 'Name'
},
description: {
value: 'CarrierID'
}
},
presentationVariant: [{
sortOrder: [{
by: 'Name',
direction: #ASC
}],
visualizations: [{
type: #AS_LINEITEM
}]
}]
}
annotate view ZJP_C_CARRIER with
{
@UI.facet: [
{
label: 'Carrier Details',
id: 'CarrierInfo',
type: #COLLECTION,
position: 10
},
{
label: 'Carrier',
id: 'Carrier',
type: #IDENTIFICATION_REFERENCE,
purpose: #STANDARD,
parentId: 'CarrierInfo',
position: 10
},
{
id: 'Connection',
purpose: #STANDARD,
type: #LINEITEM_REFERENCE,
label: 'Connections',
position: 20,
targetElement: '_Connection'
}
]
@UI.lineItem: [{ position: 10 }]
@UI.identification: [{ position: 10 }]
CarrierId;
@UI.lineItem: [{ position: 20 }]
@UI.identification: [{ position: 20 }]
Name;
@UI.lineItem: [{ position: 30 }]
@UI.identification: [{ position: 30 }]
CurrencyCode;
@UI.lineItem: [{ position: 32 }]
@UI.identification: [{ position: 32 }]
@EndUserText.label: 'Info. complete?'
IsComplete;
@UI.lineItem: [{ position: 34 }]
@UI.identification: [{ position: 34 }]
@EndUserText.label: 'No. of Connections'
NoOfConnections;
@UI.lineItem: [{ position: 40 }]
@UI.identification: [{ position: 40 }]
LocalCreatedBy;
@UI.lineItem: [{ position: 50 }]
@UI.identification: [{ position: 50 }]
LocalCreatedAt;
@UI.lineItem: [{ position: 60 }]
@UI.identification: [{ position: 60 }]
LocalLastChangedBy;
@UI.lineItem: [{ position: 70 }]
@UI.identification: [{ position: 70 }]
LocalLastChangedAt;
@UI.lineItem: [{ position: 80 }]
@UI.identification: [{ position: 80 }]
LastChangedAt;
}
4. Add the fields to the Behavior definition ZJP_I_CARRIER mapping
...
mapping for zjp_carrier control zjp_x_carrier corresponding
{
CarrierId = carrier_id;
Name = name;
CurrencyCode = currency_code;
IsComplete = is_complete;
NoOfConnections = no_of_connections;
LocalCreatedBy = local_created_by;
LocalCreatedAt = local_created_at;
LocalLastChangedBy = local_last_changed_by;
LocalLastChangedAt = local_last_changed_at;
LastChangedAt = last_changed_at;
}
...
Additionally, the control structure zjp_x_carrier should also have these two new fields. This step may not be needed for read-only fields.
zjp_x_carrier
@EndUserText.label : 'zjp_x_carrier'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
define structure zjp_x_carrier {
carrier_id : xsdboolean;
name : xsdboolean;
currency_code : xsdboolean;
is_complete : xsdboolean;
no_of_connections : xsdboolean;
local_created_by : xsdboolean;
local_created_at : xsdboolean;
local_last_changed_by : xsdboolean;
local_last_changed_at : xsdboolean;
last_changed_at : xsdboolean;
}
5. Add fields in the save_modified method
Go to the class zbp_jp_i_carrier from the behavior definition and locate the method save_modified
in Local Types. If you miss this step, the changes will not be saved.
METHOD save_modified.
DATA : carriers TYPE STANDARD TABLE OF zjp_carrier,
carriers_u TYPE STANDARD TABLE OF zjp_carrier,
controls TYPE STANDARD TABLE OF zjp_x_carrier,
lr_carriers TYPE RANGE OF zjp_carrier-carrier_id.
"Implement Logic for all possible Save Operations - Create / Update / Delete
IF create IS NOT INITIAL.
carriers = CORRESPONDING #( create-carrier MAPPING FROM ENTITY ).
INSERT zjp_carrier FROM TABLE @carriers.
ENDIF.
IF update IS NOT INITIAL.
carriers = CORRESPONDING #( update-carrier MAPPING FROM ENTITY ).
controls = CORRESPONDING #( update-carrier MAPPING FROM ENTITY USING CONTROL ).
SELECT * FROM zjp_carrier
FOR ALL ENTRIES IN @carriers
WHERE carrier_id = @carriers-carrier_id
INTO TABLE @DATA(carriers_o).
IF sy-subrc EQ 0.
carriers_u =
VALUE #(
FOR i = 1 WHILE i LE lines( carriers )
LET
control =
VALUE #( controls[ i ] OPTIONAL )
carrier =
VALUE #( carriers[ i ] OPTIONAL )
carrier_o =
VALUE #( carriers_o[ carrier_id = carrier-carrier_id ] OPTIONAL )
IN
( carrier_id = carrier-carrier_id
name = COND #( WHEN control-name IS NOT INITIAL
THEN carrier-name
ELSE carrier_o-name )
currency_code = COND #( WHEN control-currency_code IS NOT INITIAL
THEN carrier-currency_code
ELSE carrier_o-currency_code )
is_complete = COND #( WHEN control-is_complete IS NOT INITIAL
THEN carrier-is_complete
ELSE carrier_o-is_complete )
no_of_connections = COND #( WHEN control-no_of_connections IS NOT INITIAL
THEN carrier-no_of_connections
ELSE carrier_o-no_of_connections )
local_created_by = carrier_o-local_created_by
local_created_at = carrier_o-local_created_at
local_last_changed_by = COND #( WHEN control-local_last_changed_by IS NOT INITIAL
THEN carrier-local_last_changed_by
ELSE carrier_o-local_last_changed_by )
local_last_changed_at = COND #( WHEN control-local_last_changed_at IS NOT INITIAL
THEN carrier-local_last_changed_at
ELSE carrier_o-local_last_changed_at )
last_changed_at = COND #( WHEN control-last_changed_at IS NOT INITIAL
THEN carrier-last_changed_at
ELSE carrier_o-last_changed_at )
) ).
ELSE.
carriers_u = carriers.
ENDIF.
UPDATE zjp_carrier FROM TABLE @carriers_u.
ENDIF.
IF delete IS NOT INITIAL.
lr_carriers = VALUE #( FOR ls_carrier_id IN delete-carrier
( sign = 'I' option ='EQ' low = ls_carrier_id-carrierid )
).
DELETE FROM zjp_carrier WHERE carrier_id IN @lr_carriers.
ENDIF.
ENDMETHOD.
Test the application
Once everything is active the fields should be displayed on the application even though the values are blank.
This way, fields can be added to existing custom services. The next post will talk about how these fields can be determined using behavior definition determinations.
Visit ABAP RESTful Application Programming Model to explore all articles on ABAP RAP Model.
If you like the content, please subscribe…