Lightning Componentのコンポーネントライブラリが便利 #salesforce

Spring'18の時点でベータリリースはされていましたが、正式リリースされたっぽいです。

コンポーネントライブラリとは

developer.salesforce.com

実際のURLは https://developer.salesforce.com/docs/component-library/overview/components ですね。

上記のページを見てもらうのがよいですが、要するにLightningの基本コンポーネント(入力フォームとかグリッドシステムとか)のリファレンスページです。

どのようなコンポーネントがあるのか一目で分かるようになっており、各オプションの動作やコード表示も備わっているので、便利ですね。また、公式なので、リリースに伴う新しいコンポーネントやオプションもきちんと反映されていくことでしょう。

Lightningコンポーネントのライブエディタ

(ここからはただのこぼれ話です)

少し前(2017年11月頃)まではライブラリ集みたいなものはなく、ドキュメントもそこまで便利にはなっていなかったので、その不便さを解消するため、個人的に以下のようなプレビューアプリ(Lightningコンポーネントのライブエディタ)を作ったりしていました。

jappy.hatenablog.com

このアプリ、もうすぐ完成というところで、Spring'18が間近に迫り、その中でコンポーネントライブラリのベータ版 *1 もリリースされたことで、役割の被る拙作アプリの開発は止まっちゃっていました…。

当初目標に掲げていた以下のコンセプトも大体クリアできましたし、もう少しコードを整理して、別の形で活用法は考えたいなと思います。

  • Apex, Visualforceを一行も書かずに使えるアプリを作ること
  • 極力Lightning Design Systemを直に使わず、アプリ自身もLightning基本コンポーネントを徹底的に活用して作ること
  • $A.createComponents でどこまで柔軟なUIが作れるのか見極めること

*1:当時はhttps://<myDomain>.lightning.force.com/componentReference/suite.app みたいなURLにアクセスすると見ることができました。

Java10でLombokを使ったプロジェクトをGradleビルドする

業務プロジェクトのJavaのバージョンアップは8→11を目論んでいますが、新しいJavaの機能にも少しずつ慣れていかなくてはと思ってます。

というわけで、あるプロジェクトを試しにJava10でビルドしてみたところ、Lombokで躓いたのでその解消方法についてのメモです。
基本的には、エラーが出たらエラー内容をきちんと読んで、一つずつ解消していくしかない、という当たり前の結論になりました。

環境

元のプロジェクトのバージョンとバージョンアップ後の状況はこんな感じ。

バージョンアップ前 バージョンアップ後
Java 1.8.0_172 10.0.1
Gradle ※ 3.5.1 4.7
Lombok 1.16.10 1.16.22

※ Gradleは、Java10だと以下のエラーでビルドができないので、上げています。

FAILURE: Build failed with an exception.

* What went wrong:
Could not determine java version from '10.0.1'.

Lombokの最新Java対応状況

ちなみにLombokですが、今年の2月頃の状況としては、以下のページに書かれている通り、Java10 Early Access では動かなかったようです。

https://qiita.com/tmurakam99/items/b5ffe7f18bc06577f619qiita.com

LombokJDK の内部クラスにどっぷり依存しているので、JDKバージョンアップの度に追従が大変。JDKだけじゃなくてIDEの実装にも依存がある。
(略)
実際、OpenJDK10 Early Access ではすでに動かなくなってます。うはー

試したところ、Lombok 1.16.10のままで --stacktrace をつけてビルドすると、スタックトレースに以下のようなエラーが出力されました。

Caused by: java.lang.ClassNotFoundException: com.sun.tools.javac.code.TypeTags
        at lombok.launch.ShadowClassLoader.loadClass(ShadowClassLoader.java:418)
        at lombok.javac.JavacTreeMaker$SchroedingerType.getFieldCached(JavacTreeMaker.java:156)
        at lombok.javac.JavacTreeMaker$TypeTag.typeTag(JavacTreeMaker.java:244)
        at lombok.javac.Javac.<clinit>(Javac.java:154)
        ... 75 more

