dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

「Google API Expertが解説するClosure Libraryプログラミングガイド」を読んだ

業務で Closure Library を使うので読んだ。
正確には、最後まで写経しながら読み進めようと思ったけど第5章あたりで挫折して、第6章以降はさっと目を通す程度になってしまった。

出版されたのが 2010 年とかなり古いが、Closure Library を扱った書籍としてはあとこれぐらいしか見当たらず(こっちは英語)。


事前に学んでいたこと

公式ドキュメントをかいつまんで読んだ程度。


本書で学べること

目次ベースでざっくりまとめておく。
Closure Library の基本的なコンポーネント開発方法と、Closure Compiler と組み合わせてコンパイルするための環境構築あたりに興味があったので
第4章まで読んだ後一旦第9章「Closure Compiler による JavaScript の最適化」を先に読んでから第5章に戻った。


第2章「Closure Library を使ってみよう」

depswriter.py, closurebuilder.py という Closure Library に同梱されているツールの使い方が紹介されていた。
Closure Compiler を使わずに Closure Library をどうアプリに組み込めばいいか知りたかったので、最初に言及されていたのは良かった。


第3章「Closure Library の基礎」、第11章「その他の機能」

Closure Library の豊富な UI コンポーネントや関数・ツール群について紹介がされているが、なにしろ膨大なので熟読して覚えるというよりは必要なときに参照できるようリファレンスとして手元に置いとくと良いなと思った。


第4章「Tiny Word アプリケーション」以降

ここから先は複数の章を通じて Tiny Word というサンプルアプリケーションを開発しながら、Closure Library の使い方を学ぶという構成になっている。
ただし第7章、第8章はアプリとして完成させるために Closure Library とは直接関係ないサーバーサイドの開発 (Google App Engine w/ Python) の話になっている。
一部 Closure Library を使った非同期処理の書き方も含むが、アプリを作りきるのが目的でなければこのへんはスキップしてもいいかも。

サンプルアプリで使用している Closure Library のコンポーネントは、主にこのあたり:

  • データソース ( .goog.ds.JsDataSource ) およびデータマネージャ ( goog.ds.DataManager )
    • 多くの形式のデータを共通のインターフェースで扱える
  • ツールバー、メニュー ( goog.ui.Tab , goog.ui.Menu )
  • ツリーコントロール ( goog.ui.tree.TreeControl )
    • こういうやつ↓

f:id:dackdive:20190109003313p:plain


第9章「Closure Compiler による JavaScript の最適化」

JavaScript の型チェックと最適化を行う Closure Compiler の概要と、closurebuilder.py と組み合わせて使う方法について。

特に

あたり、かなりページを割いて解説されていたのは有難かった。


第10章「デバッグ・テスト」

デバッグツール ( goog.debug , goog.asserts) の使い方、およびテストフレームワーク ( JSUnit ) を使ったテストの書き方。
残念ながらあんまちゃんと読めていない。


本書で学べないこと

  • Closure Template ( .soy ファイル) を使ったコンポーネントの開発方法
    • Soy テンプレートはこの書籍の対象外
  • コンポーネントの使い方 (override しないといけないメソッドなど) をどうやって調べるか
  • Closure Compiler の型チェックでエラーが出たときの原因と対処方法の調べ方
    • 9.3.3 に代表的なエラーをリストアップしてくれているが、ここに書かれていないエラーが発生したときに何が原因で怒られているのかよくわからなかった


その他:2019年にこの本を読む上での注意点

depswriter.py, closurebuilder.py の使い方が学べてよかった、と上に書いたが、書籍が出版された時代から Closure Compiler もいくらか進歩しており
コンパイル方法などは当時とは状況が変わってそう。

というのも、closurebuilder.py でビルドをすると、以下のような警告が出る。

$ python closure-library/closure/bin/build/closurebuilder.py \
  --root=closure-library \
  --root=scripts \
  --namespace="tinyword.App" \
  --output_mode=compiled \
  --compiler_jar=compiler.jar \
  --output_file=script.js
closure-library/closure/bin/build/closurebuilder.py: Scanning paths...
closure-library/closure/bin/build/closurebuilder.py: 1620 sources scanned.
closure-library/closure/bin/build/closurebuilder.py: Building dependency tree..
closure-library/closure/bin/build/closurebuilder.py: Closure Compiler now natively understands and orders Closure dependencies and
is prefererred over using this script for performing JavaScript compilation.

Please migrate your codebase.

See:
https://github.com/google/closure-compiler/wiki/Managing-Dependencies

おそらくだが現在は Closure Compiler 単体で goog.provide() / goog.require() の依存解決はできるみたい。
また Closure Compiler も現在は npm パッケージで公開されているものもある

さらに、こんな記事もある。

