Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for ARC56 typed client generation #595

Merged
merged 2 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/cli/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions docs/features/generate.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand All @@ -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

Expand Down
4 changes: 2 additions & 2 deletions src/algokit/cli/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand All @@ -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:
Expand Down
9 changes: 5 additions & 4 deletions src/algokit/cli/project/link.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import typing
from dataclasses import dataclass
from itertools import chain
from pathlib import Path

import click
Expand Down Expand Up @@ -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
Expand Down
10 changes: 9 additions & 1 deletion src/algokit/core/typed_client_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,15 @@ 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"]
contract_name: str = (
neilcampbell marked this conversation as resolved.
Show resolved Hide resolved
application_json["name"]
if "name" in application_json
else application_json["contract"]["name"]
if "contract" in application_json and "name" in application_json["contract"]
else ""
)
if contract_name == "":
raise ValueError("Contract name not found")
except Exception:
logger.error(f"Couldn't parse contract name from {app_spec}", exc_info=True)
return None
Expand Down
25 changes: 25 additions & 0 deletions tests/generate/test_generate_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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"),
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
WARNING: No application.json | *.arc32.json files found in <cwd>/projects/project3. Skipping...
WARNING: No application.json | *.arc32.json | *.arc56.json files found in <cwd>/projects/project3. Skipping...
✅ 1/2: Finished processing contract_project_3
WARNING: No application.json | *.arc32.json files found in <cwd>/projects/project5. Skipping...
WARNING: No application.json | *.arc32.json | *.arc56.json files found in <cwd>/projects/project5. Skipping...
✅ 2/2: Finished processing contract_project_5
Loading