Skip to content

Commit 975ccff

Browse files
authored
Merge pull request #29 from dialpad/StreamlineUpdateTooling
Adds a single command to take care of routine spec-driven updates
2 parents 2306093 + c4c6867 commit 975ccff

4 files changed

Lines changed: 89 additions & 13 deletions

File tree

README.md

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -86,22 +86,21 @@ Changes/additions to the Dialpad API can be handled (mostly) automatically 👍
8686

8787
#### Update Procedure
8888

89-
- Overwrite `dialpad_api_spec.json` with the latest spec
89+
- Run `uv run cli interactive-update`
9090

91-
- Run `uv run cli preprocess-spec`
92-
- This just does a few ham-fisted inplace edits to the spec file to make the schema paths a bit nicer
91+
This will take care of pulling down the latest OAS from dialpad.com, updating the client
92+
accordingly, and bumping the package version number.
9393

94-
- Run `uv run cli update-resource-module-mapping --interactive`
95-
- This adds entries to `module_mapping.json` for any new API operations in the API spec.
96-
We (humans) get to decide the appropriate resource class and method name 👍
94+
- Run `uv run pytest`
95+
- Never hurts to confirm that nothing got borked 👍
9796

98-
![resource module mapping](./docs/images/resource_module_mapping.png)
97+
- Commit the changes, tag the commit, and push up the changes
98+
- `interactive-update` provides these in its output for convenience 👍
9999

100-
- Run `uv run cli generate-client`
101-
- This will regenerate all of the schema and resource files as per the API spec.
100+
Pushing up a version tag will trigger GHA to build, test, and publish to PyPI 🍻
102101

103-
- Run `uv run pytest`
104-
- Never hurts to confirm that nothing got borked 👍
105102

106-
TODO: version bump, build, push, publish
103+
### Feature Releases
107104

105+
The schema and resource classes in this project are now automatically-generated, but the rest of
106+
the project files can still be directly edited to add features or extend functionality

cli/client_gen/utils.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,10 @@ def write_python_file(filepath: str, module_node: ast.Module) -> None:
3737
reformat_python_file(filepath)
3838

3939
rich.print(Markdown(f'Generated `{filepath}`.'))
40+
41+
42+
def bump_patch_version() -> str:
43+
"""Bumps the patch version in pyproject.toml using uv."""
44+
return subprocess.run(
45+
['uv', 'version', '--short', '--bump', 'patch'], check=True, capture_output=True, text=True
46+
).stdout.strip()

cli/interactive_update_overview.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Interactive Client Update
2+
3+
This command will guide you through the mostly-automated process of updating this client library
4+
to match the current Dialpad API spec. To do accomplish, it will:
5+
6+
- Overwrite `dialpad_api_spec.json` with the latest OpenAPI spec from Dialpad.
7+
- Apply some light in-place preprocessing to `dialpad_api_spec.json`.
8+
- Update `module_mapping.json` to include any new API endpoints that need to be mapped.
9+
- (This is the interactive bit)
10+
- Re-generate the client resources and schemas.
11+
- Bump the package version in `pyproject.toml`
12+
13+
During the mapping update step, you will be prompted to choose the appropriate `Class` and
14+
`method()` names for any API operations that are not already mapped. The tool will suggest existing
15+
Resource class names if an API sub-path is already mapped to a particular resource class, and the
16+
suggested method names are just based on the HTTP verb (which isn't always an effective strategy).
17+
As an illustrative example, `/api/v2/users/{id}/assign_number [POST]` is mapped to
18+
`UsersResource.assign_number`.
19+
20+
This tool will only make **local changes**, so there's very little risk involved 👍
21+

cli/main.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22
import re
33
from typing import Annotated
44

5+
import requests
6+
import rich
57
import typer
68
from openapi_core import OpenAPI
9+
from rich.markdown import Markdown
710

811
from cli.client_gen.module_mapping import update_module_mapping
912
from cli.client_gen.resource_packages import resources_to_package_directory
1013
from cli.client_gen.schema_packages import schemas_to_package_directory
14+
from cli.client_gen.utils import bump_patch_version
1115

1216
REPO_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
1317
SPEC_FILE = os.path.join(REPO_ROOT, 'dialpad_api_spec.json')
@@ -35,7 +39,7 @@ def reformat_spec():
3539
with open(SPEC_FILE, 'w') as f:
3640
f.write(spec_file_contents)
3741

38-
typer.echo(f"Reformatted OpenAPI spec at '{SPEC_FILE}'")
42+
rich.print(Markdown(f"Reformatted OpenAPI spec at `{SPEC_FILE}`"))
3943

4044

4145
@app.command('update-resource-module-mapping')
@@ -64,6 +68,51 @@ def generate_client():
6468
# Write the generated resource modules to the client directory
6569
resources_to_package_directory(open_api_spec.spec, os.path.join(CLIENT_DIR, 'resources'))
6670

71+
@app.command('interactive-update')
72+
def interactive_update():
73+
"""The one-stop-shop for updating the Dialpad client with the latest dialpad api spec."""
74+
75+
# Start by printing an overview of what this command will do, and wait for user confirmation
76+
with open(os.path.join(REPO_ROOT, 'cli', 'interactive_update_overview.md'), 'r') as f:
77+
overview_content = f.read()
78+
79+
rich.print(Markdown(overview_content))
80+
81+
if not typer.confirm("Do you want to proceed with the update?"):
82+
rich.print(Markdown("Update cancelled by user."))
83+
raise typer.Exit(0)
84+
85+
api_spec_url = 'https://dialpad.com/static/openapi/platform-v1.0.json'
86+
rich.print(Markdown(f"Fetching OpenAPI spec from `{api_spec_url}`..."))
87+
88+
response = requests.get(api_spec_url)
89+
if response.status_code == 200:
90+
with open(SPEC_FILE, 'wb') as f:
91+
f.write(response.content)
92+
rich.print(Markdown(f"Downloaded OpenAPI spec to `{SPEC_FILE}`"))
93+
else:
94+
rich.print(Markdown(f"Failed to download OpenAPI spec: `{response.status_code}`"))
95+
raise typer.Exit(1)
96+
97+
reformat_spec()
98+
update_resource_module_mapping(interactive=True)
99+
generate_client()
100+
new_version_str = bump_patch_version()
101+
102+
rich.print("\n")
103+
rich.print(Markdown(f"Bumped version to `{new_version_str}` 🎉"))
104+
rich.print(Markdown('\n'.join([
105+
'Recommended next steps:',
106+
'- Review the changes with `git diff`',
107+
'- Run the test suite with `uv run pytest`',
108+
])))
109+
rich.print("\n")
110+
rich.print(Markdown('\n'.join([
111+
'If everything looks good, then you can tag and push to publish the new version to PyPI:',
112+
f'- `git tag -a "v{new_version_str}" -m "Release version {new_version_str}"`',
113+
f'- `git push origin "v{new_version_str}"`',
114+
])))
115+
67116

68117
if __name__ == '__main__':
69118
app()

0 commit comments

Comments
 (0)