关于 SAC 多步操作公用 API 的介绍

背景 

SAC 在2023.15版本中推出了多步操作公用 API。这些 API 可以用于触发多步操作的执行,并在执行后查询结果。这为将 SAC 工作流集成到外部应用程序提供了一种灵活的方式,帮助客户实现各种业务场景。 

 

这些 API 已经发布到了 SAP Business Accelerator Hub:https://api.sap.com/api/MultiActions_API/overview 。在SAC帮助文档中也可以找到有关API的介绍:https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/14cac91febef464dbb1efce20e3f1613/e5ade1ed7c274d929a18bcc859102c40.html 

 

这篇博客旨在强调必要的准备工作,并提供一些典型的示例来演示这些 API,你也可以在上述页面中找到 API 定义的更多细节。 

 

准备 

身份验证与授权 

多步操作公用 API 应在业务用户的语境下进行。API 用户需要正确配置身份验证和授权,以在 SAC 中指定所需的业务用户。通常有两种主要选项来配置身份验证和授权。 

OAuth2 授权码工作流 

详见https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/14cac91febef464dbb1efce20e3f1613/0c1fb5e6ef1f46acb83771070084f124.html#configuring-oauth-2.0-authorization-code-grant-workflow-(3-legged-oauth) ,“Configuring OAuth 2.0 Authorization Code Grant Workflow (3-legged OAuth) ”这部分介绍了如何在 SAC 中按照 OAuth2 授权码流对用户进行身份验证和授权。(由于多步操作 API 应在业务用户语境下进行,因此选择“交互式使用”作为 OAuth 客户端的“目的”)。 

OAuth 2.0 SAML Bearer Assertion 工作流 

除了授权码解决方案外,你还可以利用 OAuth2 SAML Bearer Assertion 工作流来进行身份验证和授权,前提是用户已经在 SAC 中添加了可信的 IdP。应用授权码工作流的一个限制是通常需要终端用户输入凭据登录 SAC,从而获取授权码并继续认证过程。考虑到大部分情况下,对公用 API 的调用是嵌在完全自动化的流程中的,并且不能让用户与其交互。我们建议你在这种情况下将 OAuth2 SAML Bearer Assertion 工作流视为备选解决方案。 

你可以使用由你的 IdP 颁发的 SAML Bearer Token 来获取访问令牌。这篇文章https://blogs.sap.com/2021/04/09/sap-analytics-cloud-app-integration-with-oauth2samlbearerassertion-flow./ 介绍了在 SAC 应用集成中可以应用的 OAuth2 SAML Bearer Assertion 流,并提供了一些详细的示例,介绍了如何在有或没有使用 SAP BTP Destination 服务的情况下应用这个流。 

 

获取 CSRF 令牌 

在使用 POST 方法发送 HTTP 请求之前(在多操作触发 API 中使用),你需要先获取一个有效的 CSRF 令牌。 

你可以通过向 {tenant-url}/api/v1/csrf 发送 GET 请求,在请求中包括头部 x-csrf-token: fetch 来获取令牌。然后, CSRF 令牌会在响应头 x-csrf-token 中返回。 

一旦你获得了令牌,你就可以在同一会话中使用它来发送 POST 请求(将 Cookie 中的 JSESSIONID 与 CSRF 请求保持一致)。请求中必须包含 x-csrf-token:{token} 头部。 

 

API 的定义 

请参考 SAC 帮助文档:https://help.sap.com/docs/SAP_ANALYTICS_CLOUD/14cac91febef464dbb1efce20e3f1613/e5ade1ed7c274d929a18bcc859102c40.html ,获取 API 的定义的细节,这一内容不会在这篇文章中重复说明。 

 

示例 

使用一些常见的HTTP客户端来尝试API 

注意 

