SAP Forms by Adobe使ってみた

はじめまして。初投稿になります。
鬼瓦と申します。

※この記事は、<a href="https://adventar.org/calendars/3908">SAP Advent Calendar 2019</a> の15日目の記事として投稿しています。

## はじめに
SAP Cloud Platform(SCP)で帳票生成サービスが存在し、こちら触ってみたので、紹介します。
その名もSAP Cloud Platform Forms by Adobeです。
仕事でもいくつか帳票ツール触ってきましたが、これは使いやすいと思いました。
以下、個人的に感じたメリットです。 
<ul>
<li>"Adobe"と名前がついているので、デザイン系すごそう、それっぽい</li>
<li>サービス名に"SAP"とついているが、SAPのERPが無いと動かないことは無く、ODataサービスがあるので、呼出元環境問わない</li>
<li>GUIの帳票レイアウトツールが存在する</li>
<li>サーバにレイアウトファイルをULする際、専用UIが存在し、レイアウトULも簡単</li>
</ul>
今回は、プログラミングのリハビリ、Node.js触ってみたい、という動機で、ローカルPCのNode.js環境から呼び出してみました。

## 開発環境
- ローカルPC Windows 10  
Visual Studio Code  
Node.js v12.13.1 LTS
- SAP Cloud Platform (Neo)環境 
※1 現時点(2019/12/01)ではNeo環境のみ、Forms by Adobeサービスが利用可能です。

- Adobe LiveCycle Designer 11

## 使い方
### 環境構築
サーバ立てて、ソフトウェアインストールして、、、は、クラウドサービスなので不要です！

1. サービス有効化
何がともあれ、まずは、ここからです。
Neo環境にログイン後、サービスからFroms by Adobeが存在するので、こちらを有効化します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532743/fb050fd4-0bc4-6823-2e07-7399577f4fa5.png)

2. OAuth認証設定
クライアント設定時のポイントとしては、サブスクリプションを"formsprocessing/adsrestapi"にすることです。
![IFbA_OAuth.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532743/b40a6b6c-1e42-b96f-9316-7ba471342f87.png)

3. 帳票レイアウト作成
グラフィカルに帳票レイアウト作成できます！
ツールも直感的で、操作がわかりやすいです。Adobe感あります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532743/71f6d464-0122-e666-541a-90bc9164e33d.png)


4. 帳票レイアウトファイルUL
3.で作った帳票レイアウトファイルですが、利用するには、サーバにULする必要があります。
とはいえ、UL用画面があるので簡単です！
Rest APIからわざわざULする、なんかサーバに直接ログインしてレイアウトファイルコピペする、、、ということはありません。  
サービス画面の下の方にいくつかメニューが並んでいます。
REST APIテンプレートストアUIから帳票レイアウトをULできます。
![template.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532743/bc273059-f2cd-ff57-ac54-94130e0c7382.png)  
"Create From"ボタンからフォームを作成し、右枠のTEMPLATES部分から先程作成した帳票レイアウトをULします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532743/a86f7c75-a277-e902-e022-c154eb4b68f8.png)

以上で、準備完了です。

### テスト用コード
OAuth認証、Rest API呼び出しによるPDFファイル生成、PDFファイルのローカルファイルへの保存まで実装しました。

```Javascript
/**
 * Authority EP Host
 */
const sAUTH_BASE_HOST    =   '<OAuth用Host>';

/**
 * OAuth Token EP
 */
const sTOKEN_URI        =   '/oauth2/api/v1/token'

/**
 * OAuth Client ID
 */
const sCLIENT_ID        =   '<OAuth用クライアントID>';

/**
 * OAuth Client Secret
 */
const sCLIENT_SECRET    =   '<OAuth用クライアントシークレット>';

/**
 * OAuth Grant Type
 */
const sGRANT_TYPE       =   'client_credentials';

/**
 * OAuth Grant Scope
 */
const sADS_SCOPE        =   'generate-ads-output';

/**
 * Forms by Adobe Base Host
 * https://adsrestapiformsprocessing-<yoursubaccount>.<yourregionhost:[xxx.]hana.ondemand.com>
 */
const sPDF_SRV_HOST      =   '<Rest API用Host>'
/**
 * PDF Generation URI
 */
const sPDF_RENDER_URI   =   '/ads.restapi/v1/adsRender/pdf'

/**
 * PDF Render parameter
 * The value means tepmlate file is read from SCP form/template storage.
 */
const sPDF_RENDER_PARAM =   '?templateSource=storageName'

/**
 * Generated PDF File Name in local PC
 * */
const sPDF_FILE_NAME    =   'test114.pdf'

/**
 * PDF data (XML)
 */
const sPDF_DATA         =
`<?xml version="1.0" encoding="UTF-8"?>     
<form1>                                    
    <Page1>                                 
        <Form1>                             
            <Text1>鬼瓦</Text1>         
            <Text2>ko-hei</Text2>   
            <Date1>20040606T101010</Date1>  
        </Form1>                            
    </Page1>                                
