はじめに
Protocol Buffersは、スキーマの中にはMin/MaxなどのValidationを標準ではサポートしていません。
最近、protovalidateというものが立ち上がってきています。
Goで使えるproto-gen-validateの後継とのことですが、protovalidateはGo, C++, Java, Python, TypeScript(coming soonとのこと)で使えるようになっていて、複雑なValidationもサポートしているとのです。
全体の解説は、こちらに素晴らしい記事がありますので
ここではPython版を使ってみようと思います。
環境準備
protovalidateを利用するために、buf cliをインストールします。
Dockerfileに下記を記載します。
buf cliは、サーバーにスキーマを送ってコード生成することもできるのですが、
セキュリティ上 問題になることもあるため、今回はprotocもインストールしてLocalでコード生成します。
ARG PROTOC_VERSION=27.2
ARG BUF_CLI_VERSION=1.34.0
# Install buf cli
RUN curl -LO https://github.com/bufbuild/buf/releases/download/v${BUF_CLI_VERSION}/buf-Linux-x86_64.tar.gz \
&& tar -zxvf buf-Linux-x86_64.tar.gz \
&& cp -rf buf/bin/* /usr/bin \
&& rm -rf ./*
# Install protoc
RUN curl -LO https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip \
&& unzip protoc-${PROTOC_VERSION}-linux-x86_64.zip -d /usr/ \
&& rm -rf ./*
buf cliの準備
buf.yamlを以下のように作成します。
version: v2
deps:
- buf.build/bufbuild/protovalidate
lint:
use:
- DEFAULT
breaking:
use:
- FILE
buf.gen.yamlを以下のように作成します。今回はpythonとpyiを生成します。
pluginsの要素を追加すれば他の言語のコードも生成できます。
version: v2
plugins:
- protoc_builtin: python
out: gen
include_imports: true
- protoc_builtin: pyi
out: gen
include_imports: true
protocのコマンドより便利ですね。
スキーマ作成
例としてroi.protoを作成します。4000 x 3000の画像をCropするときの設定です。
VS codeで作業をする場合は、Bufを拡張機能としてインストールすると良いです。
syntax = "proto3";
package roi;
import "buf/validate/validate.proto";
message Roi {
uint32 top = 1;
uint32 left = 2;
uint32 height = 3 [(buf.validate.field).uint32 = {gte: 1}];
uint32 width = 4 [(buf.validate.field).uint32 = {gte: 1}];
option (buf.validate.message).cel = {
id: "roi.width_sum"
message: "left + width <= 4000"
expression: "this.left + this.width <= 4000"
};
option (buf.validate.message).cel = {
id: "roi.height_sum"
message: "top + height <= 3000"
expression: "this.top + this.height <= 3000"
};
}
package roi;
の部分で警告が出ますが、今回は無視します。
コード生成
下記のコマンドで依存関係を取得します。buf.lockが生成されます。
buf dep update
下記のコマンドでコード生成を行います。
buf generate
genディレクトリ以下にroi_pb2.pyとその他のファイルが生成されます。
テスト
下記のコードでテストができます。
sys.path.appendは無理矢理感ありますが、とりあえずこれで動きます。
import sys
sys.path.append("gen")
import protovalidate
from gen.roi_pb2 import Roi
def validate_roi(roi):
print(roi.top, roi.left,roi.height,roi.width)
try:
protovalidate.validate(roi)
print("Validation Success")
except protovalidate.ValidationError as e:
print("Validation Failed")
print(e.violations)
if __name__ == "__main__":
validate_roi(Roi(left=0,top=0,width=4000,height=3000))
validate_roi(Roi(left=10,top=10,width=0,height=0))
validate_roi(Roi(left=0,top=1,width=4000,height=3000))
出力は下記です。Validationのどこで失敗したかもわかっていいですね。
0 0 3000 4000
Validation Success
10 10 0 0
Validation Failed
violations {
field_path: "height"
constraint_id: "uint32.gte"
message: "value must be greater than or equal to 1"
}
violations {
field_path: "width"
constraint_id: "uint32.gte"
message: "value must be greater than or equal to 1"
}
1 0 3000 4000
Validation Failed
violations {
constraint_id: "roi.height_sum"
message: "top + height <= 3000"
}
まとめ
protovalidateのpython版を使ってみました。
まだ、ベータ版とのことですが企業としてはSchema管理のSaaSもやっているようで、
既に複数の言語に対応しているため将来有望に思えます。