今回、自分も写経していてこのへんの開発効率の悪さ(ローカルサーバー立ち上げる、ファイル編集するたび closurebuilder.py 実行して待たされる、etc.)は結構ストレスを感じたので
開発環境まわりはこの後もう少し調べてみたい。

Google Closure CompilerとかGoogle Closure LibraryなどのClosure Tools概要まとめ

Google Closure ○○ シリーズが多くてわからん、となったのでメモ。
主に Closure Compiler と Closure Library を中心に。

総称として Closure Tools と呼ぶらしいです。ここにまとまってます。

またソースコードはすべて GitHub で公開されている模様。

https://github.com/google?utf8=%E2%9C%93&q=closure&type=&language=

Google Closure Compiler

The Closure Compiler is a tool for making JavaScript download and run faster. Instead of compiling from a source language to machine code, it compiles from JavaScript to better JavaScript. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what's left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls.

JS の型チェックや圧縮、最適化などをやってくれるコンパイラ
半角スペースなどを取り除いたり変数名を省略することで JS を圧縮してくれるのに加え、JSDoc 形式のアノテーションを記述するとコンパイル時に型のチェックなども行ってくれます。

GitHub

https://github.com/google/closure-compiler

インストール方法

いくつか選択肢がある。
(参考: https://developers.google.com/closure/compiler/https://github.com/google/closure-compiler/ の Getting Started)

  1. Java アプリ (.jar) として提供されているものをダウンロードして使う (最新版のダウンロードリンクはこちら)
  2. web アプリが https://closure-compiler.appspot.com/home にあるので、コンパイルしたいコードを貼り付けて web 上でコンパイルする
  3. REST API として提供されているものを使う (参考)
  4. npm パッケージとして提供されているものを使う https://www.npmjs.com/package/google-closure-compiler

その他、Maven のプラグインもある らしいがよくわからない。

使い方

ここでは、4 の npm パッケージを使うものとして進めます。

$ npm install google-closure-compiler

素の ./node_modules/.bin/google-closure-compiler を叩いてもいいですが、オプションとして渡すパラメータもあるので npm script を書いておきます。

"scripts": {
  "build": "google-closure-compiler --js=main.js --js_output_file=build.js"
}

--jsコンパイル対象のファイル、 --js_output_fileコンパイル後の出力先を指定します。

サンプルとなるファイルを書いてみます。

サンプル1 (単純な例)

function hello() {
  alert('hello');
}

結果1 (コンパイル後の JS)

function hello(){alert("hello")};

サンプル2 (アノテーションによる型チェック)

/** @const {number}**/
var foo = 100;

foo = 200;

/** @define {string} **/
var greeting = 'hello';

greeting = 100;

結果2 (ビルド実行時の様子)

$ npm run build
main.js:4: WARNING - constant foo assigned a value more than once.
Original definition at main.js:2
foo = 200;
^^^^^^^^^

main.js:9: WARNING - constant greeting assigned a value more than once.
Original definition at main.js:7
greeting = 100;
^^^^^^^^^^^^^^

0 error(s), 2 warning(s)

error と warning の違いはわかってません。


リファレンス

Google Closure Library

The Closure Library is a broad, well-tested, modular, and cross-browser JavaScript library. You can pull just what you need from a large set of reusable UI widgets and controls, and from lower-level utilities for DOM manipulation, server communication, animation, data structures, unit testing, rich-text editing, and more.

DOM 操作のための API やら豊富な UI コンポーネントやらが含まれているライブラリ。
APIhttps://google.github.io/closure-library/api/ に一覧が載っているが多すぎて全容を把握できない。


GitHub

https://github.com/google/closure-library


インストール方法

GitHub の README を見る限り、こちらも npm でインストールできるようです。

$ npm install google-closure-library


使い方

先ほど npm でインストールできるといったものの、 require() で読み込めるようにするには何らかのビルドツールと組み合わせて使う方法があり、かつ closure compiler でコンパイルする方法がまだよくわかっていないため
リポジトリを手動でクローンしたものを使ってサンプルを書いてみます。

ここの通り。
Getting Started with the Closure Library  |  Closure Library  |  Google Developers

$ git clone https://github.com/google/closure-library

JS (hello.js)

goog.require('goog.dom');
goog.require('goog.dom.TagName');

function sayHi() {
  var newHeader = goog.dom.createDom(goog.dom.TagName.H1, {'style': 'background-color:#EEE'},
    'Hello world!');
  goog.dom.appendChild(document.body, newHeader);
}

HTML

<html>
  <head>
    <script src="closure-library/closure/goog/base.js"></script>
    <script src="hello.js"></script>
  </head>
  <body onload="sayHi()">
  </body>
</html>

結果

f:id:dackdive:20181126165452p:plain

goog.require() で必要なモジュールをインポートして使うのが基本らしい。
注意点として、 goog.require() は指定したモジュールを読み込むための <script> タグをドキュメントに挿入するという挙動になっているので、 goog.require() してるスクリプトとインポートしたモジュールを使用するスクリプトを同じスクリプト内で実行してはいけない。

<script src="closure-library/closure/goog/base.js"></script>
<script>
  // DON'T DO THIS.
  goog.require('goog.dom');
  var newHeader = goog.dom.createDom(goog.dom.TagName.H1);
</script>


リファレンス

Google Closure Templates

Closure Templates is a client-side and server-side templating system used to build dynamic and reusable HTML and UI elements. (中略) It supports Java and JavaScript and uses a language-neutral expression syntax and data model.

テンプレートエンジン。JavaJavaScript もサポートしているとな。


GitHub

https://github.com/google/closure-templates


インストール方法

GitHub の README を見ると、これも npm で提供されてるようです。
https://www.npmjs.com/package/google-closure-templates

ただし上の2つと異なり Getting Started には npm でのインストール方法は記載されていなかったので、標準の導入方法ではない...?


使い方

クライアント側のサンプルとして、
Hello World Using JavaScript  |  Closure Templates  |  Google Developers
を読んでみます。

※なお、サイトから各リンクをクリックすると軒並み 404 になるので、GitHub の README に記載されてるリンクを開いた方がいいです

Closure Library のときと同じく、npm パッケージは使わず
https://dl.google.com/closure-templates/closure-templates-for-javascript-latest.zip から最新版の closure-templates をダウンロードしてそれを使います。

テンプレートファイルの拡張子は .soy らしい。

simple.soy

{namespace examples.simple}

/**
 * Says hello to the world.
 */
{template .helloWorld}
  Hello world!
{/template}

こいつをダウンロードした zip に入っている SoyToJsSrcCompiler.jar というファイルでコンパイルすると JS ファイルが生成されるらしい。

$ java -jar SoyToJsSrcCompiler.jar --outputPathFormat simple.js --srcs simple.soy

これを読み込む HTML ファイル側は

<!DOCTYPE html>
<html>
<head>
  <title>The Hello Wolrd of Closure Templates</title>
  <script src="soyutils.js"></script>
  <script src="simple.js"></script>
</head>
<body>
  <script type="text/javascript">
    // Exercise the .helloWorld template
    document.write(examples.simple.helloWorld());
  </script>
</body>
</html>

結果

f:id:dackdive:20181126183132p:plain


Google Closure Linter

しょっぱなに Closure Linter is deprecated と書いてあるのでスルー。

https://github.com/google/closure-linter

Please note that the closure linter is deprecated. Esp. if you use ES6 features, the tool will not work for you.

Google Closure Stylesheets

CSS を拡張した言語。
公式リファレンスサイトはなく GitHubリポジトリに飛ばされる。


GitHub

https://github.com/google/closure-stylesheets


インストール方法・使い方

jar ファイルをダウンロードしてくる方式。
時間の都合上試してない。

おまけ: ClosureBuilder

これは調べてる過程で発見したもので、上に挙げた Closure Library に含まれるツールの一部っぽいです。
Closure Library のリポジトリ内に closure/bin/build/closurebuilder.py というスクリプトがあります。

The closurebuilder.py tool, located in closure/bin/build, is a tool to help build compiled JavaScript files. It scans your files and the Closure Library files and can calculate orderings of script files in dependency order. It can output either the scripts' filenames or their contents in dependency order, or can pass the files along to the Closure Compiler to produce a compiled version.

JS のコンパイル? Closure Compiler もあるのに?という感じもしますがよくわからない。
むしろ Closure Compiler と併用する場合の実行方法とかまで書かれている。

まとめとよくわからないこと

とりあえず一通り手を動かして試してみました。ソースはここに置いておきます。
https://github.com/zaki-yama/google-closure-tools-sample

各ツールの概要はわかったけど一歩踏み込んだ使い方とかはまだまだですね。

以下、今回だけではわからなかったこと:

WEB+DB PRESS Vol.106の「速習 Spring Boot」を読んだ

Spring Framework に入門2日め、という位置づけ。
前回

の最後にもあった通り、最近の WEB+DB PRESS でちょうど Spring Boot 特集があったようなので読んだ。


感想

前回、Spring Framework や Spring Boot について苦労しながら公式ドキュメントを漁った経験からすると
これから Spring Framework に入門する人が最初に目を通しておく本としてとても良いのではないかと思いました。

第1章では Spring Framework とはなにか、から始まり、Spring Boot の特徴や Spring Framework との関係性について順を追って説明されています。

第2章では Spring Boot の基本的な構成や各用語の説明があり、第3, 4章で2つのサンプルアプリを実際に作ります。

最後の第5章では作成したアプリを本番環境で運用するための Tips として、Vagrant による仮想環境を使ったアプリ構築手順や、本番稼働しているアプリのヘルスチェック、バージョン管理方法などが紹介されています。

おすすめの読み方

第3章と第4章で作るアプリの差はそれほど多くない (外部の API を使用する & 定期実行する方法) のと、時間がなければまずは第3章のアプリだけ手を動かしてみるのでも十分だと思います。

また第5章に書かれている内容は IaaS などのサーバーに構築する際の知識なので、Heroku などの PaaS で動かしたいと考えている場合はとりあえずスキップでも問題ないかと。

ただし、第3, 4章で構築したアプリの実行手順は第5章の前半に書かれているのでそこだけ注意。


ソースコード

本書のサポートページ からダウンロードできるほか、自分でも写経したものを前回と同じリポジトリに上げておきます。

https://github.com/zaki-yama/spring-framework-study/tree/master/wdpress106-spring-boot


この書籍で学べること/学べないこと

学べること
  • Spring Framework, Spring Boot とはなにか
  • Spring Boot の全体構成
  • 以下の用語に対するざっくりとした理解
    • テンプレートエンジン Thymeleaf
    • Lombok
    • H2 Database
    • JPA (Java Persistance API), Spring Data
  • 基本的なアプリの作り方
  • ビルドしたアプリの起動方法、実際のサーバーで運用するために必要な手順
学べないこと
  • Maven, Gradle の使い方、設定ファイルの書き方

サンプルアプリは Maven で書かれています。
私は Gradle で書いたので、同じように Gradle でやりたいという方は ↑の GitHub のコードが参考になれば。

また、今回は STS (Spring Tool Suite) という IDE を使って開発を進める想定で、新規プロジェクトのひな形に Maven の設定ファイルも含まれるため、中身について詳しい説明はありません。 (なお、Spring Initializr というツールを使えば IDE を使わなくてもひな形を生成できるため、好きなエディタで開発することは可能です)

一部を除いて、各アノテーションの実際の処理までは説明されていません。
「こういう責務のクラスに対してこういうアノテーションをつけるんだー」とおまじない的に覚える感じです。

  • 実際のデータベースを使ってデータを永続化する方法

サンプルでは H2 というデータベースを使い、アプリをシャットダウンするとデータは削除されます。
データベースにデータを保存するために必要な手順については触れられていませんでした。


学習メモ

第1章


第2章

リポジトリ、サービス、コントローラは対応するアノテーションが提供されている。 クラスに対してアノテーションを付与することで、Spring Boot が自動的に Bean として登録する。 (従来の Spring Framework ではXML で行っていた部分)

  • テンプレートエンジン(Thymeleaf)
    • ビュー機能を実現するテンプレートエンジンとして FreeMaker/Groovy/Thymeleaf/Mustache をサポートしている。


第3, 4章

Gradle で依存追加

  • org.webjars とは
    • クライアントサイドのライブラリを依存解決できるようにしたもの
    • ので、バージョンの指定が必須
dependencies {
    implementation('org.webjars:bootstrap:4.1.0')
}

指定しないと以下のエラーが出る。

> Could not find org.webjars:bootstrap:.
  Required by:
      project :

@SpringBootApplication の内部は以下の3つのアノテーションを持っている

@JsonPropertyJava のフィールド名と(API のレスポンスにおける) JSON のキー名を変換するために用いる


次やろうと思ってること

初回から変わらず、公式チュートリアルのこれをやろうと思っています。

[VSCode]htmlファイル編集中のみタグ移動ショートカットキーを有効にする(主にVSCodeVimユーザ向け)

メモ。
Vim には対応する括弧にカーソルを移動する % キーがあるが、
matchit.vim というプラグインを使うと、html の場合に < から > までではなく
対応するタグ ( <body> ~ </body> ) にジャンプできる。

f:id:dackdive:20181106212254g:plain

VSCode にも Vim エクステンション を入れて使っているが、これと同じ操作ができないか調べてみた。

まさにこれでした。

どうやら、 editor.emmet.action.matchTag というコマンドが標準でサポートされており、これにショートカットキーを割り当てればよさそう。

f:id:dackdive:20181106212729p:plain
editor.emmet.action.matchTag

ただ、問題点が1つ。
これをこのまま設定してしまうと、html 以外のファイルを開いているときに % で対応する括弧を移動するショートカットが効かなくなってしまう。

stack overflow だとバッティングを避けるために Cmd+% (Cmd+Shift+5) に割り当ててるみたいだけど、できれば元の Vim と同じように操作したい。

VSCode でショートカットキーを設定する際、 when というオプションがあるが、これで特定の拡張子のときだけ有効になるように制御できないものか。
と思い調べてみるとちゃんとあった。

How can I add a key binding for only certain file types?
Use the editorLangId context key in your when clause:

{ "key": "shift+alt+a",           "command": "editor.action.blockComment",
                                     "when": "editorTextFocus && editorLangId == csharp" },

指定可能な editorLangId のリストは
https://code.visualstudio.com/docs/languages/identifiers
にある。

というわけで、html ファイルを編集中のみ % を対応するタグの移動に割り当てるには、 keybindings.json に以下のように書けばいい。

{
    "key": "shift+5",
    "command": "editor.emmet.action.matchTag",
    "when": "editorLangId == html"
}


残念なところ

1個だけどうにもならなかった点があって。
Vim だと、 % と Visual Mode を組み合わせて

  • Shift+v で開始タグがある行を選択
  • そのまま % を押し、閉じタグまでを選択
  • d でまるっと削除

とかができたんだけど、VSCode ではできなかった。諦める。

Spring Frameworkに入門した

諸事情で Spring Framework を学ぼうと思い始めてみた。1日目。
これまでのプログラミング経験が

みたいな感じだったので、Java については基本的な構文は知ってるもののビルドツールなどのエコシステムすらよくわからない状態からスタート。


やったこと

Spring Framework の全体像を知る

まずは公式ドキュメントを読んでみる。
https://spring.io/

Projects のページを見るとわかるが、Framework といいつつ Spring ○○ と名のつくプロジェクトが大量にあり、関係性やどれから学ぶべきなのかさっぱりわからない。

またこういうのはたいてい Getting Started なるチュートリアルが提供されているものだが、
Guides を見ても一番最初に読むべきものがわからない。困った。

というわけで日本語の解説サイトを検索し、このあたりに目を通す。

どちらも最初の方しか読んでないが、テックスコアの

およびクラスメソッドさんの連載記事の 第1回第2回 あたりを読むと、おおよそ以下のようなことがわかる。

  • Spring Framework は多数のモジュール(プロジェクト)からなるフレームワークであり、コアとなる機能は DI コンテナ
  • DI コンテナ機能のイメージ
  • Spring を用いた DI の実現方法にはいくつかの種類がある。XML の設定ファイルを使う/アノテーションを使う など


Spring Boot の概要を知る

上に挙げたサイトではまだ、Spring Boot というプロジェクトが何を指しているのか見えてこない。
その割に公式サイトでは一番に出てくるので気になる。
というわけで今度は Spring Boot について調べ、以下のスライドにたどり着く。

これ見て Spring Boot が何たるかをなんとなく理解する。
特に

f:id:dackdive:20181102174621p:plain

この図を見て Spring Framework 本体との関係を理解。
後半の Spring Boot の構成要素については一旦スキップ。


Gradle を学ぶ

次は Spring 公式サイトの Guides にある一番基本っぽいチュートリアル
Getting Started · Building an Application with Spring Boot
をやろうと思ったんだけど、冒頭で Gradle に詳しくなければこのモジュールを参考にするよう促されたのでまずはこっち。

Gradle や Ant, Maven などの名前は聞いたことがあって、ビルドツールということは知っていた。
のでフロントエンドで言う webpack とか gulp 的な位置づけかなと思ってたんだけど、どうやら依存パッケージの管理もやるみたいなので npm/yarn ぽい役割も兼ねているのかな、ぐらいの理解。


公式ガイドの Getting Started · Building an Application with Spring Boot をやる

ようやく Spring Framework のサンプルコードを書いてみる。
このモジュールも別に「まずこれからやるべき」と明記されてるわけではなく、 https://spring.io/guides に並んでいる膨大なモジュールの中から一番基本っぽいものを選んだ。

結果、たしかに Spring を使って簡単なアプリケーションを作ることはできたが、おまじない的に出てくる各種アノテーションが何をやっているのかはさっぱりわからなかった。

一度 Spring Boot を使わずに素の Spring Framework を使ったコードも書いてみないと感覚がつかめないのかな...と感じる。


今回の学び

  • Spring Framework の概要を学んだ
  • Gradle の基礎的な内容を学んだ
    • Gradle はビルドツール。似たようなものに Ant, Maven がある
    • 依存パッケージの管理もやってくれる
  • Spring Boot を使ったアプリケーション構築チュートリアルを試した


次回やろうと思っているもの

Spring Guides の中ではタイトル的に一番とっつきやすそう&面白そうなので。ただし

  • 冒頭の Spring Data REST が Spring Framework 全体の中でどういう位置づけなのかわかっていない
  • Spring Boot を使うぽいので、結局またコアの仕組みの部分はブラックボックス化された状態から変わらなさそう

という懸念はある。


今回は見送った参考サイト

https://github.com/zaki-yama/spring-framework-study

の README にリンクをまとめている。


参考になりそうな書籍

Spring Framework の日本語書籍、調べるといくつか出てくるがどれも Amazon のレビューがイマイチで買うの躊躇している。。。
おすすめがあれば教えてください。

なるべく出版年が新しめのもので探した。
この3冊の中だと、最初に挙げたものがやや難解だけど内容としては良さげ。

また最近の WEB+DB PRESS で Spring Boot 特集があったらしい。

会社にあるはずだから読んでみよう。

[Salesforce]Winter'19: Apex Replay Debuggerの使い方

昨日の Meetup の内容をブログにもまとめておきます。本当はこっちを事前に公開したかった。

なお Meetup で話したときの LT 資料はこちら。


Apex Replay Debugger とは

Winter'19 リリースノート:Apex Replay Debugger を使用してすべての組織を無料でデバッグ (正式リリース)

Apex Replay Debugger は VSCode拡張機能として使える Apex のデバッグ機能です。
Apex を実行した際に出力されるデバッグログファイルを元に、ログファイル出力時の状況を VSCode 上で再現(Replay)できます。

そのため、Apex をステップ実行したり、任意の行にブレークポイントを置いてその時点での変数の状態を確認することができます。


事前に必要なもの Prerequisites

Apex Replay Debugger for Visual Studio Code を参考に必要なものを事前にインストールしておきます。


セットアップ手順

プロジェクトフォルダを作成する

VSCode のコマンドパレット (Mac の場合 Cmd + Shift + P で開く) から

SFDX: Create Project
# または
SFDX: Create Project with Manifest

を実行してプロジェクトを作成します。

または、従来のディレクトリ構成 (src/ の下に package.xml を置き、Force.com Migration Tool などを使ってデプロイ) でも
sfdx-project.json を置きさえすればこの後の VSCode のコマンドを利用することは可能です。

`--src
     `--classes
     `--pages
     `--...
     `--package.xml
`--sfdx-project.json

(その場合、sfdx-project.jsonpath パラメータは force-app -> src にする必要あり)


起動構成ファイル (launch.json) を作成する

.vscode/launch.json ファイルを作成し、以下をコピペします。

{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch Apex Replay Debugger",
      "type": "apex-replay",
      "request": "launch",
      "logFile": "${command:AskForLogFileName}",
      "stopOnEntry": true,
      "trace": true
    }
  ]
}

