ota2000
6 min read

dlt-community-sources v0.6.0: rest_api 移行とサプライチェーンセキュリティ

dlt-community-sourcesv0.6.0 をリリースした。アーキテクチャを大きく変えたので書いておく。

Breaking Changes

2つの破壊的変更がある。

  • 全ソースのカスタム client.py クラスを廃止した。AppStoreConnectClientNextDNSClientTwilioClient を直接インポートしていた場合は動かなくなる
  • NextDNS の _profile_id フィールドを _profiles_id にリネームした。dlt の include_from_parent の命名規則に合わせた

rest_api への全面移行

v0.5.0 まで、各ソースは独自の HTTP クライアントクラス(client.py)で API を叩いていた。v0.6.0 では dlt の宣言的 rest_api ソースに移行した。

App Store Connect、NextDNS、Twilio の3ソースすべてが対象。標準的な REST エンドポイントは RESTAPIConfig の辞書で定義し、rest_api_resources() へ渡す形になった。

config: RESTAPIConfig = {
    "client": {
        "base_url": "https://api.example.com/v1/",
        "auth": MyAuth(),
    },
    "resources": [
        {
            "name": "apps",
            "endpoint": {"path": "apps"},
        },
        {
            "name": "builds",
            "endpoint": {
                "path": "builds",
                "params": {"filter[app]": "{resources.apps.id}"},
            },
            "include_from_parent": ["id"],
        },
    ],
}

カスタムの @dlt.resource 関数が残っているのは、rest_api では対応できないケースだけ。

  • App Store Connect の Sales/Finance/Analytics Reports(TSV / gzip バイナリのダウンロード)
  • NextDNS の Series リソース(レスポンスの時系列データを平坦化する変換が必要)
  • Twilio の一部リソース(RFC 2822 形式の日付を ISO 8601 に変換する必要がある)

なぜ移行したか

カスタムクライアントの問題点はいくつかあった。

  • ページネーション、リトライ、認証をソースごとに自前で実装していた
  • テストもクライアント単体のモックテストになりがちで、実際のパイプライン動作を検証しにくかった
  • ソースを追加するたびに似たようなクライアントコードを書く必要があった

rest_api に移行すると、エンドポイントの定義が辞書になるので追加・変更が楽になる。ページネーションやリトライは dlt 側が面倒を見てくれる。テストも _rest_api_config() が正しい辞書を返すかを検証するだけでよくなった。

認証まわりの工夫

App Store Connect は JWT 認証で、トークンの有効期限が20分と短い。rest_api の認証は BearerTokenAuth を拡張して、リクエストごとに JWT を再生成する仕組みにした。

@configspec
class AppStoreConnectAuth(BearerTokenAuth):
    key_id: str = None
    issuer_id: str = None
    private_key: str = None

    def __call__(self, request):
        self.token = self._generate_jwt()
        return super().__call__(request)

@configspec デコレータとクラスレベルのフィールド宣言が必要で、これがないと dlt の rest_api 設定バリデーションを通らなかった。

HTTP クライアントの統一

カスタムリソースでは requests.Session を直接使っていたが、dlt の dlt.sources.helpers.requests.Client に置き換えた。429/5xx の自動リトライと指数バックオフが入っているので、rest_api 側のリソースと同じリトライ挙動になる。

from dlt.sources.helpers import requests as req

def _make_client(auth) -> req.Client:
    client = req.Client()
    client.session.auth = auth
    return client

サプライチェーンセキュリティの強化

PyPI パッケージを公開しているので、サプライチェーン攻撃への対策を入れた。

SLSA Provenance

publish ワークフローに SLSA Provenance attestation を追加した。パッケージがどのコミットから、どの CI 環境でビルドされたかを暗号的に証明できる。pip install 時に --require-hashes を使えば検証も可能。

Dependency Review

PR に対して dependency-review-action を走らせている。新しい依存関係に既知の脆弱性がないかを CI でチェックする。

その他

  • CODEOWNERS ファイルの追加
  • ブランチ保護(レビュー必須、ステータスチェック必須)
  • Dependabot の脆弱性アラートとセキュリティアップデートの有効化
  • プライベート脆弱性報告の有効化

AI ルールとスキルの整備

開発体験の改善として、AI アシスタント向けのルールとスキルも整備した。

.ai/rules.md を唯一の原本として管理している。同期スクリプトが Claude Code(CLAUDE.md)、Cursor(.cursor/rules/)、Gemini CLI(GEMINI.md)、Codex CLI(AGENTS.md)向けのファイルを自動生成するので、メンテするのは .ai/rules.md だけで済む。

スキルとしては add-source(新しいソースの追加手順)と add-resource(既存ソースへのリソース追加手順)を用意している。v0.6.0 に合わせて rest_api ファーストのワークフローに書き直した。

さらに dltHub AI workbench も統合した。dlt ai initdlt ai toolkit install で dlt エコシステムのコンテキスト(rest-api-pipeline のスキル群)が入る。プロジェクト固有のルールが上書きする二層構造にしてある。