はじめに
CAP(SAP Cloud Application Programming Model)を利用してBTP環境にデプロイするODataAPIを作ってみます。(Java)
前回の記事ではサンプルを使ってCAPアプリケーション開発の全体的な流れを確認できたので、今回は自分で定義したデータモデルをサービスとして公開してみます。
また、カスタムロジックでODataリクエスト処理(CRUD処理)を試してみます。
【前回】
【SAP BTP】CAP Service SDK for JavaでOData APIを作ってみる(1)
【PostgreSQLにつないでみる】
【SAP BTP】CAPのAPI(Java)をPostgreSQLで使ってみる
前提
(1)があるので事前準備等は参考にしてください
CAPプロジェクトの作成までの記載が完了していることを前提としています。
プロジェクトを新規作成する
CAPのサンプルに追加する形でも問題ないのですが、わかりやすくするために新しいJavaアプリケーションとして作成します。
MTAプロジェクトのルートディレクトリ配下で以下を実行します。
$ mvn archetype:generate -DarchetypeArtifactId="cds-services-archetype" -DarchetypeGroupId="com.sap.cds" -DarchetypeVersion="RELEASE" "-DinteractiveMode=true"
group ID、artifact ID等は以下を設定しています。
内容 | 値 |
---|---|
groupId: | jp.co.kyoso.demo |
artifactId: | customservice |
package: | ※デフォルト |
customservice
ディレクトリが作成されます。
CDSの作成
データ定義の作成
user-service.cds
という名称で.cdsファイルを作成します。
内容は以下の通りです。
service UserService {
entity Users {
key ID : Integer;
FirstName : String(100);
LastName : String(100);
Age : Integer;
}
}
.cdsファイルにはODataサービスとして公開したいデータ定義を記載します。
次に、作成した.cdsファイルのデータ定義をコンパイルしてmetadataのxmlを作成してもらいます。
Javaアプリケーションのルートディレクトリに移動し、以下のコマンドを実行します。
$ cd customservice/
$ mvn clean install
Build Successするとgen
、edmx
ディレクトリが作成され、
データ定義をもととしたJavaクラスとmetadata.xmlが出来上がります。
テスト実行①
この段階で一度テスト実行をします。
Javaアプリケーションのルートディレクトリで以下のコマンドを実行します。
$ mvn spring-boot:run
成功するとサービスが立ち上がり、metadataを参照できることが確認できます。
イベントハンドラーの作成
Javaクラスを作成
CDSを作成できたので、次はODataAPIによるCRUD操作を処理するイベントハンドラーを作成します。
srv/src/main/java
ディレクトリの中にあるApplication.javaの存在する階層にhandlers
ディレクトリを作成します。
作成したhandlers
ディレクトリの中に新しいJavaクラスファイルUserService.java
を作成します。
package jp.co.kyoso.demo.customservice.handlers;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Component;
import com.sap.cds.services.cds.CdsCreateEventContext;
import com.sap.cds.services.cds.CdsReadEventContext;
import com.sap.cds.services.cds.CdsService;
import com.sap.cds.services.handler.EventHandler;
import com.sap.cds.services.handler.annotations.On;
import com.sap.cds.services.handler.annotations.ServiceName;
@Component
@ServiceName("UserService")
public class UserService implements EventHandler {
private Map<Object, Map<String, Object>> users = new HashMap<>();
// UsersエンティティのCreateイベント
@On(event = CdsService.EVENT_CREATE, entity = "UserService.Users")
public void onCreate(CdsCreateEventContext context) {
context.getCqn().entries().forEach(e -> users.put(e.get("ID"), e));
context.setResult(context.getCqn().entries());
}
// UsersエンティティのReadイベント
@On(event = CdsService.EVENT_READ, entity = "UserService.Users")
public void onRead(CdsReadEventContext context) {
context.setResult(users.values());
}
}
テスト実行②
イベントハンドラーを追加したので、再度テスト実行を行います。
先ほど実行したアプリケーションが生きている場合は一度停止し、再起動します。
Usersエンティティ(/odata/v4/UserService/Users)へアクセスするとデータが存在しない状態です。
イベントハンドラーにはCreateイベントの処理を登録しているので、POSTメソッドでHTTPリクエストを実施し、データを追加してみます。
PostMan等のRestクライアントを利用する場合はDeveloper Toolから認証情報のCookie(workspaces-xxxxxxx_WSR_SESSIONID)を取ってきて設定します。
【Cookieの参照】
【Cookieの設定】
HTTPヘッダにContent-Type : application/json
を設定し、以下のBodyをPOSTで送信します。
{
"ID": 1,
"FirstName": "KYOSO",
"LastName": "花子",
"Age": 20
}
POSTでのリクエストを実行後、再度GETリクエストを実行すると先ほど登録したデータが参照できることが確認できました。
DBへの接続
APIが実行できることを確認できたのですが、この状態でDeploy等をしてもまだDBへつながっていない状態なので永続的なデータとしては扱えません。
DBへ接続できるようにしていきます。
CDSファイルの作り替え
db
ディレクトリ配下にschema.cds
ファイルを作成します。
schema.cds
に以下を記載します。
@sap/cds/commonにはCDSで利用できる様々な機能があります。
今回はmanaged をつけてみました。
namespace demo;
using { managed } from '@sap/cds/common';
entity Users : managed {
key ID : Integer;
FirstName : String(100);
LastName : String(100);
Age : Integer;
}
schema.cds
にデータ構造定義を記載したので、最初に作っていたuser-service.cds
の中身を修正してschema.cds
を参照するようにします。
using { demo as db } from '../db/schema';
service UserService {
entity Users as projection on db.Users;
}
テストデータを準備する
ついでなので、テスト実行用のデータファイルを準備します。
db/data
ディレクトリを作成し、配下にdemo-Users.csv
を作成します。
.csvファイルの命名規則は
schema.cds
に記載した内容から設定する必要があり、フォーマットは<namespace>-<entity>.csv
です。
今回はnamespaceがdemoで用意するデータの対象entityがdemoなのでdemo-Users.csv
になります。
ID;FirstName;LastName;Age
1;csv;太郎;100
2;csv;次郎;101
3;csv;三郎;99
イベントハンドラーの編集
CAP SDKを使ったDB接続にはODataサービスに必要な機能がそろっているとのことで、イベントハンドラーが不要になるみたいです。
そのため、先ほど作成したhandler/UserService.java
がなくてもOData APIとして基本的な機能は動くようになりました。
ただ、カスタムロジックを入れたいときはイベントハンドラーが必要になります。
学習目的でデータ取得時の結果を編集してみます。
package jp.co.kyoso.demo.customservice.handlers;
import java.util.stream.Stream;
import org.springframework.stereotype.Component;
import com.sap.cds.services.cds.CdsService;
import com.sap.cds.services.handler.EventHandler;
import com.sap.cds.services.handler.annotations.After;
import com.sap.cds.services.handler.annotations.ServiceName;
import cds.gen.userservice.Users;
@Component
@ServiceName("UserService")
public class UserService implements EventHandler {
@After(event = CdsService.EVENT_READ, entity = "UserService.Users")
public void addText(Stream<Users> users) {
// Ageが100歳以上の場合はLastNameに" (over 100!!)"を追記します。
users.filter(b -> b.getAge() > 100).forEach(b -> b.setLastName(b.getLastName() + " (over 100!!)"));
}
}
テスト実行③
またテスト実行を行います。
先ほど実行したアプリケーションが生きている場合は一度停止し、再起動します。
Usersエンティティ(/odata/v4/UserService/Users)へアクセスするとテストデータが取得できます。
また、イベントハンドラーに追加したカスタムロジックも機能しています。
(csv次郎さんに" (over 100!!)"がついてます。)
schema.cdsに@sap/cds/commonのmanagedをつけているので、定義には含まれてないですがcreatedAt,createdBy,modifiedAt,modifiedByのカラムが追加されています。
BTP環境へデプロイ
DBへの接続もできるようになったと思うので、MTAをBuildしてデプロイします。
mta.yamlに以下を記載します。
_schema-version: "3.2"
ID: mtademo
version: 0.0.1
modules:
# サンプルのJavaModule(不要)
# - name: capdemo_api
# type: java
# path: capdemoservice
# parameters:
# buildpack: java_buildpack
# properties:
# SPRING_PROFILES_ACTIVE: cloud
# build-parameters:
# builder: custom
# commands: [ mvn clean package ]
# build-result: "srv/target/*-exec.[wj]ar"
# provides:
# - name: srv-api # required by consumers of CAP services (e.g. approuter)
# properties:
# srv-url: ${default-url}
# requires:
# - name: useDemoShared
# properties:
# JBP_CONFIG_RESOURCE_CONFIGURATION: '[tomcat/webapps/ROOT/META-INF/context.xml:
# {"service_name_for_DefaultDB" : "~{hdi-container-name}"}]'
# customserviceのJavaModule
- name: capCustom_api
type: java
path: customservice
parameters:
buildpack: java_buildpack
properties:
SPRING_PROFILES_ACTIVE: cloud
build-parameters:
builder: custom
commands: [ mvn clean package ]
build-result: "srv/target/*-exec.[wj]ar"
provides:
- name: capCustom-api # required by consumers of CAP services (e.g. approuter)
properties:
srv-url: ${default-url}
# resources -> useDemoSharedに定義したHANA DBを使う
requires:
- name: useDemoShared
properties:
JBP_CONFIG_RESOURCE_CONFIGURATION: '[tomcat/webapps/ROOT/META-INF/context.xml:
{"service_name_for_DefaultDB" : "~{hdi-container-name}"}]'
resources:
# 作成済みのHANA DBサービスをresourcesに記載
- name: useDemoShared
parameters:
service-name: demoShared
service: hanatrial
service-plan: securestore
config:
schema: sampleSchema
properties:
hdi-container-name: '${service-name}'
type: org.cloudfoundry.existing-service
MTAをBuildしてデプロイを実行します。
$ mbt build
$ cf deploy mta_archives/mtademo_0.0.1.mtar
HANA DBにテーブル追加
APIを実行する前にCDSに定義されているデータ構造をHANA DBに作成します。
CREATE TABLE demo_Users (
ID INTEGER NOT NULL,
FirstName NVARCHAR(100),
LastName NVARCHAR(100),
Age INTEGER,
createdAt TIMESTAMP,
createdBy NVARCHAR(255),
modifiedAt TIMESTAMP,
modifiedBy NVARCHAR(255),
PRIMARY KEY(ID)
);
CREATE VIEW UserService_Users AS SELECT
Users_0.createdAt,
Users_0.createdBy,
Users_0.modifiedAt,
Users_0.modifiedBy,
Users_0.ID,
Users_0.FirstName,
Users_0.LastName,
Users_0.Age
FROM demo_Users AS Users_0;
INSERT INTO "DEMO_USERS" VALUES(
1/*ID <INTEGER>*/,
'KYOSO'/*FIRSTNAME <NVARCHAR(100)>*/,
'太郎'/*LASTNAME <NVARCHAR(100)>*/,
49/*AGE <INTEGER>*/,
''/*CREATEDAT <TIMESTAMP>*/,
'HANA DB'/*CREATEDBY <NVARCHAR(255)>*/,
''/*MODIFIEDAT <TIMESTAMP>*/,
'HANA DB'/*MODIFIEDBY <NVARCHAR(255)>*/
);
INSERT INTO "DEMO_USERS" VALUES(
2/*ID <INTEGER>*/,
'KYOSO'/*FIRSTNAME <NVARCHAR(100)>*/,
'次郎'/*LASTNAME <NVARCHAR(100)>*/,
120/*AGE <INTEGER>*/,
''/*CREATEDAT <TIMESTAMP>*/,
'HANA DB'/*CREATEDBY <NVARCHAR(255)>*/,
''/*MODIFIEDAT <TIMESTAMP>*/,
'HANA DB'/*MODIFIEDBY <NVARCHAR(255)>*/
);
demo_Usersテーブルを作成し、データを投入しました。
UserService_UsersのViewも作成しています。
BTPにデプロイしたOData APIを実行する
HANA DBに投入した値が取得できることが確認できました。
$filterのクエリオプションも実行可能です。(/odata/v4/UserService/Users?$filter=ID eq 1)
ついでにKYOSO花子さんのデータをPOSTメソッドで登録します。
DBに登録できることが確認できました。
自動で登録・更新日付等も登録されています。
おわりに
CAP Service SDK for Javaを使ってODataAPIが構築できました。
CDS等はもっと多くの機能があります。また、本来であればDBのテーブル等はデータ定義をデプロイして作成するべきところなのかなと思っています。
実際に開発していくとなると調査すべき課題が多いので、調べていきたいと思います。