最新の1.16.22では、JDK10でコンパイルできるようになったようなので、試してみました。

Lombok1.16.22でビルドしてみる

早速ビルドすると、以下のようなコンパイルエラーが大量に出力…。

エラー: コンストラクタ Hoge()はすでにクラス Hogeで定義されています
@NoArgsConstructor
^

changelogを見ると、

FEATURE: Private no-args constructor for @Data and @Value to enable deserialization frameworks (like Jackson) to operate out-of-the-box. Use lombok.noArgsConstructor.extraPrivate = false to disable this behavior.

とあり、 @Data@Value アノテーションを付けた場合、privateな引数なしコンストラクタが作られるので、明示的に @NoArgsConstructor でpublicな引数なしコンストラクタを作ろうとすると、二重定義でエラーになってしまうみたいですね。

というわけで、メッセージに書かれている通り、プロジェクトの直下に lombok.config というファイルを作って、

lombok.noArgsConstructor.extraPrivate = false

と追記することで、今まで通りビルドできるようになりました。

Gradleのwarningも解消する

ビルドする過程でGradleをバージョンアップしたのですが、ビルド時にコンソールに

Deprecated Gradle features were used in this build, making it incompatible with Gradle 5.0.

Detecting annotation processors on the compile classpath is deprecated and Gradle 5.0 will ignore them.

という形の警告が表示されました。これを解消するには、dependenciesにannotationProcessorを追加すればOKです。

  dependencies {
      annotationProcessor "org.projectlombok:lombok:1.16.22"  // これを追加
      compileOnly "org.projectlombok:lombok:1.16.22"
  }

参考記事) java - Gradle deprecated annotation processor warnings for lombok - Stack Overflow

sfdx形式のソースとメタデータAPI形式のソースの違いについて #salesforce

メモ。

sfdxとスクラッチ組織を使った開発をしていく上で、従来のメタデータAPIに慣れている場合、sfdx形式のソースとメタデータAPI形式のソースでは違いがあることを知っておく必要があります。

「具体的にどんな違いがあるんだろう?」と疑問に思って、Salesforce CLIforce:(source|mdapi):convert コマンドで試したりしていたのですが、公式のドキュメントにきちんと記述されてましたw

developer.salesforce.com

カスタムオブジェクトや静的リソース(zip)については大胆に変わっているので、そのあたりの違いを抑えておくとよさそうです。
特に静的リソースについては、webpackやGulpなどを使って組んでいる自前の静的リソースのビルドルールに影響してきそう。

イマドキのSalesforce開発のプロジェクト雛形を作った #salesforce

ひとまず取り入れたかった技術要素が盛り込めたので、公開してみました。

開発環境の構築は最初に必要な作業ですが、得てしてハードルが高いので、そのあたりの敷居を下げるのが狙いです。

Lightning Testing Service (LTS) の使い方

LTSを使ってみようとしたわけですが、ドキュメント( https://forcedotcom.github.io/LightningTestingService/ )の情報量が少なく、独自のテストを追加してテストを走らせる方法が、ソースを見るまで分からなかったので、簡単に使い方をメモします。

LTSのインストール

$ sfdx force:lightning:test:install

対象のスクラッチ組織に LTS の非管理パッケージをインストールします。
パッケージ名は「Lightning Testing Service with Examples」です。
デフォルトでは最新バージョンがインストールされ、執筆時点で1.3でした。

これはスクラッチ組織ごとに必須な処理ですが、そんなに大きなパッケージではないため、おおよそ10秒未満でインストールが完了するようです。これくらいだったらCIを回す上でも許容範囲かなと。

独自のテストを作成

$ sfdx force:lightning:test:create -n {テスト名}

ここで作成されるテストクラスは、実体(メタデータ)は静的リソースです。

静的リソースといってもZIPファイルではなく単なるJavaScriptなので、ここにテストを記述していくことができます。

テストスイートとなるLightningアプリケーションを作成

追加した独自のテストを実行させるためには、別途Lightningアプリケーションが必要なようです。
これは、LTSパッケージに含まれているテストスイートでは、LTSパッケージ内に事前に用意されているテストしか実行されないためです。
例えばjasmineTestsテストスイートは以下のように記述されており、3つのテストを実行させるように指定されていますが、当然、こちらで新たに追加したテストは含まれていません。

<aura:application>

    <c:lts_jasmineRunner testFiles="{!join(',', 
      $Resource.jasmineHelloWorldTests,
      $Resource.jasmineExampleTests,
      $Resource.jasmineLightningDataServiceTests
    )}" />

