Skip to content

Conversation

@vdusek
Copy link
Contributor

@vdusek vdusek commented Dec 10, 2025

This PR introduces fully typed API clients, with models generated directly from OpenAPI specifications.

Description

Issues

Testing

  • Add "easy win" additional integration tests, but avoid complex workflows (e.g. Actor creation, execution, and waiting for completion - these things are still being tested only from the SDK).

@vdusek vdusek self-assigned this Dec 10, 2025
@vdusek vdusek added the t-tooling Issues with this label are in the ownership of the tooling team. label Dec 10, 2025
@github-actions github-actions bot added this to the 129th sprint - Tooling team milestone Dec 10, 2025
@vdusek vdusek changed the title refactor!: Add fully typed clients refactor!: Introduce fully typed clients [WIP] Dec 10, 2025
@vdusek vdusek force-pushed the typed-clients branch 2 times, most recently from e470329 to 64a173b Compare December 17, 2025 12:18
@codecov
Copy link

codecov bot commented Dec 19, 2025

Codecov Report

❌ Patch coverage is 91.00775% with 174 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.85%. Comparing base (692aad9) to head (d591e7a).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/apify_client/_resource_clients/run.py 64.61% 23 Missing ⚠️
src/apify_client/_resource_clients/actor.py 60.46% 17 Missing ⚠️
src/apify_client/_resource_clients/task.py 31.81% 15 Missing ⚠️
src/apify_client/_resource_clients/build.py 42.85% 12 Missing ⚠️
src/apify_client/_resource_clients/schedule.py 36.84% 12 Missing ⚠️
src/apify_client/_resource_clients/user.py 69.23% 12 Missing ⚠️
src/apify_client/_resource_clients/webhook.py 36.84% 12 Missing ⚠️
...rc/apify_client/_resource_clients/actor_env_var.py 33.33% 8 Missing ⚠️
...rc/apify_client/_resource_clients/actor_version.py 46.66% 8 Missing ⚠️
src/apify_client/_resource_clients/dataset.py 75.00% 8 Missing ⚠️
... and 12 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #554      +/-   ##
==========================================
+ Coverage   76.01%   84.85%   +8.83%     
==========================================
  Files          42       41       -1     
  Lines        2468     3888    +1420     
==========================================
+ Hits         1876     3299    +1423     
+ Misses        592      589       -3     
Flag Coverage Δ
integration 83.38% <90.64%> (+14.42%) ⬆️
unit 74.22% <84.80%> (+9.64%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

super().__init__(*args, resource_path=resource_path, **kwargs)

def list(self) -> ListPage[dict]:
def list(self) -> ListPage[EnvVar]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ListPage is not fully based on API. API returns only total + items , so the rest is made up: count, offset, limit, desc

This is already in code, but since this aims at improving the API....
Fixing it would complicate the types quite a bit though



class Data(PaginationResponse):
items: list[ActorShort]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this generated?
Real response is like this:

  "data": {
    "total": ...,
    "count": ...,
    "offset": ...,
    "limit": ...,
    "desc": ...,
    "items": [...

Is the spec incorrect?

Comment on lines +148 to +153
class PricingModel3(Enum):
FREE = 'FREE'


class FreeActorPricingInfo(CommonActorPricingInfo):
pricing_model: Annotated[Literal['FREE'], Field(alias='pricingModel')]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So is this literal or Enum?



class Data3(PaginationResponse):
items: list[WebhookShort] | None = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this generated?
Real response is like this:

  "data": {
    "total": ...,
    "count": ...,
    "offset": ...,
    "limit": ...,
    "desc": ...,
    "items": [...

Is the spec incorrect?

stats: KeyValueStoreStats | None = None


class Data7(PaginationResponse):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this generated?
Real response is like this:

  "data": {
    "total": ...,
    "count": ...,
    "offset": ...,
    "limit": ...,
    "desc": ...,
    "items": [...

Is the spec incorrect?

Comment on lines +1311 to +1326
class Data8(BaseModel):
items: list[Item]
count: Annotated[float, Field(examples=[2])]
limit: Annotated[float, Field(examples=[2])]
exclusive_start_key: Annotated[str | None, Field(alias='exclusiveStartKey', examples=['some-key'])] = None
is_truncated: Annotated[bool, Field(alias='isTruncated', examples=[True])]
next_exclusive_start_key: Annotated[str | None, Field(alias='nextExclusiveStartKey', examples=['third-key'])] = None


class ListOfKeysResponse(BaseModel):
data: Data8


class GetListOfKeysResponse(BaseModel):
data: ListOfKeysResponse

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is seems wrong. It looks like double nested data, but in reality, it should be just one level of data.
https://docs.apify.com/api/v2/key-value-store-keys-get

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-tooling Issues with this label are in the ownership of the tooling team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Introduce fully typed client Write integration tests

3 participants