もし、プロジェクトごとに設定するのが面倒な場合、ユーザー設定に "launch": ... という設定を追加することでも可能のようです。

f:id:dackdive:20181031003338p:plain

このあたりは VSCode のドキュメントが参考になります。
https://vscode-doc-jp.github.io/docs/userguide/debugging.html#起動構成


デバッグログレベルを FINEST にする

Replay Debugger を実行するためには、デバッグログレベルを

  • VISUALFORCE: FINER or FINEST
  • APEX_CODE: FINEST

に設定する必要があります。

これは、VSCode 上で

SFDX: Turn On Apex Debug Log for Replay Debugger

というコマンドを実行することで変更できます。


Replay Debugger の使い方


ソースコード上に Checkpoint を置く

処理を中断したりその時点での変数の状態を確認したい行にカーソルを置き、

SFDX: Toggle Checkpoint

を実行します。
行番号の左に赤いマークがつくのがわかります。これを Checkpoint と呼びます。

f:id:dackdive:20181031004307g:plain


Checkpoint を組織に反映させる

設置した Checkpoint は組織に反映させる必要があります。
事前に Scratch Org を作成したり、 sfdx force:auth:web:login などで認証を済ませておいてから

SFDX: Update Checkpoints in Org

を実行します。Output パネルに

