Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
870de26
Adds minimal working conversion to py3-only and uv-managed
jakedialpad Jun 2, 2025
65b0a6a
Uses up-to-date spec for unit test mocks
jakedialpad Jun 2, 2025
d68e102
Removes deprecated endpoints
jakedialpad Jun 3, 2025
5a3ed2f
Adds a dev CLI tool stub
jakedialpad Jun 4, 2025
fe0977d
Adds annotation generation util methods and tests
jakedialpad Jun 4, 2025
32065b7
Moves the openapi spec to a module fixture
jakedialpad Jun 4, 2025
4018e67
Adds description to annotation.py
jakedialpad Jun 4, 2025
3eb0bb2
Adds a test for http_method_to_func_def as well as a simple stub for …
jakedialpad Jun 4, 2025
53b0409
Adds a first draft implementation for the http method function signature
jakedialpad Jun 4, 2025
844a664
Adds a docstring generation piece that gemini came up with
jakedialpad Jun 4, 2025
c7745a6
Adds a similar bit for resource class definitions
jakedialpad Jun 5, 2025
4f9d6a0
Adds a simple generator method and test for resource module definitions
jakedialpad Jun 5, 2025
abfa7ec
Adds a simple generate-module cli command
jakedialpad Jun 5, 2025
e812f29
Adds copilot instructions
jakedialpad Jun 5, 2025
aa27a7b
Improves module generation command
jakedialpad Jun 5, 2025
68ae416
Updates the CLI gen tool to optionally take the API path via a CLI pa…
jakedialpad Jun 5, 2025
fd051ee
Adds the current user-id API module result verbatim into client_gen_e…
jakedialpad Jun 5, 2025
40f4bd5
Moves test_client_gen into the client_gen_tests directory
jakedialpad Jun 5, 2025
5e65597
Beefs up the client gen exemplar test output
jakedialpad Jun 5, 2025
44cc92f
Adds copilot instruction for early-return preference
jakedialpad Jun 5, 2025
7c2d94e
Adds better pytest default flags
jakedialpad Jun 5, 2025
1daf75d
Adds small desired behaviour change via the user ID resource exemplar
jakedialpad Jun 5, 2025
e573d53
Adds workaround for copilot to read test output
jakedialpad Jun 5, 2025
691aa7a
Adds appropriate method naming
jakedialpad Jun 5, 2025
66bb830
More instruction nuance
jakedialpad Jun 5, 2025
998e961
Adds schema class generation
jakedialpad Jun 6, 2025
db0fae2
Makes pytest defaults include locals in the stack trace
jakedialpad Jun 6, 2025
e82c6d9
Adds schema module generation and completeness test
jakedialpad Jun 6, 2025
9c6e184
Adds CLI command to generate schema modules
jakedialpad Jun 6, 2025
a6e4167
Fix annotation order mistake
jakedialpad Jun 6, 2025
47b3318
Fix schema class name mistake
jakedialpad Jun 6, 2025
b55f2c0
Fix innapropriate use of the TypedDict "total" kwarg
jakedialpad Jun 6, 2025
a6d39c2
Fix incorrect import
jakedialpad Jun 6, 2025
a82e304
Adds a concrete schema module exemplar as well
jakedialpad Jun 6, 2025
e58a667
Adds office schema module exemplar test
jakedialpad Jun 6, 2025
9e1a178
Moves the python file writing-and-reformatting logic into a separate …
jakedialpad Jun 6, 2025
e9f757e
Makes the file generation output a bit nicer
jakedialpad Jun 6, 2025
d6bf5b4
Adds a full schema package generation command
jakedialpad Jun 6, 2025
fcd2ae4
Pins requests because the error message is annoying
jakedialpad Jun 6, 2025
3cc178e
Whoops
jakedialpad Jun 6, 2025
8f2070d
Adds a disgusting but effective spec preprocessing command to make th…
jakedialpad Jun 6, 2025
7aa5e9b
Updates the schema module generation to account for dependency imports
jakedialpad Jun 6, 2025
93006b7
Updates the resource module generation to include the appropriate sch…
jakedialpad Jun 6, 2025
a352ac8
Adds class-level docstrings on the TypedDict schema definitions
jakedialpad Jun 6, 2025
68980b7
Adds field docstrings as well
jakedialpad Jun 6, 2025
d63cdcf
Removes mostly-erroneous Optional annotations
jakedialpad Jun 6, 2025
4f928f6
Removes the existing resource methods for now, and updates the base c…
jakedialpad Jun 9, 2025
e53b47c
Makes the resource method generation closer to correct
jakedialpad Jun 9, 2025
d9895bf
Adds a simple tool to allow the maintainer to determine the resource …
jakedialpad Jun 9, 2025
05dca4e
Makes the tool a bit smoother and adds a few entries
jakedialpad Jun 9, 2025
c8df4ab
Painstakingly maps operations to class names and method names
jakedialpad Jun 9, 2025
f4e0a46
Adds module-mapping-aware resource packaging
jakedialpad Jun 10, 2025
fdf5a75
Properly reformats the client gen files
jakedialpad Jun 10, 2025
aa5c0b7
Partial fix for the resource method generation code, and retires the …
jakedialpad Jun 10, 2025
3885b60
Reformats everything with ruff
jakedialpad Jun 10, 2025
32cd68f
Adds command to regenerate all the client components inplace
jakedialpad Jun 10, 2025
a0986be
Adds a DialpadResourcesMixin class to easily interface with the gener…
jakedialpad Jun 11, 2025
64ac86f
Updates client test to automatically invoke every request method
jakedialpad Jun 11, 2025
a775a21
Fixes formatting
jakedialpad Jun 11, 2025
8bb5706
Bug fix
jakedialpad Jun 11, 2025
f352b8e
Updates resource module gen to include inner-type import for collecti…
jakedialpad Jun 11, 2025
7cf6589
Fixes path f-string bug
jakedialpad Jun 11, 2025
663e514
Properly unwraps NotRequired types
jakedialpad Jun 11, 2025
d9ac36b
Adds support for Optional and Union as well
jakedialpad Jun 11, 2025
a2d68a9
Handle inner list types as well
jakedialpad Jun 11, 2025
10b8349
Burns out most test issues
jakedialpad Jun 11, 2025
c4d400d
Updates schema module class definition order to be deterministic
jakedialpad Jun 11, 2025
6c84433
Formatting fix
jakedialpad Jun 11, 2025
7e5dbba
Remove wfm bits (for now)
jakedialpad Jun 11, 2025
64176b6
Fixes unused imports and whatnot
jakedialpad Jun 11, 2025
81a9d00
Formatting and linting fixes
jakedialpad Jun 11, 2025
85fb60a
Adds pagination handling test
jakedialpad Jun 11, 2025
427f726
Removes superfluous commands from cli
jakedialpad Jun 11, 2025
2a370f9
Updates the readme per the new tooling and processes and whatnot
jakedialpad Jun 12, 2025
f56256b
Adds code coverage back in
jakedialpad Jun 12, 2025
0ae3d24
Fixes the b64 special-case
jakedialpad Jun 12, 2025
ea42ffb
Fixes the number swap issue (mostly)
jakedialpad Jun 12, 2025
f6c2cee
Fixes the ambiguous schema validation issue as well (and reformats so…
jakedialpad Jun 12, 2025
4a9a550
Add pytest GHA
jakedialpad Jun 12, 2025
7d0d186
Whoops
jakedialpad Jun 12, 2025
de6c11f
:o
jakedialpad Jun 12, 2025
47829aa
:/
jakedialpad Jun 12, 2025
940f9b5
More whoops
jakedialpad Jun 12, 2025
ea92267
Add ruff linting as well
jakedialpad Jun 12, 2025
9a90c77
More appropriate lint step naming
jakedialpad Jun 12, 2025
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
6 changes: 0 additions & 6 deletions .bumpversion.cfg

This file was deleted.

33 changes: 33 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
applyTo: "**"
---

# Project Guidelines

## General Coding Standards

- Use descriptive variable and function names.
- Add concise comments to explain complex logic.
- Prioritize clear, maintainable code.

## Python Style Requirements

- Use Python for all new code.
- Prefer single-quoted strings for consistency.
- Use `f-strings` for string formatting.
- Use two spaces for indentation.
- Use concise and correct type annotations as much as possible.
- Prefer early-returns over if-else constructs.
- Follow functional programming principles where possible.
- Ensure code is compatible with Python 3.9+.

## Logging Guidance

- Use the `logging` module for logging.
- Create a logger instance for each module a la `logger = logging.getLogger(__name__)`.
- Make use of the `rich` library to enhance logging output when possible.

## Project Conventions
- Use `uv run pytest -xvs &> output.txt` to run tests.
- There is a bug in the current version of Copilot that causes it to not read the output of commands directly. This workaround allows you to read the output from `output.txt` instead.
- The tests are fast and inexpensive to run, so please favour running *all* of them to avoid missing any issues.
37 changes: 37 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: pytest

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true

- name: Set up Python ${{ matrix.python-version }} with uv
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install the project
run: uv sync --locked --dev

- name: Run tests with pytest
run: uv run pytest

- name: Run code formatting checks with ruff
run: uv run ruff check
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,4 @@ ENV/
# Rope project settings
.ropeproject
test/.resources/
output.txt
2 changes: 0 additions & 2 deletions MANIFEST.in

This file was deleted.

111 changes: 28 additions & 83 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ For information about the API itself, head on over to our

## Installation

Just use everyone's favourite python package installer: `pip`
Just use everyone's new favourite python package manager: `uv`

```bash
pip install python-dialpad
uv add python-dialpad
```

## Usage
Expand All @@ -29,7 +29,7 @@ from dialpad import DialpadClient

dp_client = DialpadClient(sandbox=True, token='API_TOKEN_HERE')

print(dp_client.user.get(user_id='1234567'))
print(dp_client.users.get(user_id='1234567'))
```

### Client Constructor Arguments
Expand All @@ -42,14 +42,18 @@ print(dp_client.user.get(user_id='1234567'))

### API Resources

In general, each resource that we support in our public API will be exposed as properties of the
client object. For example, the `User` resource can be accessed using the `user` property (as
In general, resources that we support in our public API will be exposed as properties of the
client object. For example, the `User` resource can be accessed using the `users` property (as
demonstrated above).

Each of these resource properties will expose related HTTP methods as methods of that resource
property.

For example, `GET /api/v2/users/{id}` translates to `dp_client.user.get('the_user_id')`.
For example, `GET /api/v2/users/{id}` translates to `dp_client.users.get('a_user_id')`.

When in doubt, type annotations and docstrings are sourced directly from the Dialpad API spec, and
should behave well with most editors' autocomplete/tooltip features:
![user list method tooltip](./docs/images/tooltip_example.png)


### API Responses
Expand All @@ -65,98 +69,39 @@ from dialpad import DialpadClient

dp_client = DialpadClient(sandbox=True, token='API_TOKEN_HERE')

for user in dp_client.user.list():
for user in dp_client.users.list():
print(user)
```


## Development

### Testing

That's right, the testing section is first in line! Before you start diving in, let's just make sure your environment is set up properly, and that the tests are running buttery-smooth.

Assuming you've already cloned the repository, all you'll need to do is install `tox`, and run the command against the appropriate environment.

* Install the `tox` package.
```shell
$ pip install tox
```

* Run the tests
```shell
$ tox
```
Optionaly, you can specify an environment to run the tests against. For eg:
```shell
$ tox -e py3
```
That was easy :)

