In the Blog, we will understand how to set up Joule through BTP account, Connect to an On-Premise API, build a capability with the help of Joule and Integrate the bot to your application.
Create a Sample Application
Let’s create a sample RAP based application with a header table and an item table as shown below:
Order category has a domain with fixed values as seen below:
As we Know, to use RAP based application we must create a Root View and a projection view. Let’s create for both Header and item table.
Header Entity
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Header Table'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define root view entity ZI_JOULE_HEADER
as select from zjoule_header
composition[0..*] of ZI_JOULE_ITEM as _Item
{
key order_id as OrderId,
order_category as OrderCategory,
created_on as CreatedOn,
created_by as CreatedBy,
_Item
}
Item Entity:
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Joule Item'
@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define view entity ZI_JOULE_ITEM
as select from zjoule_item
association to parent ZI_JOULE_HEADER as _header on $projection.OrderId = _header.OrderId
{
key cast(order_id as vbeln_va preserving type ) as OrderId,
key order_item as OrderItem,
@Semantics.quantity.unitOfMeasure: 'Unit'
quantity as Quantity,
unit as Unit,
@Semantics.amount.currencyCode: 'Currency'
price as Price,
currency as Currency,
model as Model,
_header
}
Header Projection:
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Projection for Header Table'
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define root view entity ZC_JOULE_HEADER
provider contract transactional_query
as projection on ZI_JOULE_HEADER
{
key OrderId,
OrderCategory,
CreatedOn,
CreatedBy,
/* Associations */
_Item : redirected to composition child ZC_JOULE_ITEM
}
Item Projection:
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Projection for Item Table'
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define view entity ZC_JOULE_ITEM
as projection on ZI_JOULE_ITEM
{
key OrderId,
key OrderItem,
@Semantics.quantity.unitOfMeasure: 'Unit'
Quantity,
Unit,
@Semantics.amount.currencyCode: 'Currency'
Price,
Currency,
Model,
/* Associations */
_header : redirected to parent ZC_JOULE_HEADER
}
Let’s also create the Metadata extension for both the projection views.
Header table:
@Metadata.layer: #CORE
@UI:{ headerInfo: {
typeName: 'Order',
typeNamePlural: 'Orders',
title: {
type: #STANDARD,
value: 'OrderId'
}
} }
annotate entity ZC_JOULE_HEADER with
{
.facet: [{
id: 'Order',
purpose: #STANDARD,
position: 10,
label: 'Order',
type: #IDENTIFICATION_REFERENCE
},
{
id: 'Items',
purpose: #STANDARD,
position: 20,
label: 'Booking',
type: #LINEITEM_REFERENCE,
targetElement: '_Item'
}]
:{ lineItem: [{position: 10,importance: #HIGH }],
identification: [{position: 10 }],
selectionField: [{position: 10 }] }
OrderId;
:{ lineItem: [{position: 20,importance: #HIGH }],
identification: [{position: 20 }],selectionField: [{position: 20 }] }
OrderCategory;
:{ lineItem: [{position: 30,importance: #MEDIUM }],
identification: [{position: 30 }],selectionField: [{position: 30 }] }
CreatedOn;
:{ lineItem: [{position: 40,importance: #MEDIUM }],
identification: [{position: 40 }] }
CreatedBy;
}
Item Table:
@Metadata.layer: #CORE
@UI.headerInfo: {
typeName: 'Order',
typeNamePlural: 'Orders',
title: {
type: #STANDARD,
label: 'Order',
value: 'OrderId'
}
}
annotate entity ZC_JOULE_ITEM with
{
.facet: [{id: 'Order',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Order',
position: 10 }]
:{lineItem: [{ position: 10,importance: #HIGH }],
identification: [{position: 10 }]
}
OrderId;
:{lineItem: [{ position: 20,importance: #HIGH }],
identification: [{position: 20 }]
}
OrderItem;
:{lineItem: [{ position: 30,importance: #HIGH }],
identification: [{position: 30 }]
}
Quantity;
:{lineItem: [{ position: 40,importance: #HIGH }],
identification: [{position: 40 }]
}
Unit;
:{lineItem: [{ position: 50,importance: #HIGH }],
identification: [{position: 50 }]
}
Price;
:{lineItem: [{ position: 60,importance: #HIGH }],
identification: [{position: 60 }]
}
Currency;
:{lineItem: [{ position: 70,importance: #HIGH }],
identification: [{position: 70 }] }
Model;
}
Once we create Entities, we will create a behaviour definition with managed scenario. As we are using ODATA V4, also create a draft table for the same. We will not be implementing the class since it’s a managed scenario.
managed implementation in class zbp_i_joule_header unique;
strict ( 2 );
with draft;
with privileged mode disabling NoCheckWhenPrivileged;
define authorization context NoCheckWhenPrivileged { 'O_OIU_TXN'; }
define own authorization context { 'O_OIU_TXN'; }
define behavior for ZI_JOULE_HEADER alias Order
persistent table zjoule_header
draft table zjoule_draft_hd
lock master total etag CreatedOn
authorization master ( instance )
etag master CreatedOn
{
create;
update;
delete;
association _Item { create; with draft; }
draft action Edit;
draft action Discard;
draft action Resume;
draft action Activate optimized;
draft determine action Prepare { }
field ( mandatory ) OrderCategory;
mapping for zjoule_header
{
OrderId = order_id;
OrderCategory = order_category;
CreatedOn = created_on;
CreatedBy = created_by;
}
}
define behavior for ZI_JOULE_ITEM alias Items
persistent table zjoule_item
draft table zjoule_draft_it
lock dependent by _header
authorization dependent by _header
//etag master <field_name>
{
update;
delete;
field ( readonly ) OrderId;
field(mandatory) Currency, Price, Quantity, Unit, Model;
association _header { with draft; }
mapping for zjoule_item
{
OrderId = order_id;
OrderItem = order_item;
Currency = currency;
Price = price;
Quantity = quantity;
Unit = unit;
Model = model;
}
}
Create a Service Definition and a Service binding, Register the service to test.
@EndUserText.label: 'Service Definitions'
define service ZU_JOULEHEADER {
expose ZC_JOULE_HEADER as Orders;
expose ZC_JOULE_ITEM as Items;
}
I have created some sample data to test the application.
Connect your BTP account to Access Joule services
You must subscribe to SAP DAS and then create an instance of SAP DAS. You must be a global account admin to perform those tasks. Then, add the entitlement SAP DAS for your global account in the SAP BTP control center. Kindly refer to the below url for Global account setup
https://help.sap.com/docs/joule/serviceguide/prerequisites?locale=en-US
In the SAP BTP cockpit, create a subaccount. Open your sub account and establish Trust configurations( Preferably create a Custom IDP ). Then, go to service marketplace, look for SAP DAS and open the same. Based on the entitlements, you will see the development plan under Application plans. Click on the actions menu and select create. Now, you have subscribed to SAP DAS.
To develop capabilities with Joule, you need to assign roles to your user. Refer the below URL
https://help.sap.com/docs/joule/service-guide/assign-roles?locale=en-US
Create a Service Instance and Service Key
Inside your subaccount, go to the service marketplace, look for the app SAP Digital Assistant and click to open. After adding your entitlements you will see designer plan under service plans. Click on the actions to create the instance.
Select the SAP Digital Assistant that you created, click on the actions menu to choose service key. Choose a name for your service and proceed.
Installing SAPDAS CLI:
Prerequisites - You should have installed Node.js( minimum version 18 ). Open the terminal and run the following commands to:
Install sapdas
npm install -g sapdas-cli
Get a version
npm install -g sapdas-cli@<version>
Replace <version> with a version number.
Thanks,
Srinivasa Muralidhar
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
61 | |
21 | |
13 | |
11 | |
11 | |
8 | |
8 | |
8 | |
6 | |
6 |