Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
23 changes: 11 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 7 additions & 0 deletions cli/client_gen/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,10 @@ 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."""
return subprocess.run(
['uv', 'version', '--short', '--bump', 'patch'], check=True, capture_output=True, text=True
).stdout.strip()
21 changes: 21 additions & 0 deletions cli/interactive_update_overview.md
Original file line number Diff line number Diff line change
@@ -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 👍

51 changes: 50 additions & 1 deletion cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
import re
from typing import Annotated

import requests
import rich
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
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')
Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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()