Skip to content

Commit de054a4

Browse files
committed
Add team_id to variable APIs
1 parent 83d6c6c commit de054a4

9 files changed

Lines changed: 152 additions & 9 deletions

File tree

airflow-core/src/airflow/api_fastapi/core_api/datamodels/variables.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import json
2121
from collections.abc import Iterable
22+
from uuid import UUID
2223

2324
from pydantic import Field, JsonValue, model_validator
2425

@@ -35,6 +36,7 @@ class VariableResponse(BaseModel):
3536
val: str = Field(alias="value")
3637
description: str | None
3738
is_encrypted: bool
39+
team_id: UUID | None
3840

3941
@model_validator(mode="after")
4042
def redact_val(self) -> Self:
@@ -57,6 +59,7 @@ class VariableBody(StrictBaseModel):
5759
key: str = Field(max_length=ID_LEN)
5860
value: JsonValue = Field(serialization_alias="val")
5961
description: str | None = Field(default=None)
62+
team_id: UUID | None = Field(default=None)
6063

6164

6265
class VariableCollectionResponse(BaseModel):

airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13068,6 +13068,12 @@ components:
1306813068
- type: string
1306913069
- type: 'null'
1307013070
title: Description
13071+
team_id:
13072+
anyOf:
13073+
- type: string
13074+
format: uuid
13075+
- type: 'null'
13076+
title: Team Id
1307113077
additionalProperties: false
1307213078
type: object
1307313079
required:
@@ -13107,12 +13113,19 @@ components:
1310713113
is_encrypted:
1310813114
type: boolean
1310913115
title: Is Encrypted
13116+
team_id:
13117+
anyOf:
13118+
- type: string
13119+
format: uuid
13120+
- type: 'null'
13121+
title: Team Id
1311013122
type: object
1311113123
required:
1311213124
- key
1311313125
- value
1311413126
- description
1311513127
- is_encrypted
13128+
- team_id
1311613129
title: VariableResponse
1311713130
description: Variable serializer for responses.
1311813131
VersionInfo:

airflow-core/src/airflow/models/variable.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
from sqlalchemy_utils import UUIDType
3131