Starting SFDX: Update Checkpoints in Org
SFDX: Update Checkpoints in Org, Step 1 of 6: Retrieving org information
SFDX: Update Checkpoints in Org, Step 2 of 6: Retrieving source and line information
SFDX: Update Checkpoints in Org, Step 3 of 6: Setting typeRefs for checkpoints
SFDX: Update Checkpoints in Org, Step 4 of 6: Clearing existing checkpoints
SFDX: Update Checkpoints in Org, Step 5 of 6: Uploading checkpoints
SFDX: Update Checkpoints in Org, Step 6 of 6: Confirming successful checkpoint creation
Ending SFDX: Update Checkpoints in Org

のように出力されれば成功です。

ちなみにこれは、開発者コンソールでエディタを開き、Checkpoint を設置したのと同じことをやっています。

f:id:dackdive:20181031004947p:plain


Apex を実行する

Checkpoint を設置した Apex コードを実行します。
実行にはいくつか方法がありますが、好きな方法で OK です。

  1. Visualforce ページなどで使われている Apex の場合、画面を直接操作する
  2. 該当の Apex のテストコードを実行する
  3. Execute Anonymous Window などで実行する

余談ですが特に VSCode を使った開発の場合、2 に関してはテストクラスを開くとメソッドの上に Run Test というリンクが表示されており、そこからメソッドごとにテストを実行させることができたり