</aura:application>

自分で追加するテストスイート用のアプリケーションでは、上記を真似て、testFiles 属性のところで、作成したテストクラス(静的リソース)の名前を渡してあげるよう変更すればOKです。

<aura:application >
    <c:lts_jasmineRunner testFiles="{!join(',', $Resource.<テスト名>)}" />
</aura:application>

テスト関連のメタデータをデプロイ

上記のLightningアプリケーションと静的リソースをスクラッチ組織にデプロイします。

$ sfdx force:source:push

テストの実行

$ sfdx force:lightning:test:run -a <テストアプリケーション名>

ちなみに、標準でJUnitのレポート出力も用意されているので、Travis CIなどのサービスでなくJenkinsでレポートすることも余計な手間なくできそうです。

テストの書き方

テストコードの文法は、JasmineやMochaの流儀にしたがっていけばよいわけですが、Lightningコンポーネントのテストをするのが目的なので、テストコード中でLightningコンポーネントへのアクセスが必要になります。

LTSの場合、テストコード中では $T というグローバルなオブジェクトが利用できるので、それを使うみたいです。

Jasmineの場合はこんな感じ。

describe("c:Hoge", function () {
  it("sets component attributes", function (done) {
    var that = this;

    $T.createComponent("c:Hoge", null)  // テスト対象となるコンポーネントを作成
      .then(function (component) {

        // 必要であれば、$T.waitFor を使って、条件を満たすまでウエイトを入れることができる
        return $T.waitFor(function () {
          that.component = component;
          return 1 === 1; // サンプルのためすぐに true が評価されるようにして、抜ける
        }, 10000);
      }).then(function () {
        // $T.createComponent のコールバックに作成されたコンポーネントが渡されるので、それを使ってassertionを記述する
        expect(that.component.get("v.attr1")).toBe("aaaaa");
        done();
      }).catch(function (e) {
        done.fail(e);
      });
  });
});

テストコードはバージョン管理上どこに置くべき?

どうすべきなんでしょうね…。

sfdxで素直に作ったプロジェクトであれば foce-app がパッケージディレクトリとなっていると思うので、今回は force-app/test/default 以下にテストコード(静的リソース)やテストスイート(Lightningアプリケーション)を置くようにしてみました。

Salesforce CLI (SFDX)のコマンドがわかる一枚絵 #salesforce

いっぱいあるsfdxのコマンドの勘所をつかむための一枚絵を作ってみました。

f:id:jappy:20180224114254p:plain

(探せばありそうな気もしますが…)

参考

qiita.com

適当に作ったDE組織のメタデータをSFDXのプロジェクト化する #salesforce

作業メモです。

developer.salesforce.com

非管理パッケージを作った後で、取ってきたメタデータをコピーしてあげればよいみたいですね。

CLI: プロジェクトの土台を作成

$ sfdx force:project:create --projectname sample-project

慣れないうちはレイアウトもデフォルトにしておいた方が無難でしょう。

CLI: DE組織を接続

CLI経由で、DE組織へのアクセスが行えるようにします。

$ sfdx force:auth:web:login -a de1

ブラウザが立ち上がって、Salesforceへのログインが促されるので、ログインします。
何かと使うので、組織のエイリアス de1 も指定しておきます。