</form1>                                   
`;

/* Generate HTTP Client */
const fnRequest           =   require('request');

/* Generate Request Header */
let sAuthString         =   conv2Base64(sCLIENT_ID + ':' + sCLIENT_SECRET);

let oAuthHeaders        =   {
    "Content-Type"  : 'application/x-www-form-urlencoded',
    "Authorization" : 'Basic ' + sAuthString
};

let sAuthBody       = 'grant_type=' + sGRANT_TYPE + '&scope=' + sADS_SCOPE;

let oAuthOptions         =   {
    "url"       :   sAUTH_BASE_HOST + sTOKEN_URI,
    "method"    :   'POST',
    "headers"   :   oAuthHeaders,
    "body"      :   sAuthBody,
    "json"      :   true
};

/* Request token */
requestService('Authority', oAuthOptions, generatePDF);

/**
 * Convert string as base64 format.
 * @param   {String} sTarget 
 * @return  {String} Converted string as base64
 */
function conv2Base64(sTarget) {
    return  Buffer.from(sTarget).toString('base64');
}

/**
 * HTTP Request
 * @param {String} sLabel       Console label
 * @param {Object} oOptions     Request option
 * @param {Function} fnSuccess  Called function when the request is called sucessfully
 */
function requestService(sLabel, oOptions, fnSuccess) {
    fnRequest(oOptions, (oError, oResponse, oBody) => {
        console.log('<---- ' + sLabel + ' ---->');
        if (oError) {
            console.log(oError);
            return;
        } 

        console.log('Successed');
        fnSuccess(oResponse, oBody);
    });
}

/**
 * Get PDF file from SCP service.
 * @param {*} oResponse HTTP Response
 * @param {*} oBody     HTTP Response Body
 */
function generatePDF(oResponse, oBody)  {
    const sToken  =   oBody.access_token;

    let oPdfHeaders =   {
        "Content-Type"  : 'application/json',
        "Authorization" : 'Bearer ' + sToken
    };

    let oPdfBody   =   {
        "xdpTemplate"   : '<Form名>/<Tempalte名>', // 環境準備の4でULしたレイアウト
        "xmlData"       : conv2Base64(sPDF_DATA),
        "formType"      : 'Print',
        "formLocale"    : 'ja_JP',
        "taggedPdf"     : 0,
        "embedFont"     : 0
    };

    let oPdfOptions    =   {
        "url"       :   sPDF_SRV_HOST + sPDF_RENDER_URI + sPDF_RENDER_PARAM,
        "method"    :   'POST',  
        "headers"   :   oPdfHeaders,
        "body"      :   oPdfBody,
        "json"      :   true
    };

    requestService('PDF Generation', oPdfOptions, outputPDFFile)
}

/**
 * Output PDF to local pc from response body.
 * @param {*} oResponse 
 * @param {*} oBody 
 */
function outputPDFFile(oResponse, oBody) {
    const oFileIO = require('fs');
    console.log(oBody);
    if (oBody.fileContent) {
        let oBin_pdf    = Buffer.from(oBody.fileContent, 'base64');
        oFileIO.writeFile(sPDF_FILE_NAME, oBin_pdf, (err) => console.log(err));
    } else {
        console.log('PDF not generated.');
    }
}
```

## デモ
上記のテストコードを実行すると、以下の様なPDFがローカルに出力されます。
すごい簡単にPDFの帳票ファイルが作成されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532743/d9f50261-6ec9-fb42-c323-6ca4acfe5421.png)


## 終わりに
以前、仕事で帳票ツール使うとなると、、、、
帳票サーバ立ち上げるの大変、、、、
修正する際はそもそもレイアウト編集用ツールが無くて大変、、、、
等々あったのですが、、、、
こちらは、両者とも簡単でとても便利でした！
SAPに限らず、カスタム開発でも使っていけそうです！

## 参考リンク
<a href="https://help.sap.com/viewer/6d3eac5a9e3144a7b43932a1078c7628/Cloud/en-US/3f4f7318d8c941308696512c2125424e.html">SAP Forms by Adobe REST API</a>