f:id:dackdive:20181031005645p:plain

3 に関しては、適当なスクリプトファイルを作成し、開いた状態で

SFDX: Execute Anonymous Apex with Editor Contents

を実行すれば、開発者コンソールでの Open Execute Anonymous Window とほぼ同等のことを行えて便利です。


デバッグログファイルを取得する

Apex を実行したら、その時のログファイルをローカルにダウンロードします。
これについても VSCode 上で

SFDX: Get Apex Debug Logs...

を実行すると、新しい順にデバッグログの一覧が表示されるので、そこから選択するとダウンロードすることができます。

f:id:dackdive:20181031010027p:plain

ログは .sfdx/tools/debug/logs/ の下に保存されます。


デバッグログファイルから Replay Debugger を起動する

該当のログファイルを開いた状態で、コマンドパレットまたは右クリックして表示されるメニューから

SFDX: Launch Apex Replay Debugger with Current File

を実行します。
ログパネルが開き、またログファイルの1行目に黄色いカーソルが表示され、デバッグ用のメニューが表示されます。

f:id:dackdive:20181031010756p:plain


デバッグ:Apex をステップ実行したり、変数の中身を覗いてみる

この状態で |▶ ボタン (Continue: F5) を押すと、先ほどの Checkpoint まで処理が進み、該当行でストップしていることがわかります。
左側の VARIABLES ペインを見ると、この時点での変数に格納されている値を確認することができます。