DE: 非管理パッケージを作成

バージョン管理したいメタデータコンポーネントに追加した上で、非管理パッケージをアップロードします。

とりあえず重要なのは

だけなので、それ以外はテキトーに…。

以下、パッケージ名は「pkg1」にしたことを前提としています(要:適宜読み替え)。

CLI: 非管理パッケージを取得

$ sfdx force:mdapi:retrieve -s -r ./tmp -u de1 -p pkg1

./tmp/ の下に unpackaged.zip が落ちてくるはず。

CLI: 非管理パッケージを展開

$ unzip ./tmp/unpackaged.zip -d ./unzipped

CLI: メタデータをsfdx形式のフォーマットに変換する

$ sfdx force:mdapi:convert -r ./unzipped

コマンドリファレンスに "This command must be run in a project." とあるので、必須な処理みたいですね。

ところでこのコマンド、「一体何をしているか?」ですが、基本的には、unzipped/**/* から force-app/main/default/**/* への再帰的なファイルコピーのようです。

ただし単純なコピーではなく、以下のように、いくつか例外(?)があります。

  • package.xml はコピーしない。これは .forceignore が効いているせいかもしれません。
  • 対応するソースに *-meta.xml ファイルがない場合、オリジナルのファイルに -meta.xml を付けた形でリネームする。
  • Zip形式の静的リソースは展開してコピーする。

挙動から推測するより、中身を見たほうが早いかも、と思いました。

Bulk API 2.0のJavaクライアントを作った #salesforce

Winter '18にて、Bulk API 2.0が正式リリースされました。

Bulk API 2.0 (正式リリース)

あんまり話題になっていないような気がするので、簡単にまとめてみます。

Bulk API 2.0とは

新機能については上記ページでも言及されていますが、主観で重要そうだなと思うところをピックアップします。

一番大きな違いはSOAPからRESTになったことですね。そのため、使う側からするとほぼ別物として見た方がよさそうです。

v1.0 v2.0
API SOAP(XML)ベース REST(JSON,CSV)ベース
結果の評価・後処理 結果(Request)はID, Success, Created, Errorの4項目しかないため、RequestとResultを行番号やIDで突き合わせて行なう 結果取得APIレスポンスの中に要求した各項目も含めて返ってくるため、突き合わせが不要
バッチのローテーション CSVが10,000行あるいは10,000,000 文字を超えたら 自分で 面倒を見る 最大 150MB (base64 エンコード後) までは 自動で 分割される
処理されなかったジョブデータ 結果CSVから自分で評価して抜き出す 専用のAPIがある

また、アップロードするデータが20,000 文字以下の場合、ジョブの作成とアップロードをマルチパート要求で1回でできるので、 (大半のケースで)SalesforceAPI消費が1.0よりも少なくて済むと思います(多分)。

Javaクライント

APISOAPからRESTに変わったことで、「APIのドキュメントは公開するから好きなHTTPクライアントで実装してね」という方針になったのかどうかは分かりませんが、ネットで探してもあまり利用例が見当たりません。

ということで、試してみました(言語はJavaです)。

とは言え、Bulk API 2.0では、一般的なREST APIJSONCSVをやり取りするだけなので、呼び出し方さえ間違えなければさくっと動いてくれました*1

で、色々と試行錯誤した結果、薄いクライアントライブラリ(ラッパー)ができあがったので、公開してみます。

github.com

(使用例はリポジトリのREADMEにあります)

  • ドキュメントとして公開されているものについては、一通りのAPIとオプション(リクエストパラメータの種類)をカバーしています。
  • HTTPクライアントにはOkHttpを使っています。
  • JSONエンコーダ・デコーダはJacksonを使いました。OkHttpのWikiでは、Moshiというライブラリが紹介されていましたが…(はじめて知りました)

*1:エラーレスポンスのフォーマットがドキュメントに見当たらなかったり、日本語版のドキュメントでレスポンスパラメータ名が間違っていたりはありましたが…w