diff --git a/docs/cli/index.md b/docs/cli/index.md index a2d99a280..70f6deb3f 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -541,7 +541,7 @@ algokit generate [OPTIONS] COMMAND [ARGS]... ### client -Create a typed ApplicationClient from an ARC-32 application.json +Create a typed ApplicationClient from an ARC-32/56 application.json Supply the path to an application specification file or a directory to recursively search for "application.json" files diff --git a/docs/features/generate.md b/docs/features/generate.md index c9af99ceb..7e3a81ace 100644 --- a/docs/features/generate.md +++ b/docs/features/generate.md @@ -4,7 +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) or [ARC-0056](https://github.com/algorandfoundation/ARCs/pull/258) application specification with both Python and TypeScript available as target languages. ### Prerequisites @@ -15,7 +15,7 @@ Each generated client will also have a dependency on `algokit-utils` libraries f ### 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). +You can either specify a path to an ARC-0032 JSON file, an ARC-0056 JSON file or to a directory that is recursively scanned for `application.json`, `*.arc32.json`, `*.arc56.json` file(s). ### Output tokens @@ -24,8 +24,8 @@ 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 the `application.json` or `*.arc32.json` file which can be useful to output a client relative to its source file. +- `{contract_name}`: This will resolve to a name based on the ARC-0032/ARC-0056 contract name, formatted appropriately for the target language. +- `{app_spec_dir}`: This will resolve to the parent directory of the `application.json`, `*.arc32.json`, `*.arc56.json` file which can be useful to output a client relative to its source file. ### Version Pinning diff --git a/src/algokit/cli/generate.py b/src/algokit/cli/generate.py index 76346194d..cb977ab35 100644 --- a/src/algokit/cli/generate.py +++ b/src/algokit/cli/generate.py @@ -141,7 +141,7 @@ def generate_group() -> None: def generate_client( output_path_pattern: str | None, app_spec_path_or_dir: Path, language: str | None, version: str | None ) -> None: - """Create a typed ApplicationClient from an ARC-32 application.json + """Create a typed ApplicationClient from an ARC-32/56 application.json Supply the path to an application specification file or a directory to recursively search for "application.json" files""" @@ -164,7 +164,7 @@ def generate_client( if not app_spec_path_or_dir.is_dir(): app_specs = [app_spec_path_or_dir] else: - patterns = ["application.json", "*.arc32.json"] + patterns = ["application.json", "*.arc32.json", "*.arc56.json"] app_specs = [] for pattern in patterns: diff --git a/src/algokit/cli/project/link.py b/src/algokit/cli/project/link.py index 7646e55da..7f28f122b 100644 --- a/src/algokit/cli/project/link.py +++ b/src/algokit/cli/project/link.py @@ -1,6 +1,7 @@ import logging import typing from dataclasses import dataclass +from itertools import chain from pathlib import Path import click @@ -85,12 +86,12 @@ def _link_projects( """ output_path_pattern = f"{frontend_clients_path}/{{contract_name}}.{'ts' if language == 'typescript' else 'py'}" generator = ClientGenerator.create_for_language(language, version=version) - app_specs = list(contract_project_root.rglob("application.json")) + list( - contract_project_root.rglob("*.arc32.json") - ) + file_patterns = ["application.json", "*.arc32.json", "*.arc56.json"] + app_specs = list(chain.from_iterable(contract_project_root.rglob(pattern) for pattern in file_patterns)) if not app_specs: click.secho( - f"WARNING: No application.json | *.arc32.json files found in {contract_project_root}. Skipping...", + f"WARNING: No application.json | *.arc32.json | *.arc56.json files found in {contract_project_root}. " + "Skipping...", fg="yellow", ) return diff --git a/src/algokit/core/typed_client_generation.py b/src/algokit/core/typed_client_generation.py index 5c6a35e63..556145f24 100644 --- a/src/algokit/core/typed_client_generation.py +++ b/src/algokit/core/typed_client_generation.py @@ -58,7 +58,10 @@ def create_for_extension(cls, extension: str, version: str | None) -> "ClientGen def resolve_output_path(self, app_spec: Path, output_path_pattern: str | None) -> Path | None: try: application_json = json.loads(app_spec.read_text()) - contract_name: str = application_json["contract"]["name"] + try: + contract_name: str = application_json["name"] # ARC-56 + except KeyError: + contract_name = application_json["contract"]["name"] # ARC-32 except Exception: logger.error(f"Couldn't parse contract name from {app_spec}", exc_info=True) return None diff --git a/tests/generate/test_generate_client.py b/tests/generate/test_generate_client.py index 3d409a90d..ee5cdbae5 100644 --- a/tests/generate/test_generate_client.py +++ b/tests/generate/test_generate_client.py @@ -76,6 +76,11 @@ def arc32_json(cwd: Path, dir_with_app_spec_factory: DirWithAppSpecFactory) -> P return dir_with_app_spec_factory(cwd, "app.arc32.json") +@pytest.fixture() +def arc56_json(cwd: Path, dir_with_app_spec_factory: DirWithAppSpecFactory) -> Path: + return dir_with_app_spec_factory(cwd, "app.arc56.json") + + @pytest.fixture(autouse=True) def which_mock(mocker: MockerFixture) -> WhichMock: which_mock = WhichMock() @@ -211,6 +216,26 @@ def test_generate_client_python_arc32_filename( assert proc_mock.called[3].command == _get_python_generate_command(None, arc32_json, expected_output_path).split() +@pytest.mark.parametrize( + ("options", "expected_output_path"), + [ + ("-o client.py", "client.py"), + ], +) +def test_generate_client_python_arc56_filename( + proc_mock: ProcMock, arc56_json: Path, options: str, expected_output_path: Path +) -> None: + proc_mock.should_bad_exit_on(["poetry", "show", PYTHON_PYPI_PACKAGE, "--tree"]) + proc_mock.should_bad_exit_on(["pipx", "list", "--short"]) + + result = invoke(f"generate client {options} {arc56_json.name}", cwd=arc56_json.parent) + + assert result.exit_code == 0 + verify(_normalize_output(result.output), options=NamerFactory.with_parameters(*options.split())) + assert len(proc_mock.called) == 4 # noqa: PLR2004 + assert proc_mock.called[3].command == _get_python_generate_command(None, arc56_json, expected_output_path).split() + + @pytest.mark.usefixtures("mock_platform_system") @pytest.mark.parametrize( ("options", "expected_output_path"), diff --git a/tests/generate/test_generate_client.test_generate_client_python_arc56_filename.-o.client.py.approved.txt b/tests/generate/test_generate_client.test_generate_client_python_arc56_filename.-o.client.py.approved.txt new file mode 100644 index 000000000..0025cbc82 --- /dev/null +++ b/tests/generate/test_generate_client.test_generate_client_python_arc56_filename.-o.client.py.approved.txt @@ -0,0 +1,18 @@ +DEBUG: Searching for project installed client generator +DEBUG: Running 'poetry show algokit-client-generator --tree' in '{current_working_directory}' +DEBUG: poetry: STDOUT +DEBUG: poetry: STDERR +DEBUG: Running 'pipx --version' in '{current_working_directory}' +DEBUG: pipx: STDOUT +DEBUG: pipx: STDERR +DEBUG: Searching for globally installed client generator +DEBUG: Running 'pipx list --short' in '{current_working_directory}' +DEBUG: pipx: STDOUT +DEBUG: pipx: STDERR +DEBUG: No matching installed client generator found, run client generator via pipx +Generating Python client code for application specified in {current_working_directory}/app.arc56.json and writing to client.py +DEBUG: Running 'pipx run --spec=algokit-client-generator algokitgen-py -a {current_working_directory}/app.arc56.json -o client.py' in '{current_working_directory}' +DEBUG: pipx: STDOUT +DEBUG: pipx: STDERR +STDOUT +STDERR diff --git a/tests/generate/test_generate_client.test_generate_help.approved.txt b/tests/generate/test_generate_client.test_generate_help.approved.txt index c7c22bafe..250f7edf1 100644 --- a/tests/generate/test_generate_client.test_generate_help.approved.txt +++ b/tests/generate/test_generate_client.test_generate_help.approved.txt @@ -8,4 +8,4 @@ Options: -h, --help Show this message and exit. Commands: - client Create a typed ApplicationClient from an ARC-32 application.json + client Create a typed ApplicationClient from an ARC-32/56 application.json diff --git a/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_invalid_generic_generator.approved.txt b/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_invalid_generic_generator.approved.txt index 71f683116..f68ce022c 100644 --- a/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_invalid_generic_generator.approved.txt +++ b/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_invalid_generic_generator.approved.txt @@ -9,4 +9,4 @@ Options: -h, --help Show this message and exit. Commands: - client Create a typed ApplicationClient from an ARC-32 application.json + client Create a typed ApplicationClient from an ARC-32/56 application.json diff --git a/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_no_toml.approved.txt b/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_no_toml.approved.txt index c7c22bafe..250f7edf1 100644 --- a/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_no_toml.approved.txt +++ b/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_no_toml.approved.txt @@ -8,4 +8,4 @@ Options: -h, --help Show this message and exit. Commands: - client Create a typed ApplicationClient from an ARC-32 application.json + client Create a typed ApplicationClient from an ARC-32/56 application.json diff --git a/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_valid_generator.approved.txt b/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_valid_generator.approved.txt index ca88bb990..35b570178 100644 --- a/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_valid_generator.approved.txt +++ b/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_valid_generator.approved.txt @@ -7,5 +7,5 @@ Options: -h, --help Show this message and exit. Commands: - client Create a typed ApplicationClient from an ARC-32... + client Create a typed ApplicationClient from an ARC-32/56... smart-contract Generates a new smart contract diff --git a/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_valid_generator_invalid_path.approved.txt b/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_valid_generator_invalid_path.approved.txt index 02a1f64bd..83c714a0d 100644 --- a/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_valid_generator_invalid_path.approved.txt +++ b/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_valid_generator_invalid_path.approved.txt @@ -8,4 +8,4 @@ Options: -h, --help Show this message and exit. Commands: - client Create a typed ApplicationClient from an ARC-32 application.json + client Create a typed ApplicationClient from an ARC-32/56 application.json diff --git a/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_valid_generator_no_description.approved.txt b/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_valid_generator_no_description.approved.txt index 91839b13c..8dc4fa1e1 100644 --- a/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_valid_generator_no_description.approved.txt +++ b/tests/generate/test_generate_custom_generate_commands.test_generate_custom_generate_commands_valid_generator_no_description.approved.txt @@ -7,5 +7,5 @@ Options: -h, --help Show this message and exit. Commands: - client Create a typed ApplicationClient from an ARC-32... + client Create a typed ApplicationClient from an ARC-32/56... smart-contract Generator command description is not supplied. diff --git a/tests/project/link/test_link.test_link_command_multiple_names_no_specs_success.approved.txt b/tests/project/link/test_link.test_link_command_multiple_names_no_specs_success.approved.txt index 02c4f3680..6f3ec17c6 100644 --- a/tests/project/link/test_link.test_link_command_multiple_names_no_specs_success.approved.txt +++ b/tests/project/link/test_link.test_link_command_multiple_names_no_specs_success.approved.txt @@ -1,4 +1,4 @@ -WARNING: No application.json | *.arc32.json files found in /projects/project3. Skipping... +WARNING: No application.json | *.arc32.json | *.arc56.json files found in /projects/project3. Skipping... ✅ 1/2: Finished processing contract_project_3 -WARNING: No application.json | *.arc32.json files found in /projects/project5. Skipping... +WARNING: No application.json | *.arc32.json | *.arc56.json files found in /projects/project5. Skipping... ✅ 2/2: Finished processing contract_project_5