diff --git a/docs/features/generate.md b/docs/features/generate.md index d7c722fc..15dde010 100644 --- a/docs/features/generate.md +++ b/docs/features/generate.md @@ -4,8 +4,7 @@ The `algokit generate` [command](../cli/index.md#generate) is used to generate c ## 1. Typed clients -The `algokit generate client` [command](../cli/index.md#client) can be used to generate a typed client from an [ARC-0032](https://arc.algorand.foundation/ARCs/arc-0032) application specification with -both Python and TypeScript available as target languages. +The `algokit generate client` [command](../cli/index.md#client) can be used to generate a typed client from an [ARC-0032](https://arc.algorand.foundation/ARCs/arc-0032) application specification with both Python and TypeScript available as target languages. ### Prerequisites @@ -17,6 +16,10 @@ Each generated client will also have a dependency on `algokit-utils` libraries f - Python clients require: `algokit-utils@^1.2` - TypeScript clients require: `@algorandfoundation/algokit-utils@^2.0` +### Input file / directory + +You can either specify a path to a ARC-0032 JSON file, or to a directory that is recursively scanned for `application.json` or `*.arc32.json` file(s). + ### Output tokens The output path is interpreted as relative to the current working directory, however an absolute path may also be specified e.g. @@ -25,7 +28,7 @@ The output path is interpreted as relative to the current working directory, how There are two tokens available for use with the `-o`, `--output` [option](../cli/index.md#-o---output-): - `{contract_name}`: This will resolve to a name based on the ARC-0032 contract name, formatted appropriately for the target language. -- `{app_spec_dir}`: This will resolve to the parent directory of an `application.json` which can be useful to output a client relative to its source application.json. +- `{app_spec_dir}`: This will resolve to the parent directory of the `application.json` or `*.arc32.json` file which can be useful to output a client relative to its source file. ### Usage @@ -59,24 +62,24 @@ response = app_client.hello(name="World") ```typescript // A similar working example can be seen in the beaker_production template, when using TypeScript deployment -import { HelloWorldAppClient } from './artifacts/HelloWorldApp/client' +import { HelloWorldAppClient } from "./artifacts/HelloWorldApp/client"; const appClient = new HelloWorldAppClient( { - resolveBy: 'creatorAndName', + resolveBy: "creatorAndName", findExistingUsing: indexer, sender: deployer, creatorAddress: deployer.addr, }, - algod, -) + algod +); const app = await appClient.deploy({ allowDelete: isLocal, allowUpdate: isLocal, - onSchemaBreak: isLocal ? 'replace' : 'fail', - onUpdate: isLocal ? 'update' : 'fail', -}) -const response = await appClient.hello({ name: 'world' }) + onSchemaBreak: isLocal ? "replace" : "fail", + onUpdate: isLocal ? "update" : "fail", +}); +const response = await appClient.hello({ name: "world" }); ``` ### Examples diff --git a/src/algokit/cli/generate.py b/src/algokit/cli/generate.py index c247dc75..622ba078 100644 --- a/src/algokit/cli/generate.py +++ b/src/algokit/cli/generate.py @@ -125,7 +125,14 @@ def generate_client(output_path_pattern: str | None, app_spec_path_or_dir: Path, if not app_spec_path_or_dir.is_dir(): app_specs = [app_spec_path_or_dir] else: - app_specs = sorted(app_spec_path_or_dir.rglob("application.json")) + patterns = ["application.json", "*.arc32.json"] + + app_specs = [] + for pattern in patterns: + app_specs.extend(app_spec_path_or_dir.rglob(pattern)) + + app_specs = list(set(app_specs)) + app_specs.sort() if not app_specs: raise click.ClickException("No app specs found") for app_spec in app_specs: diff --git a/tests/generate/test_generate_client.py b/tests/generate/test_generate_client.py index c3badeb4..4cd74a25 100644 --- a/tests/generate/test_generate_client.py +++ b/tests/generate/test_generate_client.py @@ -13,7 +13,7 @@ from tests.utils.proc_mock import ProcMock from tests.utils.which_mock import WhichMock -DirWithAppSpecFactory = Callable[[Path], Path] +DirWithAppSpecFactory = Callable[[Path, str], Path] def _normalize_output(output: str) -> str: @@ -29,9 +29,9 @@ def cwd(tmp_path_factory: TempPathFactory) -> Path: def dir_with_app_spec_factory() -> DirWithAppSpecFactory: app_spec_example_path = Path(__file__).parent / "application.json" - def factory(app_spec_dir: Path) -> Path: + def factory(app_spec_dir: Path, app_spec_file_name: str) -> Path: app_spec_dir.mkdir(exist_ok=True, parents=True) - app_spec_path = app_spec_dir / "application.json" + app_spec_path = app_spec_dir / app_spec_file_name shutil.copy(app_spec_example_path, app_spec_path) return app_spec_path @@ -40,7 +40,12 @@ def factory(app_spec_dir: Path) -> Path: @pytest.fixture() def application_json(cwd: Path, dir_with_app_spec_factory: DirWithAppSpecFactory) -> Path: - return dir_with_app_spec_factory(cwd) + return dir_with_app_spec_factory(cwd, "application.json") + + +@pytest.fixture() +def arc32_json(cwd: Path, dir_with_app_spec_factory: DirWithAppSpecFactory) -> Path: + return dir_with_app_spec_factory(cwd, "app.arc32.json") @pytest.fixture(autouse=True) @@ -82,6 +87,21 @@ def test_generate_client_python(application_json: Path, options: str, expected_o assert (application_json.parent / expected_output_path).read_text() +@pytest.mark.parametrize( + ("options", "expected_output_path"), + [ + ("-o client.py", "client.py"), + ], +) +def test_generate_client_python_arc32_filename(arc32_json: Path, options: str, expected_output_path: Path) -> None: + result = invoke(f"generate client {options} {arc32_json.name}", cwd=arc32_json.parent) + + assert result.exit_code == 0 + verify(_normalize_output(result.output), options=NamerFactory.with_parameters(*options.split())) + assert (arc32_json.parent / expected_output_path).exists() + assert (arc32_json.parent / expected_output_path).read_text() + + @pytest.mark.parametrize( ("options", "expected_output_path"), [ @@ -133,7 +153,7 @@ def test_generate_client_recursive(cwd: Path, dir_with_app_spec_factory: DirWith cwd / "dir2" / "sub_dir", ] for dir_path in dir_paths: - dir_with_app_spec_factory(dir_path) + dir_with_app_spec_factory(dir_path, "application.json") result = invoke("generate client -o {app_spec_dir}/output.py .", cwd=cwd) assert result.exit_code == 0 diff --git a/tests/generate/test_generate_client.test_generate_client_python_arc32_filename.-o.client.py.approved.txt b/tests/generate/test_generate_client.test_generate_client_python_arc32_filename.-o.client.py.approved.txt new file mode 100644 index 00000000..85d4eacb --- /dev/null +++ b/tests/generate/test_generate_client.test_generate_client_python_arc32_filename.-o.client.py.approved.txt @@ -0,0 +1,2 @@ +Generating Python client code for application specified in {current_working_directory}/app.arc32.json and writing to client.py +Output typed client for HelloWorldApp to client.py