在下面的示例中,我们将使用 Postman 来尝试 API,因为它提供了友好的用户界面和简单的配置,以便促进 OAuth2 授权码流。你也可以使用其他典型的 HTTP 客户端来尝试 API(例如 curl: https://curl.se/docs/manual.html,swagger-ui: https://swagger.io/tools/swagger-ui/ 等),只要它们已经内置了相关的身份验证/授权流,或者你也可以自定义解决方案,按照授权码流或 SAML Bearer Assertion 流获取访问令牌,并使用授权报头发送令牌。 

(要在 swagger-ui 中使用 API,您可以从 https://api.sap.com/api/MultiActions_API/overview 下载API规范作为参考) 

OAuth2 授权码工作流 

  1. 在 SAC 中创建一个 OAuth 客户端,将用途设置为“交互使用”,将重定向 URI 设置为:https://www.getpostman.com/oauth2/callback,这是 postman 需要的。 
  2. 转到 Postman 中的请求集合的“Authorization”选项卡,将“configure new token”部分定义为: 
  3. 配置完成后,点击 postman 中“Authorization”选项卡底部的“configure new token”。在弹出窗口中登录SAC 后,访问令牌会被获取。 
  4. 接下来,访问令牌就可以用于访问多步操作公用 API 了。同时,使用 POST 方法访问 API 需要 csrf 令牌。先使用以下请求获取 csrf 令牌。 

           你将会在响应报头中拿到 csrf 令牌: 

       

5. 在下面的 POST 请求中使用 csrf 令牌来执行多步操作。csrf 令牌仅在同一会话中有效,因此请确保以下请求使用相同的 cookie (这通常由 Postman 默认处理,一般情况下你不需要在以下请求中显式设置 cookie) 

6. 使用查询状态 API 来获取多步操作执行的状态。 

OAuth 2.0 SAML Bearer Assertion 工作流 

与上述授权码流相比,使用 OAuth 2.0 SAML Bearer Assertion 尝试 API 的唯一区别在于获取访问令牌的方式,而在其他部分,它们应该是相同的。 

https://blogs.sap.com/2021/04/09/sap-analytics-cloud-app-integration-with-oauth2samlbearerassertion-flow./ 提供了使用和不使用 BTP destination 服务获取访问令牌的示例。例如,你可以参考 https://blogs.sap.com/2021/04/08/how-to-generate-saml-bearer-assertion-token-for-oauth2samlbearerassertion-flow/#sac-brief 来创建一个nodejs程序,以便在 OAuth 2.0 SAML Bearer Assertion 流之后获得访问令牌(如果手头没有 BTP destination)。如果你能够在你的解决方案中使用 BTP destination,那将会变得更加容易https://blogs.sap.com/2021/03/24/oauth2samlbearerassertion-flow-with-the-sap-btp-destination-service.-sap-analytics-cloud-cf-edition./ 说明了如何利用 BTP destination 来使用 OAuth 2.0 SAML Bearer Assertion 流访问 SAC。 

然后从前面授权码流示例中的步骤4开始,使用访问令牌访问 API,下面的步骤应该是相同的。 

从 ABAP/NetWeaver 栈调用 API 

可以从 ABAP/NetWeaver 栈中使用多操作公用 API,因此可以将其与BW, BPC等产品集成,只要它们提供定制的 ABAP 出口。在下面的示例中,我们将利用 BTP destination 服务(作为身份提供者,它可以被作为 IdP 角色的其他服务取代)来实现 OAuth2 SAML Beaerer Assertion 流,从而可以从 ABAP 程序调用多步操作公用 API,无需用户干预,并且可以嵌入在 ABAP/NetWeaver 侧的自动化作业中。 

还请注意,下面所示的过程非常常见,您也可以通过利用所选技术提供的相应 http 客户端,将ABAP 栈替换为其他编程环境。 

工作流的实现步骤如下: 

在 SAC 中创建 OAuth Client 

在 SAC 中创建一个 OAuth client,用途设置为“交互式使用”,并且不需要定义重定向url。 

 

 

在 BTP 中 创建 destination 以及 destination 服务 

这篇文章 https://blogs.sap.com/2021/03/24/oauth2samlbearerassertion-flow-with-the-sap-btp-destination-service.-sap-analytics-cloud-cf-edition./ 全面解释了如何利用 BTP destination 服务作为 IdP 在 SAC 中实现 OAuth 2.0 SAML Bearer 身份认证(https://help.sap.com/docs/connectivity/sap-btp-connectivity-cf/consuming-destination-service )。在当前场景的语境中,以下是在 BTP 中设置 destination 和 destination 服务的一些关键步骤: 

在 BTP 子帐户中创建 destination 

在BTP子帐户下创建一个目标,其身份验证类型为:OAuth2 SAML Beaerer Assertion。请参考下面的快照进行字段映射: 

下载 IDP metadata,记下在 SAC 中添加信任 IDP 时将使用的 entityID。 

Download Trust 并复制这里的 public certificate value ,这也将用于在 SAC 中添加信任 IdP: 

在 BTP 中创建 destination 服务实例(如果服务之前不存在) 

如果之前没有创建可用的服务实例,则在 BTP 中创建 destination 服务实例 

打开服务密钥,记下 client id 和 client secrete,它们将用于使用 OAuth2 client 凭证方法访问 destination 服务实例。 

 

在 SAC 中添加受信任的 IdP 

在 SAC 中添加受信任的 IdP,设置如下: 

ABAP 层的实现 

创建一个 ABAP 程序并声明公共变量 

REPORT z_run_multi_actions_in_sac.
PARAMETERS:
pv_ma_id TYPE string LOWER CASE DEFAULT 'Input multi-actions id here'.
DATA lo_client TYPE REF TO if_http_client.
DATA lo_rest_client TYPE REF TO if_rest_client.
DATA lo_rest_entity TYPE REF TO if_rest_entity.
DATA lo_request TYPE REF TO if_rest_entity.
DATA lv_body TYPE string.
DATA lv_http_status TYPE string.
DATA lv_response TYPE string.
DATA lv_acc_token_to_destination TYPE string.
DATA lv_acc_token_to_sac TYPE string.
DATA lv_csrf TYPE string.

 

获取访问令牌以请求 destination 服务 

相应的 ABAP 实现: 

*----------------------------------------get access to destination service--------------------------------- 
 
cl_http_client=>create_by_url( EXPORTING url = '<your token url for the btp destination service>' IMPORTING client = lo_client ). 
 
CREATE OBJECT lo_rest_client TYPE cl_rest_http_client 
  EXPORTING 
    io_http_client = lo_client. 
 
lo_request = lo_rest_client->create_request_entity( ). 
lo_request->set_content_type( iv_media_type = 'application/x-www-form-urlencoded' ). 
lv_body = 'grant_type=client_credentials&client_id=<your oauth client id for destination service>&client_secret=<your oauth client secret for destination service>'. 
lo_request->set_string_data( lv_body ). 
lo_rest_client->post( EXPORTING io_entity = lo_request ). 
lv_http_status = lo_rest_client->get_status( ). 
lo_rest_entity = lo_rest_client->get_response_entity( ). 
 
lv_response = lo_rest_entity->get_string_data( ). 
 
DATA lr_data TYPE REF TO data. 
/ui2/cl_json=>deserialize( 
  EXPORTING 
    json         = lv_response 
    pretty_name  = /ui2/cl_json=>pretty_mode-user 
    assoc_arrays = abap_true 
  CHANGING 
    data         = lr_data ). 
 
IF lr_data IS BOUND. 
  ASSIGN lr_data->* TO FIELD-SYMBOL(<lfs_data>). 
  ASSIGN COMPONENT 'access_token' OF STRUCTURE <lfs_data> TO FIELD-SYMBOL(<lfs_results>). 
  ASSIGN <lfs_results>->* TO FIELD-SYMBOL(<ld_token_value>). 
  WRITE: 'Get access to destination service successfully'. 
ENDIF. 
 
lv_acc_token_to_destination = <ld_token_value>. 

 

通过 destination 服务请求 destination 以获取访问令牌以访问 SAC 

相应的 ABAP 实现: 

*----------------------------------------get access to destination service--------------------------------- 
 
cl_http_client=>create_by_url( EXPORTING url = '<your token url for the btp destination service>' IMPORTING client = lo_client ). 
 
CREATE OBJECT lo_rest_client TYPE cl_rest_http_client 
  EXPORTING 
    io_http_client = lo_client. 
 
lo_request = lo_rest_client->create_request_entity( ). 
lo_request->set_content_type( iv_media_type = 'application/x-www-form-urlencoded' ). 
lv_body = 'grant_type=client_credentials&client_id=<your oauth client id for destination service>&client_secret=<your oauth client secret for destination service>'. 
lo_request->set_string_data( lv_body ). 
lo_rest_client->post( EXPORTING io_entity = lo_request ). 
lv_http_status = lo_rest_client->get_status( ). 
lo_rest_entity = lo_rest_client->get_response_entity( ). 
 
lv_response = lo_rest_entity->get_string_data( ). 
 
DATA lr_data TYPE REF TO data. 
/ui2/cl_json=>deserialize( 
  EXPORTING 
    json         = lv_response 
    pretty_name  = /ui2/cl_json=>pretty_mode-user 
    assoc_arrays = abap_true 
  CHANGING 
    data         = lr_data ). 
 
IF lr_data IS BOUND. 
  ASSIGN lr_data->* TO FIELD-SYMBOL(<lfs_data>). 
  ASSIGN COMPONENT 'access_token' OF STRUCTURE <lfs_data> TO FIELD-SYMBOL(<lfs_results>). 
  ASSIGN <lfs_results>->* TO FIELD-SYMBOL(<ld_token_value>). 
  WRITE: 'Get access to destination service successfully'. 
ENDIF. 
 
lv_acc_token_to_destination = <ld_token_value>. 

 

 

 获取 SAC 的 csrf 令牌和 session id 

*----------------------------------------send request get csrf--------------------------------- 
cl_http_client=>create_by_url( EXPORTING url = '<your SAC url>/api/v1/csrf' IMPORTING client = lo_client ). 
CREATE OBJECT lo_rest_client TYPE cl_rest_http_client 
  EXPORTING 
    io_http_client = lo_client. 
 
lo_rest_client->set_request_header( 
    EXPORTING 
      iv_name  = 'X-CSRF-TOKEN' 
      iv_value = 'FETCH' ). 
 
lo_rest_client->set_request_header( 
    EXPORTING 
      iv_name  = 'Authorization' 
      iv_value = lv_acc_token_to_sac ). 
 
lo_rest_client->get( ). 
lv_http_status = lo_rest_client->get_status( ). 
lo_rest_entity = lo_rest_client->get_response_entity( ). 
lv_csrf = lo_rest_entity->get_header_field( 'X-CSRF-TOKEN' ). 
DATA lt_headers TYPE tihttpnvp. 
DATA ls_header TYPE ihttpnvp. 
lt_headers = lo_rest_entity->get_header_fields( ). 
DATA lt_cookies TYPE TABLE OF string. 
DATA lv_cookie TYPE string. 
DATA lv_cookie_remaining TYPE string. 
LOOP AT lt_headers INTO ls_header. 
  IF ls_header-name = 'set-cookie'. 
    IF ls_header-value CS 'JSESSIONID='. 
      SPLIT ls_header-value AT ';' INTO lv_cookie lv_cookie_remaining. 
      lv_cookie = lv_cookie+11. 
      EXIT. 
    ENDIF. 
  ENDIF. 
ENDLOOP. 
*WRITE: lv_csrf. 
DATA lv_decoded_cookie TYPE string. 
lv_decoded_cookie = cl_http_utility=>unescape_url( 
                escaped   = lv_cookie ). 
WRITE: / 'Get csrf token from SAC public api service'. 
*WRITE: lv_csrf. 
*WRITE: lv_decoded_cookie. 

 

向 SAC 发送请求来开启多步操作 

*----------------------------------------trigger multiactions--------------------------------- 
DATA lv_url TYPE string. 
lv_url = '<your SAC url>/api/v1/multiActions/' && pv_ma_id && '/executions'. 
cl_http_client=>create_by_url( EXPORTING url = lv_url IMPORTING client = lo_client ). 
 
CALL METHOD lo_client->request->set_method( if_http_request=>co_request_method_post ). 
lo_client->request->set_header_field( name = 'x-csrf-token' value = lv_csrf ). 
lo_client->request->set_header_field( name = 'Authorization' value = lv_acc_token_to_sac ). 
lo_client->request->set_header_field( name = 'Content-Type' value = 'application/json' ). 
lo_client->request->set_cookie( name = 'JSESSIONID' 
                                           value = lv_decoded_cookie ). 
lv_body = '{"parameterValues": [ ]}'. 
lo_client->request->set_cdata( data = lv_body ). 
CALL METHOD lo_client->send 
  EXCEPTIONS 
    http_communication_failure = 1 
    http_invalid_state         = 2 
    http_processing_failed     = 3. 
 
ASSERT sy-subrc = 0. 
 
CALL METHOD lo_client->receive 
  EXCEPTIONS 
    http_communication_failure = 1 
    http_invalid_state         = 2 
    http_processing_failed     = 3. 
lv_response = lo_client->response->get_cdata( ). 
WRITE: lv_response. 
WRITE: / 'Trigger multi-actions ' && pv_ma_id && ' successfully'. 

 

在 BTP 中集成 SAP Build Process Automation 

SAP Build Process Automation(将在下面的描述中使用“BTP PA”来表示该平台)是 BTP 上可用的低代码平台,可用于跨 SAP/非SAP 应用程序构建工作流。(https://www.sap.com/products/technology-platform/process-automation/features.html )我们还可以在 BTP PA 过程中嵌入多步操作 API,以实现相应的业务需求。以下是配置的一些关键步骤: 

在 BTP 子账户下订阅 SAP Build Process Automation 计划 

 

安全配置 

在 SAC 中创建一个 OAuth Client,就像上面的 ABAP/Netweaver 一节中提到的一样。 

使用 OAuth2 SAML Bearer Assertion 认证类型在 BTP 中创建 destination,就像上面 ABAP/Netweaver 部分中提到的一样,同时也有一些需要注意的差异: 

  • 在 URL 后增加“api/v1”后缀,例如:https://{tenant url}/api/v1 
  • 在 additional properties 部分,增加: 
    • sap.applicationdevelopment.actions.enabled = true 
    • sap.processautomation.enabled = true 
    • SystemUser 不是必需的,因为您可以配置使用启动 BTP PA 进程的用户作为触发多步操作的用户 

在 SAC 中添加 BTP destination 作为受信任的 IdP,类似的过程也在上面的“ABAP/Netweaver”一节中介绍过了。 

BTP PA 配置 

打开 BTP PA,进入“settings”选项卡,添加在上面步骤中配置的 destination。 

创建一个多步操作 API action 

进入“lobby”选项卡,点击“create”按钮,选择“build a automated process”,然后选择“actions”。 

点击“upload API specification”,上传多步操作 API 规范 

  • 你可以从 https://api.sap.com/api/MultiActions_API/overview 下载 API 规范。由于我们的destination URL 被定义为 https://{tenant url}/api/v1,而不是 API 规范中定义的https://{tenant url}/api/v1/multiActions(因为 BTP PA 需要在相同的 destination url 前缀下配置 csrf 令牌 url),让我们手动更新 API 规范中的路径名,以添加前缀“/multiActions”。 

 

在 action 设置和 post 请求中同时为 action 启用csrf 设置: 

 

释放动作并发布到库。 

 

创建一个业务流程 

从 BTP PA 的 lobby 选项卡创建业务流程 

 

在流程项目下,创建一个新的数据类型 

创建多步操作参数的结构如下并保存: 

创建一个流程,从 action library 中添加触发多步操作请求作为流程的一个步骤: 

 

配置流程输入如下: 

单击我们刚刚添加的多步操作步骤,创建一个 destination 变量,它将在部署流程时用于绑定 destination : 

 

配置以代表启动流程的用户来运行操作(用户在SAC中也应存在): 

配置 action 输入如下: 

发布并部署流程。 

从 monitor 中运行该流程 

进入 BTP PA 的 monitor 选项卡,点击“Manage – processes and Workflows”,选择我们刚刚部署的流程,点击“Start new instance” 

  

以 Json 格式提供流程输入,点击“start new instance”: 

{ 
       "pmaid": "t.7:FD000504F025851615C5037A42DD56BE", 
       "pparameter":  
          { 
               "parameterId": "TargetVersion", 
               "value": { 
                   "memberIds": [ 
                       "public.Actual" 
                  ], 
                "hierarchyId": "" 
              } 
          } 
} 

 

在“monitor – Process and Workflows”中,你可以查看流程的状态: 

(结束) 

 

原文链接:Introduction of SAC Multi-actions Public API

原作者:Manu Xu

Scroll to Top