f:id:dackdive:20181031011213p:plain

また、行番号の左のスペースをクリックすることで、 Checkpoint を設置した行以外の行にも Breakpoint を設置することができます。
次にデバッガを実行したときにはこの Breakpoint でも処理が中断されるようになります。
(Checkpoint と Breakpoint の違いは後述)


注意事項

今のところ以下の制約があるようです。

  • Checkpoint は最大5個まで。また有効期限は30分
  • 一度に実行できるのは1個のログファイルまで
    • そのため、非同期処理などのデバッグは注意が必要

詳細は Apex Replay Debugger の「Considerations」の項 を一読されることをおすすめします。


その他

Checkpoint と Breakpoint

SFDX: Toggle Checkpoint を使い、また組織にもデプロイした Checkpoint と、VSCode 上だけで定義した Breakpoint は、できること(得られる情報)が異なります。
「Set Breakpoints and Checkpoints」「Considerations」 あたりを読む限り、以下のような違いがあるようです。

For more information than line breakpoints provide, add checkpoints. You can set up to five checkpoints to get heap dumps when lines of code run. All local variables, static variables, and trigger context variables have better information at checkpoints. Trigger context variables don’t exist in logs and are available only at checkpoint locations.

トリガーのコンテキスト変数をはじめとして、Checkpoint の方が持っている情報が多いみたいです。

  • Long string variable values are truncated at breakpoints. At checkpoints, heap-dump-augmented variables have full strings.
  • When viewing a standard or custom object at a breakpoint, you can drill down only to the object’s immediate child variables (one level deep). At checkpoints, heap-dump-augmented variables have full drill-down to child standard objects, not only to immediate children.

