From 99c1635f4928d047bd1dc1b19a0a6d27ba1bb4a7 Mon Sep 17 00:00:00 2001 From: Jake Nielsen Date: Thu, 12 Jun 2025 16:12:02 -0700 Subject: [PATCH 1/4] Adds a single command to take care of routine spec-driven updates --- cli/client_gen/utils.py | 10 ++++++ cli/interactive_update_overview.md | 21 ++++++++++++ cli/main.py | 51 +++++++++++++++++++++++++++++- 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 cli/interactive_update_overview.md diff --git a/cli/client_gen/utils.py b/cli/client_gen/utils.py index 1915168..264b949 100644 --- a/cli/client_gen/utils.py +++ b/cli/client_gen/utils.py @@ -37,3 +37,13 @@ def write_python_file(filepath: str, module_node: ast.Module) -> None: reformat_python_file(filepath) rich.print(Markdown(f'Generated `{filepath}`.')) + + +def bump_patch_version() -> str: + """Bumps the patch version in pyproject.toml using uv.""" + subprocess.run( + ['uv', 'version', '--bump', 'patch'], check=True, capture_output=True, text=True + ) + return subprocess.run( + ['uv', 'version', '--short'], check=True, capture_output=True, text=True + ).stdout.strip() diff --git a/cli/interactive_update_overview.md b/cli/interactive_update_overview.md new file mode 100644 index 0000000..c2468b4 --- /dev/null +++ b/cli/interactive_update_overview.md @@ -0,0 +1,21 @@ +# Interactive Client Update + +This command will guide you through the mostly-automated process of updating this client library +to match the current Dialpad API spec. To do accomplish, it will: + +- Overwrite `dialpad_api_spec.json` with the latest OpenAPI spec from Dialpad. +- Apply some light in-place preprocessing to `dialpad_api_spec.json`. +- Update `module_mapping.json` to include any new API endpoints that need to be mapped. + - (This is the interactive bit) +- Re-generate the client resources and schemas. +- Bump the package version in `pyproject.toml` + +During the mapping update step, you will be prompted to choose the appropriate `Class` and +`method()` names for any API operations that are not already mapped. The tool will suggest existing +Resource class names if an API sub-path is already mapped to a particular resource class, and the +suggested method names are just based on the HTTP verb (which isn't always an effective strategy). +As an illustrative example, `/api/v2/users/{id}/assign_number [POST]` is mapped to +`UsersResource.assign_number`. + +This tool will only make **local changes**, so there's very little risk involved 👍 + diff --git a/cli/main.py b/cli/main.py index e54816e..e350e48 100644 --- a/cli/main.py +++ b/cli/main.py @@ -3,11 +3,15 @@ from typing import Annotated import typer +import requests +import rich +from rich.markdown import Markdown from openapi_core import OpenAPI from cli.client_gen.module_mapping import update_module_mapping from cli.client_gen.resource_packages import resources_to_package_directory from cli.client_gen.schema_packages import schemas_to_package_directory +from cli.client_gen.utils import bump_patch_version REPO_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) SPEC_FILE = os.path.join(REPO_ROOT, 'dialpad_api_spec.json') @@ -35,7 +39,7 @@ def reformat_spec(): with open(SPEC_FILE, 'w') as f: f.write(spec_file_contents) - typer.echo(f"Reformatted OpenAPI spec at '{SPEC_FILE}'") + rich.print(Markdown(f"Reformatted OpenAPI spec at `{SPEC_FILE}`")) @app.command('update-resource-module-mapping') @@ -64,6 +68,51 @@ def generate_client(): # Write the generated resource modules to the client directory resources_to_package_directory(open_api_spec.spec, os.path.join(CLIENT_DIR, 'resources')) +@app.command('interactive-update') +def interactive_update(): + """The one-stop-shop for updating the Dialpad client with the latest dialpad api spec.""" + + # Start by printing an overview of what this command will do, and wait for user confirmation + with open(os.path.join(REPO_ROOT, 'cli', 'interactive_update_overview.md'), 'r') as f: + overview_content = f.read() + + rich.print(Markdown(overview_content)) + + if not typer.confirm("Do you want to proceed with the update?"): + rich.print(Markdown("Update cancelled by user.")) + raise typer.Exit(0) + + api_spec_url = 'https://dialpad.com/static/openapi/platform-v1.0.json' + rich.print(Markdown(f"Fetching OpenAPI spec from `{api_spec_url}`...")) + + response = requests.get(api_spec_url) + if response.status_code == 200: + with open(SPEC_FILE, 'wb') as f: + f.write(response.content) + rich.print(Markdown(f"Downloaded OpenAPI spec to `{SPEC_FILE}`")) + else: + rich.print(Markdown(f"Failed to download OpenAPI spec: `{response.status_code}`")) + raise typer.Exit(1) + + reformat_spec() + update_resource_module_mapping(interactive=True) + generate_client() + new_version_str = bump_patch_version() + + rich.print("\n") + rich.print(Markdown(f"Bumped version to `{new_version_str}` 🎉")) + rich.print(Markdown('\n'.join([ + 'Recommended next steps:', + '- Review the changes with `git diff`', + '- Run the test suite with `uv run pytest`', + ]))) + rich.print("\n") + rich.print(Markdown('\n'.join([ + 'If everything looks good, then you can tag and push to publish the new version to PyPI:', + f'- `git tag -a "v{new_version_str}" -m "Release version {new_version_str}"`', + f'- `git push origin "v{new_version_str}"`', + ]))) + if __name__ == '__main__': app() From 882602a4ea4fdad81792616077dc924ba93a53a0 Mon Sep 17 00:00:00 2001 From: Jake Nielsen Date: Fri, 13 Jun 2025 11:05:18 -0700 Subject: [PATCH 2/4] Removes needless second version cmd call --- cli/client_gen/utils.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cli/client_gen/utils.py b/cli/client_gen/utils.py index 264b949..df790c5 100644 --- a/cli/client_gen/utils.py +++ b/cli/client_gen/utils.py @@ -41,9 +41,6 @@ def write_python_file(filepath: str, module_node: ast.Module) -> None: def bump_patch_version() -> str: """Bumps the patch version in pyproject.toml using uv.""" - subprocess.run( - ['uv', 'version', '--bump', 'patch'], check=True, capture_output=True, text=True - ) return subprocess.run( - ['uv', 'version', '--short'], check=True, capture_output=True, text=True + ['uv', 'version', '--short', '--bump', 'patch'], check=True, capture_output=True, text=True ).stdout.strip() From cbde58e25853e20959fb9ac2d3d8bbdacee9d362 Mon Sep 17 00:00:00 2001 From: Jake Nielsen Date: Fri, 13 Jun 2025 11:44:22 -0700 Subject: [PATCH 3/4] Updates readme --- README.md | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8177a4f..4c34d83 100644 --- a/README.md +++ b/README.md @@ -86,22 +86,21 @@ Changes/additions to the Dialpad API can be handled (mostly) automatically 👍 #### Update Procedure -- Overwrite `dialpad_api_spec.json` with the latest spec +- Run `uv run cli interactive-update` -- Run `uv run cli preprocess-spec` - - This just does a few ham-fisted inplace edits to the spec file to make the schema paths a bit nicer +This will take care of pulling down the latest OAS from dialpad.com, updating the client +accordingly, and bumping the package version number. -- Run `uv run cli update-resource-module-mapping --interactive` - - This adds entries to `module_mapping.json` for any new API operations in the API spec. - We (humans) get to decide the appropriate resource class and method name 👍 +- Run `uv run pytest` + - Never hurts to confirm that nothing got borked 👍 -![resource module mapping](./docs/images/resource_module_mapping.png) +- Commit the changes, tag the commit, and push up the changes + - `interactive-update` provides these in its output for convenience 👍 -- Run `uv run cli generate-client` - - This will regenerate all of the schema and resource files as per the API spec. +Pushing up a version tag will trigger GHA to build, test, and publish to PyPI 🍻 -- Run `uv run pytest` - - Never hurts to confirm that nothing got borked 👍 -TODO: version bump, build, push, publish +### Feature Releases +The schema and resource classes in this project are now automatically-generated, but the rest of +the project files can still be directly edited to add features or extend functionality From c4c686769aa245ad5ff66d3d62970b250221660e Mon Sep 17 00:00:00 2001 From: Jake Nielsen Date: Fri, 13 Jun 2025 11:45:49 -0700 Subject: [PATCH 4/4] Format fix --- cli/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/main.py b/cli/main.py index e350e48..867f23d 100644 --- a/cli/main.py +++ b/cli/main.py @@ -2,11 +2,11 @@ import re from typing import Annotated -import typer import requests import rich -from rich.markdown import Markdown +import typer from openapi_core import OpenAPI +from rich.markdown import Markdown from cli.client_gen.module_mapping import update_module_mapping from cli.client_gen.resource_packages import resources_to_package_directory