Neato!

### Adding New Resources

Most of the changes to this library will probably just be adding support for additional resources
and endpoints that we expose in the API, so let's start with how to add a new resource.

Each resource exposed by this library should have its own python file under the `dialpad/resources`
directory, and should define a single `class` that inherits from `DialpadResource`.

The class itself should set the `_resource_path` class property to a list of strings such
that `'/api/v2/' + '/'.join(_resource_path)` corresponds to the API path for that resource.

Once the `_resource_path` is defined, the resource class can define instance methods to expose
functionality related to the resource that it represents, and can use the `self.request` helper
method to make authenticated requests to API paths under the `_resource_path`. For example,
if `_resource_path` is set to `['users']`, then calling `self.request(method='POST')` would make
a `POST` request to `/api/v2/users`. (A more precise description of the `request` method is given
in the following section)

With that in mind, most methods that the developer chooses to add to a resource class will probably
just be a very thin method that passes the appropriate arguments into `self.request`, and returns
the result.


#### The `request` Helper Method

`self.request` is a helper method that handles the details of authentication, response parsing, and
pagination, such that the caller only needs to specify the API path, HTTP method, and request data.
The method arguments are as follows:
This project is now managed with `uv`, and exposes a cli tool to automate most maintenance tasks.
`uv run cli --help` for details.