Checkpoint の方が長い文字列も省略されなかったり、ネストしたオブジェクトの情報なども完全に保持しているらしい。


Apex Debugger との違い

名前がややこしいですが、VSCode拡張機能として Apex Debugger というのもあります。

ただ、こちらは

This extension enables VS Code to use the real-time Apex Debugger with your scratch orgs and to use ISV Customer Debugger with your subscribers’ sandbox orgs.

というところから対象組織は Scratch Org (および ISV 向けの機能については Sandbox Org) に限定されたり、

  • One Apex Debugger session is included with Performance Edition and Unlimited Edition orgs.
  • To purchase Apex Debugger sessions for Enterprise Edition orgs, or to purchase more sessions for orgs that already have allocated sessions, contact Salesforce.

から Performance Edition, Unlimited Edition 以外では有償のようです。

[Salesforce]UserRecordAccessでレコードの参照・編集権限をチェックする

こんなことやりたい

Apex で SOQL を実行してレコードを取得するとき、実行ユーザが編集権限のあるレコードだけ返したい。
このとき、レコードに対し参照権限だけあると SOQL では取得できてしまうため適宜フィルターする必要がある。


先にまとめ

UserRecordAccess オブジェクトというのがあるので、それを使いましょう。
UserRecordAccess | SOAP API 開発者ガイド | Salesforce Developers

