Extend Codegen CLI - テンプレートとデバッグ
注釈:本資料はAI技術を用いて翻訳されています。
Overview
このガイドでは、Extend Codegen CLI が OpenAPI 2.0 の仕様から SDK コードを生成する仕組みについて詳しく説明します。テンプレートエンジン、カスタムフィルター、デバッグツールを理解することで、問題のトラブルシューティングや、コード生成プロセスのカスタマイズが可能になります。
この情報は、すべての SDK テンプレートパック(Unreal、Unity、Go など)に適用されます。これらはすべて、AccelByte 固有の拡張機能を備えた同じ Jinja2 テンプレートエンジンを使用しているためです。
コード生成のワークフロー
Extend Codegen CLI の中核は、シンプルなテンプレートベースのコードジェネレーターです。2つの入力を受け取り、1つの出力を生成します。
Makefile が複数の生成処理をどのように統括するか
テンプレートパック内で make all を実行すると、実際には Extend Codegen CLI が異なるテンプレートと出力の組み合わせで複数回呼び出されます。
- Unreal
- Unity
- Go
# Simplified example of what the Unreal Makefile does:
# Generation 1: Model classes
docker run ... renderc config.yaml \
--input spec/guild.json \
--template templates/operations-models.j2 \
--output Models/GuildModels.h
# Generation 2: Client API declarations
docker run ... renderc config.yaml \
--input spec/guild.json \
--template templates/Client-operations-decl.j2 \
--output Api/GuildApi.h
# Generation 3: Client API implementations
docker run ... renderc config.yaml \
--input spec/guild.json \
--template templates/Client-operations-def.j2 \
--output Api/GuildApi.cpp
# ... and so on for each file type
# Simplified example of what the Unity Makefile does:
# Generation 1: Model classes
docker run ... renderc config.yaml \
--input spec/guild.json \
--template templates/operations-models.j2 \
--output Models/GuildModels.cs
# Generation 2: Client API classes
docker run ... renderc config.yaml \
--input spec/guild.json \
--template templates/Client-operations.j2 \
--output Api/Guild.cs
# Generation 3: Client API wrapper
docker run ... renderc config.yaml \
--input spec/guild.json \
--template templates/Client-wrapper.j2 \
--output Wrapper/GuildWrapper.cs
# ... and so on for each file type
# Simplified example of what the Go Makefile does:
# Generation 1: Model structs
docker run ... renderc config.yaml \
--input spec/guild.json \
--template templates/operations-models.j2 \
--output guildService-sdk/pkg/guildserviceclientmodels/guild_models.go
# Generation 2: Client API operations
docker run ... renderc config.yaml \
--input spec/guild.json \
--template templates/Client-operations.j2 \
--output guildService-sdk/pkg/guildserviceclient/guild_operations.go
# Generation 3: Short operations wrapper
docker run ... renderc config.yaml \
--input spec/guild.json \
--template templates/Client-short-operations.j2 \
--output guildService-sdk/pkg/guildserviceclient/guild_short.go
# ... and so on for each file type
各 make ターゲットは基本的に以下のようになっています。
- 同じ INPUT 1: OpenAPI スペックファイル(例:
guild.json) - 異なる INPUT 2: 特定のテンプレートファイル(例: モデル用の
operations-models.j2、ヘッダー用のClient-operations-decl.j2) - 異なる OUTPUT: 適切なディレクトリ内の適切な名前のターゲットファイル
自分の SDK でコード生成がどのように機能するかを理解するには、以下を行ってください。
- Unreal
- Unity
- Go
accelbyte-unreal-sdk-template-pack.zipをダウンロードして展開するMakefileを開く。各ターゲットで、使用されているテンプレート、入力、出力ファイルがわかるtemplates/sdk_customization/内のテンプレートファイルを確認し、Jinja2 の構文を確認する- テンプレートと生成された
.h・.cppファイルを比較して変換内容を確認する
accelbyte-unity-sdk-template-pack.zipをダウンロードして展開するMakefileを開く。各ターゲットで、使用されているテンプレート、入力、出力ファイルがわかるtemplates/sdk_customization/内のテンプレートファイルを確認し、Jinja2 の構文を確認する- テンプレートと生成された
.csファイルを比較して変換内容を確認する
accelbyte-go-sdk-template-pack.zipをダウンロードして展開するMakefileを開く。各ターゲットで、使用されているテンプレート、入力、出力ファイルがわかるtemplates/sdk_customization/内のテンプレートファイルを確認し、Jinja2 の構文を確認する- テンプレートと生成された
.goファイルを比較して変換内容を確認する
この実践的な探索が、生成処理を理解するための最良の方法です。
Jinja2 テンプレートエンジン
コード生成には、強力な Python テンプレート言語である Jinja2 を使用します。
基本のテンプレート構文
{# This is a comment - not included in output #}
{{ variable }} {# Output a variable value #}
{% for item in items %} {# Loop through a list #}
Process {{ item }}
{% endfor %}
{% if condition %} {# Conditional logic #}
Do something
{% elif other_condition %}
Do something else
{% else %}
Default action
{% endif %}
{% set my_var = "value" %} {# Set a variable #}
{% import 'module.j2' as module %} {# Import macros from another template #}
{% macro func_name(params) %} {# Define a reusable template function #}
Macro content
{% endmacro %}
SDK テンプレートの例
- Unreal
- Unity
- Go
Unreal SDK テンプレート(Client-operations-decl.j2)の実際の例を以下に示します。
{% import 'common/services.j2' as services -%}
{% set w_class_name = service.file_stem | to_pascal %}
{% set model_prefix = "FAccelByte" + service.file_stem | to_pascal %}
class ACCELBYTEUE4SDKCUSTOMIZATION_API {{ w_class_name }} : public Core::FApiBase
{
public:
{% for operation in operations %}
/**
* @brief {{ operation.summary }}.
*/
{% if operation.responses[0].field %}
{% set response_type = operation.responses[0].field | field_unreal_type(model_prefix) %}
THandler<{{ response_type }}> const& OnSuccess,
{% endif %}
{% endfor %}
};
これにより、以下のような C++ コードが生成されます。
class ACCELBYTEUE4SDKCUSTOMIZATION_API Guild : public Core::FApiBase
{
public:
/**
* @brief Create a new guild.
*/
THandler<FAccelByteGuildModelsCreateGuildResponse> const& OnSuccess,
};
Unity SDK テンプレート(Client-operations.j2)の実際の例を以下に示します。
{% set class_name = service.file_stem | to_pascal %}
{% set namespace = "AccelByte.Api" %}
namespace {{ namespace }}
{
public class {{ class_name }}
{
{% for operation in operations %}
/// <summary>
/// {{ operation.summary }}
/// </summary>
public void {{ operation.operation_id | to_pascal }}(
{% if operation.request_body %}
{{ operation.request_body.field | field_csharp_type }} body,
{% endif %}
{% if operation.responses[0].field %}
ResultCallback<{{ operation.responses[0].field | field_csharp_type }}> callback
{% endif %}
)
{
// Implementation...
}
{% endfor %}
}
}
これにより、以下のような C# コードが生成されます。
namespace AccelByte.Api
{
public class Guild
{
/// <summary>
/// Create a new guild
/// </summary>
public void CreateGuild(
CreateGuildRequest body,
ResultCallback<GuildResponse> callback
)
{
// Implementation...
}
}
}
Go SDK テンプレート(Client-operations.j2)の実際の例を以下に示します。
{% set package_name = "guildserviceclient" %}
package {{ package_name }}
import (
"github.com/AccelByte/accelbyte-go-modular-sdk/services-api/pkg/repository"
)
{% for operation in operations %}
// {{ operation.operation_id | to_pascal }} {{ operation.summary }}
func (a *{{ service.file_stem | to_pascal }}Service) {{ operation.operation_id | to_pascal }}(
{% if operation.request_body %}
body *{{ operation.request_body.field | field_go_type }},
{% endif %}
) (*{{ operation.responses[0].field | field_go_type }}, error) {
// Implementation...
}
{% endfor %}
これにより、以下のような Go コードが生成されます。
package guildserviceclient
import (
"github.com/AccelByte/accelbyte-go-modular-sdk/services-api/pkg/repository"
)
// CreateGuild Create a new guild
func (a *GuildService) CreateGuild(
body *CreateGuildRequest,
) (*GuildResponse, error) {
// Implementation...
}
カスタム Jinja2 フィルター
Extend Codegen CLI は、コード生成のために豊富なカスタムフィルターを提供します。これらのフィルターは、OpenAPI スペックのデータを言語固有のコードに変換します。
フィルターはいくつかのカテゴリーに分類されます。
ケース変換フィルター
異なる命名規則(PascalCase、camelCase、snake_case、kebab-case など)の間で変換を行います。これらは、言語固有の命名規則に従うコードを生成するために不可欠です。
使用例:
{% set class_name = service.file_stem | to_pascal %}
class {{ class_name }} {
// Converts "guild_service" to "GuildService"
}
一般的なフィルター: to_pascal、to_camel、to_snake、to_kebab、to_sentence、to_title
言語固有の型フィルター
OpenAPI の型をターゲット言語の型に変換します。各 SDK(Unreal、Unity、Go など)には、それぞれ独自の型変換フィルターのセットがあります。
Unreal C++ フィルター:
- 型変換:
field_unreal_type、unreal_class、unreal_enum_value - フォーマット:
unreal_var、unreal_enum_name、field_unreal_param - 検証:
field_unreal_is_list_type
C#(Unity)フィルター:
- 型変換:
field_csharp_type、csharp_class、csharp_enum_value - フォーマット:
csharp_prop、csharp_var、csharp_enum_name - ジェネリックの処理:
csharp_generic_names
Go フィルター:
- 型変換:
field_go_type、go_struct、go_var
例:
{% for field in model.properties %}
{{ field | field_unreal_type("FAccelByte") }} {{ field.name | to_pascal }};
// Generates: FString GuildName;
{% endfor %}
文字列操作フィルター
コード生成のために文字列を変換・操作します。
- 追加/削除:
prefix、suffix、affix、removeprefix、removesuffix - 解析:
split、strip、substr - フォーマット:
inquotes
正規表現フィルター
パターンマッチングとテキスト抽出を行います。
- 検索:
regex_search、regex_findall - フィルタリング:
alnumonly、alphaonly、digitonly、numericonly
ファイルおよびコンテンツフィルター
外部コンテンツをテンプレートに取り込みます。
filecontents- ファイルの全内容を読み込むfilesnippet- ファイルから特定の行を抽出する
ユーティリティフィルター
汎用的な変換フィルターです。
- コレクション:
flatten、merge - 条件分岐:
onlyif、ternary、boolean - 型の処理:
typename、to_csv
サンプル生成フィルター
ドキュメントやテスト用のサンプルデータを作成します。
create_example- OpenAPI スキーマに基づいてサンプル値を生成するcreate_example_json- JSON のサンプルを生成するcreate_example_wsm- WebSocket メッセージのサンプルを生成する
使用しているテンプレートパックのバージョンで利用可能なフィルターの完全なリストを確認するには、次のセクションで説明する --inspect verbose フラグを使用してください。
カスタムテンプレートでのデバッグと探索
コード生成を理解するための最も強力な方法の一つは、独自のシンプルなテンプレートを作成し、利用可能なデータやフィルターを試してみることです。
テストの前提条件
テンプレートのレンダリングをテスト・デバッグするには、テンプレートパックから以下の3つが必要です。
config.yaml- テンプレートパックの設定ファイル(すべてのテンプレートパックに含まれています)- OpenAPI スペック - API を記述する
guild.json(または類似の)ファイル - テンプレートファイル - 既存の
.j2ファイル、または独自のカスタムテストテンプレート
テストテンプレートの作成
利用可能なデータを探索するための最小限のテンプレートを作成できます。test-template.j2 というファイルを作成してください。
{# Simple template to explore available data #}
Service Information:
File Stem: {{ service.file_stem }}
Base Path: {{ service.base_path }}
Operations Count: {{ operations | length }}
First Operation (if exists):
{% if operations %}
{% set op = operations[0] %}
Summary: {{ op.summary }}
Method: {{ op.method }}
Path: {{ op.path }}
{% endif %}
{# Test filters #}
Testing Filters:
to_pascal: {{ "hello_world" | to_pascal }}
to_camel: {{ "HelloWorld" | to_camel }}
to_snake: {{ "HelloWorld" | to_snake }}
テストテンプレートの実行
- Unreal
- Unity
- Go
# From within the extracted accelbyte-unreal-sdk-template-pack/ directory
docker run --rm \
-v "$(pwd)":"$(pwd)" \
-w "$(pwd)" \
accelbyte/extend-codegen-cli:0.0.28 renderc \
config.yaml \
--input /path/to/my-unreal-project/spec/guild.json \
--template test-template.j2
# From within the extracted accelbyte-unity-sdk-template-pack/ directory
docker run --rm \
-v "$(pwd)":"$(pwd)" \
-w "$(pwd)" \
accelbyte/extend-codegen-cli:0.0.28 renderc \
config.yaml \
--input /path/to/my-unity-project/spec/guild.json \
--template test-template.j2
# From within the extracted accelbyte-go-sdk-template-pack/ directory
docker run --rm \
-v "$(pwd)":"$(pwd)" \
-w "$(pwd)" \
accelbyte/extend-codegen-cli:0.0.28 renderc \
config.yaml \
--input /path/to/my/extend-service-extension-module-sdk/spec/guild.json \
--template test-template.j2
これにより、レンダリングされたテンプレートが標準出力に出力され、以下を確認できます。
- 利用可能なデータ構造
- 各オブジェクトが持つプロパティ
- フィルターが値をどのように変換するか
--inspect verbose による詳細な探索
--inspect verbose フラグは、テンプレート環境の全体像を明らかにします。
- Unreal
- Unity
- Go
# From within accelbyte-unreal-sdk-template-pack/
docker run --rm \
-v "$(pwd)":"$(pwd)" \
-w "$(pwd)" \
accelbyte/extend-codegen-cli:0.0.28 renderc \
config.yaml \
--input /path/to/my-unreal-project/spec/guild.json \
--template test-template.j2 \
--inspect verbose
# From within accelbyte-unity-sdk-template-pack/
docker run --rm \
-v "$(pwd)":"$(pwd)" \
-w "$(pwd)" \
accelbyte/extend-codegen-cli:0.0.28 renderc \
config.yaml \
--input /path/to/my-unity-project/spec/guild.json \
--template test-template.j2 \
--inspect verbose
# From within accelbyte-go-sdk-template-pack/
docker run --rm \
-v "$(pwd)":"$(pwd)" \
-w "$(pwd)" \
accelbyte/extend-codegen-cli:0.0.28 renderc \
config.yaml \
--input /path/to/my/extend-service-extension-module-sdk/spec/guild.json \
--template test-template.j2 \
--inspect verbose
--inspect verbose が表示する内容:
- 入力プロセッサーの情報 - OpenAPI スペックがどのように解析されるか
- 利用可能なフィルター - すべての Jinja2 フィルター(200以上)の関数シグネチャを含む完全なリスト
- グローバル関数 -
range、dict、lipsumなどの組み込み関数 - テスト関数 -
is odd、is defined、is divisiblebyなどの条件式 - テンプレートローダーのパス - テンプレートが検索される場所
- シンプルに始める: 変数名だけを出力するシンプルなテンプレートを作成する
- データ構造を調べる:
{{ variable }}を使用して、各オブジェクトの中身を確認する - フィルターをテストする: サンプルデータに異なるフィルターを試し、出力を確認する
- 既存のテンプレートと比較する: テンプレートパック内のテンプレートを見て、実際の使用例を確認する
- inspect の出力を保存する:
--inspect verbose > filters.txtを実行して、後で参照できるようにする
探索用テンプレートの例:
{# Debug template - see all available data #}
Service: {{ service }}
Operations: {{ operations }}
Models: {{ models }}
反復的なテンプレート開発
推奨されるアプローチ:
- テンプレートパックを展開し、そのディレクトリに移動する
- 既存のテンプレートをコピーして出発点にする(例:
operations-models.j2) - シンプルなテストケースを作成する - 1つか2つのエンドポイントを持つ最小限の OpenAPI スペック
- 変更してテストする - 小さな変更を加えて、すぐに再実行する
--inspect verboseを使用する - 特定のフィルターを見つけたり、利用可能性を確認したいとき
よくあるデバッグシナリオ
適切なフィルターを見つける:
# Save inspect output and search it
docker run ... --inspect verbose > inspect.txt
grep "unreal" inspect.txt # Find Unreal-specific filters
grep "to_" inspect.txt # Find case conversion filters
フィルターの動作をテストする:
{# Create a test template #}
Original: guild_service
to_pascal: {{ "guild_service" | to_pascal }}
to_camel: {{ "guild_service" | to_camel }}
to_snake: {{ "GuildService" | to_snake }}
データ構造を理解する:
{# Explore what properties exist #}
{% for operation in operations %}
Operation {{ loop.index }}:
Path: {{ operation.path }}
Method: {{ operation.method }}
Summary: {{ operation.summary }}
Parameters: {{ operation.parameters | length }}
{% endfor %}
設定ファイル(config.yaml)
各テンプレートパックの config.yaml ファイルは、レンダリング処理を設定します。
input_processor: 'accelbyte_codegen.ext.openapi2.legacy.OA2LegacyProcessor'
template_processor: 'textf'
renderer: 'accelbyte_codegen.ext.openapi2.legacy.OA2LegacyRenderer'
extension:
- '*jinja' # Standard Jinja2 extensions
- 'accelbyte_codegen.ext.openapi2.legacy.OA2LegacyExtension' # Custom filters
loader:
- './templates' # Template search path
output: 'stdout'
主なフィールド:
input_processor: OpenAPI 2.0 の解析を処理するrenderer: 解析済みデータにテンプレートを適用するextension: カスタムフィルターと関数を読み込むloader: テンプレートを検索するディレクトリ
よくあるデバッグシナリオ
生成されたコードの型が間違っている
問題: OpenAPI の integer が int ではなく string として生成される。
デバッグ手順:
- OpenAPI スペックを確認する。フィールドが
"type": "integer"として正しく定義されているか確認する --inspect verboseを使用して、ターゲット言語に対応する正しい型変換フィルターを見つける- テンプレートファイルを確認し、どのフィルターが適用されているかを確認する
- フィルターが正しいパラメーターで呼び出されているか確認する
名前空間が欠落している、または間違っている
問題: 生成されたコードの名前空間やパッケージ名が間違っている。
デバッグ手順:
- OpenAPI スペックの
basePathが正しいか確認する - OpenAPI の
info.titleフィールドを確認する --inspect verboseを使用して、名前空間関連のフィルターを検索する- テンプレートが名前空間/パッケージ名をどのように構築しているかを確認する
カスタムフィルターが動作しない
問題: テンプレートでフィルターを使用しているが、出力が正しくない、または欠落している。
デバッグ手順:
--inspect verboseを実行して、使用しているテンプレートパックのバージョンにそのフィルターが存在するか確認する- テンプレート内のフィルター名のスペルを確認する
- 入力データの型がフィルターの期待する型と一致しているか確認する(フィルターは文字列とオブジェクトで異なる動作をする場合があります)
- 最小限のテンプレートでテストして、問題を切り分ける
テンプレートが見つからないエラー
問題: コード生成が「template not found」エラーで失敗する。
デバッグ手順:
--inspect verboseを実行して、テンプレートローダーのパスを確認する- テンプレートファイルが想定される場所に存在するか確認する
- テンプレートのファイル名やパスに誤字がないか確認する
- Docker のボリュームマウントにテンプレートディレクトリが含まれているか確認する
テンプレートのカスタマイズ(上級者向け)
テンプレートパックはそのまま使えるように設計されていますが、特定のニーズに合わせてカスタマイズすることも可能です。
テンプレートをカスタマイズすると、新しいテンプレートパックのバージョンへのアップグレードが難しくなる場合があります。変更内容は必ず記録してください。
カスタマイズの手順
-
テンプレートパックを展開する:
unzip accelbyte-unreal-sdk-template-pack.zip
cd accelbyte-unreal-sdk-template-pack/ -
変更するテンプレートを見つける:
テンプレートは
templates/sdk_customization/にあります。operations-decl.j2- API クラスの宣言operations-def.j2- API クラスの実装operations-models.j2- モデルの定義
-
変更を行う:
上記で説明したフィルターを使用して、Jinja2 テンプレートファイルを編集します。
-
変更をテストする:
makeコマンドを実行して、生成された出力を確認します。 -
カスタマイズ内容を記録する:
今後の参照のために、何を、なぜ変更したかをメモしておきます。
カスタマイズの例
生成されるすべての API クラスにカスタムコメントを追加する例です。
{# In operations-decl.j2 #}
class {{ w_class_name }} : public Core::FApiBase
{
// Custom comment: Generated by AccelByte Extend Codegen CLI
// OpenAPI file: {{ service.file_stem }}.json
// Generated on: {{ "now" | strftime("%Y-%m-%d") }}
public:
{% for operation in operations %}
...
{% endfor %}
};
その他のリソース
- Extend Codegen CLI GitHub Repository - ソースコードとイシュートラッカー
- Extend Codegen CLI Docker Hub - 利用可能な Docker イメージのバージョン
- Jinja2 Documentation - Jinja2 テンプレートエンジンの公式ドキュメント
- Jinja2 Template Designer Documentation - テンプレート構文の包括的なガイド
- OpenAPI 2.0 Specification - OpenAPI 2.0(Swagger)仕様のリファレンス
Summary
Extend Codegen CLI のテンプレートシステムを理解することで、以下が可能になります。
--inspect verboseを使ってコード生成の問題を効果的にデバッグする- Jinja2 テンプレートを通じて、OpenAPI スペックが生成コードにどのようにマッピングされるかを理解する
- ターゲット言語やユースケースに適したフィルターを使用する
- 必要に応じてテンプレートをカスタマイズする
Jinja2 の柔軟性と AccelByte の豊富なカスタムフィルターの組み合わせにより、単一の OpenAPI スペックから複数のプログラミング言語と SDK をターゲットにできる強力なコード生成システムが実現しています。