- `path (optional)` Any additional path elements that should be added after the `_resource_path`
- `method (optional, default: 'GET')` The HTTP method
- `data (optional)` A python dict defining either the query params or the JSON payload, depending on
which HTTP method is specified
- `headers (optional)` Any additional headers that should be included in the request (the API key
is automatically included)

If the request succeeds, then `self.request` will either return a python dict, or an iterator of
python dicts, depending on whether the server responds with a pagenated response. Pagenated
responses will be detected automatically, so the caller does not need to worry about it.
### Maintenance Releases

If the request fails, then a `requests.HTTPError` exception will be raised, and it'll be up to the
consumer of this library to deal with it 😎
Changes/additions to the Dialpad API can be handled (mostly) automatically 👍

#### Update Procedure

#### The `resources/__init__.py` File
- Overwrite `dialpad_api_spec.json` with the latest spec

When a new file is added to the `resources` directory, a new import statement should also be added
to `__init__.py` to expose the newly defined resource class as a direct property of the `resources`
module.
- 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

- 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 👍

#### `DialpadClient` Resource Properties
![resource module mapping](./docs/images/resource_module_mapping.png)

In addition to adding the new class to the `__init__.py` file, the new resource class should also
be added as a cached property of the `DialpadClient` class.
- Run `uv run cli generate-client`
- This will regenerate all of the schema and resource files as per the API spec.

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

#### Recap
TODO: version bump, build, push, publish

To add a new resource to this client library, simply:
- Create a new file under the `resources` directory
- Define a new subclass of `DialpadResource` within said file
- Expose methods related to that resource as methods on your new class
- Add a new import statement in `resources/__init__.py`
- Add a new property to the `DialpadClient` class
Empty file added cli/__init__.py
Empty file.
Empty file added cli/client_gen/__init__.py
Empty file.
Loading