This is an automated email from the ASF dual-hosted git repository.

pierrejeambrun pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-1-test by this push:
     new 788ed214751 [v3-1-test] Pool API improve slots validation (#61071) 
(#61114)
788ed214751 is described below

commit 788ed214751785c78b9c5f768b89cae0a9f320d8
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Jan 27 14:12:36 2026 +0100

    [v3-1-test] Pool API improve slots validation (#61071) (#61114)
    
    (cherry picked from commit 4a0364a632a93e5daa72afcebd13f2c1a5734289)
    
    Co-authored-by: Pierre Jeambrun <[email protected]>
---
 .../api_fastapi/core_api/datamodels/pools.py        |  6 +++---
 .../core_api/openapi/v2-rest-api-generated.yaml     |  3 +++
 .../airflow/ui/openapi-gen/requests/schemas.gen.ts  |  5 ++++-
 .../core_api/routes/public/test_pools.py            | 21 ++++++++++++++++++++-
 .../src/airflowctl/api/datamodels/generated.py      | 10 +++++++---
 5 files changed, 37 insertions(+), 8 deletions(-)

diff --git a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/pools.py 
b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/pools.py
index 34cf4cb9825..cde5746b42b 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/pools.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/pools.py
@@ -20,7 +20,7 @@ from __future__ import annotations
 from collections.abc import Callable
 from typing import Annotated
 
-from pydantic import BeforeValidator, Field
+from pydantic import BeforeValidator, Field, PositiveInt
 
 from airflow.api_fastapi.core_api.base import BaseModel, StrictBaseModel
 
@@ -38,7 +38,7 @@ class BasePool(BaseModel):
     """Base serializer for Pool."""
 
     pool: str = Field(serialization_alias="name")
-    slots: int
+    slots: PositiveInt
     description: str | None = Field(default=None)
     include_deferred: bool
 
@@ -65,7 +65,7 @@ class PoolPatchBody(StrictBaseModel):
     """Pool serializer for patch bodies."""
 
     name: str | None = Field(default=None, alias="pool")
-    slots: int | None = None
+    slots: PositiveInt | None = None
     description: str | None = None
     include_deferred: bool | None = None
 
diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
 
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
index 45f38707a6e..38db3d22848 100644
--- 
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
+++ 
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
@@ -11433,6 +11433,7 @@ components:
           title: Name
         slots:
           type: integer
+          exclusiveMinimum: 0.0
           title: Slots
         description:
           anyOf:
@@ -11476,6 +11477,7 @@ components:
         slots:
           anyOf:
           - type: integer
+            exclusiveMinimum: 0.0
           - type: 'null'
           title: Slots
         description:
@@ -11499,6 +11501,7 @@ components:
           title: Name
         slots:
           type: integer
+          exclusiveMinimum: 0.0
           title: Slots
         description:
           anyOf:
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts 
b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
index ab7aa4942f7..7366d970a68 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
@@ -4248,6 +4248,7 @@ export const $PoolBody = {
         },
         slots: {
             type: 'integer',
+            exclusiveMinimum: 0,
             title: 'Slots'
         },
         description: {
@@ -4310,7 +4311,8 @@ export const $PoolPatchBody = {
         slots: {
             anyOf: [
                 {
-                    type: 'integer'
+                    type: 'integer',
+                    exclusiveMinimum: 0
                 },
                 {
                     type: 'null'
@@ -4355,6 +4357,7 @@ export const $PoolResponse = {
         },
         slots: {
             type: 'integer',
+            exclusiveMinimum: 0,
             title: 'Slots'
         },
         description: {
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_pools.py 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_pools.py
index a444af70f70..2abd7e23835 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_pools.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_pools.py
@@ -259,6 +259,24 @@ class TestPatchPool(TestPoolsEndpoint):
                     ],
                 },
             ),
+            # Negative slot number
+            (
+                POOL1_NAME,
+                {},
+                {"slots": -10},
+                422,
+                {
+                    "detail": [
+                        {
+                            "ctx": {"gt": 0},
+                            "input": -10,
+                            "loc": ["body", "slots"],
+                            "msg": "Input should be greater than 0",
+                            "type": "greater_than",
+                        },
+                    ],
+                },
+            ),
             # Partial body on default_pool
             (
                 Pool.DEFAULT_POOL_NAME,
@@ -335,7 +353,8 @@ class TestPatchPool(TestPoolsEndpoint):
         if response.status_code == 422:
             for error in body["detail"]:
                 # pydantic version can vary in tests (lower constraints), we 
do not assert the url.
-                del error["url"]
+                if "url" in error:
+                    del error["url"]
 
         assert body == expected_response
         if response.status_code == 200:
diff --git a/airflow-ctl/src/airflowctl/api/datamodels/generated.py 
b/airflow-ctl/src/airflowctl/api/datamodels/generated.py
index f546515584a..65364faa318 100644
--- a/airflow-ctl/src/airflowctl/api/datamodels/generated.py
+++ b/airflow-ctl/src/airflowctl/api/datamodels/generated.py
@@ -614,11 +614,15 @@ class PoolBody(BaseModel):
         extra="forbid",
     )
     name: Annotated[str, Field(max_length=256, title="Name")]
-    slots: Annotated[int, Field(title="Slots")]
+    slots: Annotated[int, Field(gt=0, title="Slots")]
     description: Annotated[str | None, Field(title="Description")] = None
     include_deferred: Annotated[bool | None, Field(title="Include Deferred")] 
= False
 
 
+class Slots(RootModel[int]):
+    root: Annotated[int, Field(gt=0, title="Slots")]
+
+
 class PoolPatchBody(BaseModel):
     """
     Pool serializer for patch bodies.
@@ -628,7 +632,7 @@ class PoolPatchBody(BaseModel):
         extra="forbid",
     )
     pool: Annotated[str | None, Field(title="Pool")] = None
-    slots: Annotated[int | None, Field(title="Slots")] = None
+    slots: Annotated[Slots | None, Field(title="Slots")] = None
     description: Annotated[str | None, Field(title="Description")] = None
     include_deferred: Annotated[bool | None, Field(title="Include Deferred")] 
= None
 
@@ -639,7 +643,7 @@ class PoolResponse(BaseModel):
     """
 
     name: Annotated[str, Field(title="Name")]
-    slots: Annotated[int, Field(title="Slots")]
+    slots: Annotated[int, Field(gt=0, title="Slots")]
     description: Annotated[str | None, Field(title="Description")] = None
     include_deferred: Annotated[bool, Field(title="Include Deferred")]
     occupied_slots: Annotated[int, Field(title="Occupied Slots")]

Reply via email to