UserRecordAccess には Has*Access ( *EditDelete など) という項目があるので、チェックしたい権限に応じた項目を使います。

また API バージョン 30.0 以降であれば UserRecordAccess は各 SObject のリレーション項目として取得できます。

例:SELECT Id, Name, Email, UserRecordAccess.HasEditAccess FROM Contact


方法

ほぼほぼこちらのテラスカイさんの記事にある通りです。感謝。
が、少々アップデートがあるみたいなので順を追って記載します。

ブログ記事にあるように、あるユーザが特定のレコードに対して参照・編集・削除などの権限を有しているかを判定するためのオブジェクトとして UserRecordAccess というオブジェクトがあります。

UserRecordAccess | SOAP API 開発者ガイド | Salesforce Developers

上のオブジェクト定義を見るとわかりますが、 UserRecordAccess には以下の項目があります。

  • 各種の権限があるかどうかのフラグ項目
    • HasReadAccess : 参照
    • HasEditAccess : 編集
    • HasDeleteAccess : 削除
    • ほか
  • UserId
  • RecordId

UserIdRecordId を WHERE 句に指定することで、「あるユーザが特定のレコードに対して権限を有しているか」を判断できるわけです。

なので、冒頭に記載したように「編集権限のあるレコードだけ返す」を実現するためには、こういったメソッドを用意すればいいでしょう。

public List<Contact> getEditableContacts() {
    Map<Id, Contact> contactMap = new Map<Id, Contact>([
        SELECT Id, Name, Email FROM Contact
    ]);
                                                         
    List<UserRecordAccess> accesses = [
        SELECT
            RecordId, HasEditAccess
        FROM
            UserRecordAccess
        WHERE
            UserId = :UserInfo.getUserId()
        AND
            RecordId IN :contactMap.keySet()
    ];
                                                         
    List<Contact> result = new List<Contact>();
    for (UserRecordAccess access : accesses) {
        if (access.HasEditAccess) {
            result.add(contactMap.get(access.RecordId));
        }
    }
    return result;
}


制限事項

というわけで便利なオブジェクトですが、制限事項がいくつかあります。
↑のテラスカイさんのブログ記事でも述べられていますが、個人的に気になったものを挙げると

  • RecordId は単一または複数のレコードを WHERE 句に指定できるが、 UserId は単一のみ
    • UserId IN *** とすると Error: Can filter on only UserId = [single ID], either RecordId = [single ID] or RecordId IN [list of IDs], and Has*Access = true というエラーが出る
  • 一度に取得できるレコードは 200 件までで、SOQL の結果が 200 件を超えると QueryException が発生する

特に後者の制限が厳しいですね。。。


より良い方法

制限事項を回避できる何か良い方法はないのかと SOAP API 開発者ガイド を最後まで読むと、

API バージョン 30.0 以降では、UserRecordAccess はレコードの外部キーになります。このオブジェクトをルックアップまたは外部キーとして使用する場合は、UserId または RecordId 項目を検索条件に使用したり、指定したりすることはできません。前記のサンプルクエリは、次のように実行できます。
SELECT Id, Name, UserRecordAccess.HasReadAccess, UserRecordAccess.HasTransferAccess, UserRecordAccess.MaxAccessLevel FROM Account

お! UserRecordAccessAPI バージョン 30.0 以降だと任意の SObject のリレーション項目として一緒に取得できるようです。
なので、先ほどのサンプルは以下のように書き直すことができます。

/** Since API version 30.0 **/
public List<Contact> getEditableContacts() {
    List<Contact> contacts = [
        SELECT
            Id, Name, Email,
            UserRecordAccess.HasEditAccess
        FROM
            Contact
    ];
    List<Contact> result = new List<Contact>();
    for (Contact contact : contacts) {
        System.debug('Contact [' + contact.Name + '] is editable?: ' + contact.UserRecordAccess.HasEditAccess);
        if (contact.UserRecordAccess.HasEditAccess) {
            result.add(contact);
        }
    }
    return result;
}

これにより、制限事項のうち「一度に取得できるレコードは 200 件まで」は回避できました。よかった。

ただし、 UserRecordAccess.Has*Access は WHERE 句に直接は指定できず、以下のエラーになります。

Error: You cannot filter on UserRecordAccess when used in a relationship


注意事項

Task, Event など一部の標準オブジェクトについては、UserRecordAccess を項目として取得する方法は使えないようです。

UserRecordAccess オブジェクトへのクエリが一度に 200 件までという制限があることなど、内部的にパフォーマンスに影響ありそうなことをやってそうな香りがしますね。
実際に使うときはパフォーマンス面での影響を少し意識しなければ。


サンプルコード

https://github.com/zaki-yama/salesforce-samples/tree/master/user-record-access