3232
from airflow._shared.secrets_masker import mask_secret
33-
from airflow.configuration import ensure_secrets_loaded
33+
from airflow.configuration import conf, ensure_secrets_loaded
3434
from airflow.models.base import ID_LEN, Base
3535
from airflow.models.crypto import get_fernet
3636
from airflow.models.team import Team
@@ -149,7 +149,7 @@ def get(
149149
# means SQLA etc is loaded, but we can't avoid that unless/until we add import shims as a big
150150
# back-compat layer
151151

152-
# If this is set it means are in some kind of execution context (Task, Dag Parse or Triggerer perhaps)
152+
# If this is set it means we are in some kind of execution context (Task, Dag Parse or Triggerer perhaps)
153153
# and should use the Task SDK API server path
154154
if hasattr(sys.modules.get("airflow.sdk.execution_time.task_runner"), "SUPERVISOR_COMMS"):
155155
warnings.warn(
@@ -185,6 +185,7 @@ def set(
185185
value: Any,
186186
description: str | None = None,
187187
serialize_json: bool = False,
188+
team_id: str | None = None,
188189
session: Session | None = None,
189190
) -> None:
190191
"""
@@ -196,13 +197,14 @@ def set(
196197
:param value: Value to set for the Variable
197198
:param description: Description of the Variable
198199
:param serialize_json: Serialize the value to a JSON string
200+
:param team_id: ID of the team associated to the variable (if any)
199201
:param session: optional session, use if provided or create a new one
200202
"""
201203
# TODO: This is not the best way of having compat, but it's "better than erroring" for now. This still
202204
# means SQLA etc is loaded, but we can't avoid that unless/until we add import shims as a big
203205
# back-compat layer
204206

205-
# If this is set it means are in some kind of execution context (Task, Dag Parse or Triggerer perhaps)
207+
# If this is set it means we are in some kind of execution context (Task, Dag Parse or Triggerer perhaps)
206208
# and should use the Task SDK API server path
207209
if hasattr(sys.modules.get("airflow.sdk.execution_time.task_runner"), "SUPERVISOR_COMMS"):
208210
warnings.warn(
@@ -221,6 +223,11 @@ def set(
221223
)
222224
return
223225

226+
if team_id and not conf.getboolean("core", "multi_team"):
227+
raise ValueError(
228+
"Multi-team mode is not configured in the Airflow environment. To assign a team to a variable, multi-mode must be enabled."
229+
)
230+
224231
# check if the secret exists in the custom secrets' backend.
225232
Variable.check_for_write_conflict(key=key)
226233
if serialize_json:
@@ -235,7 +242,7 @@ def set(
235242
ctx = create_session()
236243

237244
with ctx as session:
238-
new_variable = Variable(key=key, val=stored_value, description=description)
245+
new_variable = Variable(key=key, val=stored_value, description=description, team_id=team_id)
239246

240247
val = new_variable._val
241248
is_encrypted = new_variable.is_encrypted
@@ -252,13 +259,15 @@ def set(
252259
val=val,
253260
description=description,
254261
is_encrypted=is_encrypted,
262+
team_id=team_id,
255263
)
256264
stmt = pg_stmt.on_conflict_do_update(
257265
index_elements=["key"],
258266
set_=dict(
259267
val=val,
260268
description=description,
261269
is_encrypted=is_encrypted,
270+
team_id=team_id,
262271
),
263272
)
264273
elif dialect_name == "mysql":
@@ -269,11 +278,13 @@ def set(
269278
val=val,
270279
description=description,
271280
is_encrypted=is_encrypted,
281+
team_id=team_id,
272282
)
273283
stmt = mysql_stmt.on_duplicate_key_update(
274284
val=val,
275285
description=description,
276286
is_encrypted=is_encrypted,
287+
team_id=team_id,
277288
)
278289
else:
279290
from sqlalchemy.dialects.sqlite import insert as sqlite_insert
@@ -283,13 +294,15 @@ def set(
283294
val=val,
284295
description=description,
285296
is_encrypted=is_encrypted,
297+
team_id=team_id,
286298
)
287299
stmt = sqlite_stmt.on_conflict_do_update(
288300
index_elements=["key"],
289301
set_=dict(
290302
val=val,
291303
description=description,
292304
is_encrypted=is_encrypted,
305+
team_id=team_id,
293306
),
294307
)
295308

airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6559,6 +6559,18 @@ export const $VariableBody = {
65596559
}
65606560
],
65616561
title: 'Description'
6562+
},
6563+
team_id: {
6564+
anyOf: [
6565+
{
6566+
type: 'string',
6567+
format: 'uuid'
6568+
},
6569+
{
6570+
type: 'null'
6571+
}
6572+
],
6573+
title: 'Team Id'
65626574
}
65636575
},
65646576
additionalProperties: false,
@@ -6612,10 +6624,22 @@ export const $VariableResponse = {
66126624
is_encrypted: {
66136625
type: 'boolean',
66146626
title: 'Is Encrypted'
6627+
},
6628+
team_id: {
6629+
anyOf: [
6630+
{
6631+
type: 'string',
6632+
format: 'uuid'
6633+
},
6634+
{
6635+
type: 'null'
6636+
}
6637+
],
6638+
title: 'Team Id'
66156639
}
66166640
},
66176641
type: 'object',
6618-
required: ['key', 'value', 'description', 'is_encrypted'],
6642+
required: ['key', 'value', 'description', 'is_encrypted', 'team_id'],
66196643
title: 'VariableResponse',
66206644
description: 'Variable serializer for responses.'
66216645
} as const;

airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,6 +1594,7 @@ export type VariableBody = {
15941594
key: string;
15951595
value: JsonValue;
15961596
description?: string | null;
1597+
team_id?: string | null;
15971598
};
15981599

15991600
/**
@@ -1612,6 +1613,7 @@ export type VariableResponse = {
16121613
value: string;
16131614
description: string | null;
16141615
is_encrypted: boolean;
1616+
team_id: string | null;
16151617
};
16161618

16171619
/**

0 commit comments

Comments
 (0)