Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,18 @@ class VariablesImportResponse(BaseModel):
created_variable_keys: list[str]
import_count: int
created_count: int


class VariableExportResponse(BaseModel):
"""Variable serializer for export (unmasked values)."""

key: str
val: str = Field(alias="value")
description: str | None
is_encrypted: bool


class VariableExportBody(StrictBaseModel):
"""Variable export request body."""

variable_keys: list[str] = Field(description="List of variable keys to export")
Original file line number Diff line number Diff line change
Expand Up @@ -7643,6 +7643,57 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/api/v2/variables/export:
post:
tags:
- Variable
summary: Export Variables
description: Export variables with unmasked values when having elevated admin
access.
operationId: export_variables
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/VariableExportBody'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
items:
$ref: '#/components/schemas/VariableExportResponse'
type: array
title: Response Export Variables
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPExceptionResponse'
'403':
description: Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPExceptionResponse'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPExceptionResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
security:
- OAuth2PasswordBearer: []
- HTTPBearer: []
/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/logs/{try_number}:
get:
tags:
Expand Down Expand Up @@ -12984,6 +13035,44 @@ components:
- total_entries
title: VariableCollectionResponse
description: Variable Collection serializer for responses.
VariableExportBody:
properties:
variable_keys:
items:
type: string
type: array
title: Variable Keys
description: List of variable keys to export
additionalProperties: false
type: object
required:
- variable_keys
title: VariableExportBody
description: Variable export request body.
VariableExportResponse:
properties:
key:
type: string
title: Key
value:
type: string
title: Value
description:
anyOf:
- type: string
- type: 'null'
title: Description
is_encrypted:
type: boolean
title: Is Encrypted
type: object
required:
- key
- value
- description
- is_encrypted
title: VariableExportResponse
description: Variable serializer for export (unmasked values).
VariableResponse:
properties:
key:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
from airflow.api_fastapi.core_api.datamodels.variables import (
VariableBody,
VariableCollectionResponse,
VariableExportBody,
VariableExportResponse,
VariableResponse,
)
from airflow.api_fastapi.core_api.openapi.exceptions import create_openapi_http_exception_doc
Expand Down Expand Up @@ -187,3 +189,27 @@ def bulk_variables(
) -> BulkResponse:
"""Bulk create, update, and delete variables."""
return BulkVariableService(session=session, request=request).handle_request()


@variables_router.post(
"/export",
responses=create_openapi_http_exception_doc([status.HTTP_404_NOT_FOUND]),
dependencies=[Depends(requires_access_variable("PUT"))],
)
def export_variables(
export_body: VariableExportBody,
session: SessionDep,
) -> list[VariableExportResponse]:
"""Export variables with unmasked values when having elevated admin access."""
variables = session.scalars(select(Variable).where(Variable.key.in_(export_body.variable_keys))).all()

# Check if any requested variables were not found
found_keys = {var.key for var in variables}
missing_keys = set(export_body.variable_keys) - found_keys
if missing_keys:
raise HTTPException(
status.HTTP_404_NOT_FOUND,
f"The following variables were not found: {', '.join(sorted(missing_keys))}",
)

return [VariableExportResponse.model_validate(var) for var in variables]
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,7 @@ export type TaskInstanceServicePostClearTaskInstancesMutationResult = Awaited<Re
export type PoolServicePostPoolMutationResult = Awaited<ReturnType<typeof PoolService.postPool>>;
export type XcomServiceCreateXcomEntryMutationResult = Awaited<ReturnType<typeof XcomService.createXcomEntry>>;
export type VariableServicePostVariableMutationResult = Awaited<ReturnType<typeof VariableService.postVariable>>;
export type VariableServiceExportVariablesMutationResult = Awaited<ReturnType<typeof VariableService.exportVariables>>;
export type BackfillServicePauseBackfillMutationResult = Awaited<ReturnType<typeof BackfillService.pauseBackfill>>;
export type BackfillServiceUnpauseBackfillMutationResult = Awaited<ReturnType<typeof BackfillService.unpauseBackfill>>;
export type BackfillServiceCancelBackfillMutationResult = Awaited<ReturnType<typeof BackfillService.cancelBackfill>>;
Expand Down
15 changes: 14 additions & 1 deletion airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { UseMutationOptions, UseQueryOptions, useMutation, useQuery } from "@tanstack/react-query";
import { AssetService, AuthLinksService, BackfillService, CalendarService, ConfigService, ConnectionService, DagParsingService, DagRunService, DagService, DagSourceService, DagStatsService, DagVersionService, DagWarningService, DashboardService, DependenciesService, EventLogService, ExperimentalService, ExtraLinksService, GridService, ImportErrorService, JobService, LoginService, MonitorService, PluginService, PoolService, ProviderService, StructureService, TaskInstanceService, TaskService, TeamsService, VariableService, VersionService, XcomService } from "../requests/services.gen";
import { BackfillPostBody, BulkBody_BulkTaskInstanceBody_, BulkBody_ConnectionBody_, BulkBody_PoolBody_, BulkBody_VariableBody_, ClearTaskInstancesBody, ConnectionBody, CreateAssetEventsBody, DAGPatchBody, DAGRunClearBody, DAGRunPatchBody, DAGRunsBatchBody, DagRunState, DagWarningType, PatchTaskInstanceBody, PoolBody, PoolPatchBody, TaskInstancesBatchBody, TriggerDAGRunPostBody, UpdateHITLDetailPayload, VariableBody, XComCreateBody, XComUpdateBody } from "../requests/types.gen";
import { BackfillPostBody, BulkBody_BulkTaskInstanceBody_, BulkBody_ConnectionBody_, BulkBody_PoolBody_, BulkBody_VariableBody_, ClearTaskInstancesBody, ConnectionBody, CreateAssetEventsBody, DAGPatchBody, DAGRunClearBody, DAGRunPatchBody, DAGRunsBatchBody, DagRunState, DagWarningType, PatchTaskInstanceBody, PoolBody, PoolPatchBody, TaskInstancesBatchBody, TriggerDAGRunPostBody, UpdateHITLDetailPayload, VariableBody, VariableExportBody, XComCreateBody, XComUpdateBody } from "../requests/types.gen";
import * as Common from "./common";
/**
* Get Assets
Expand Down Expand Up @@ -1876,6 +1876,19 @@ export const useVariableServicePostVariable = <TData = Common.VariableServicePos
requestBody: VariableBody;
}, TContext>({ mutationFn: ({ requestBody }) => VariableService.postVariable({ requestBody }) as unknown as Promise<TData>, ...options });
/**
* Export Variables
* Export variables with unmasked values. Requires PUT permission (elevated access).
* @param data The data for the request.
* @param data.requestBody
* @returns VariableExportResponse Successful Response
* @throws ApiError
*/
export const useVariableServiceExportVariables = <TData = Common.VariableServiceExportVariablesMutationResult, TError = unknown, TContext = unknown>(options?: Omit<UseMutationOptions<TData, TError, {
requestBody: VariableExportBody;
}, TContext>, "mutationFn">) => useMutation<TData, TError, {
requestBody: VariableExportBody;
}, TContext>({ mutationFn: ({ requestBody }) => VariableService.exportVariables({ requestBody }) as unknown as Promise<TData>, ...options });
/**
* Pause Backfill
* @param data The data for the request.
* @param data.backfillId
Expand Down
50 changes: 50 additions & 0 deletions airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6531,6 +6531,56 @@ export const $VariableCollectionResponse = {
description: 'Variable Collection serializer for responses.'
} as const;

export const $VariableExportBody = {
properties: {
variable_keys: {
items: {
type: 'string'
},
type: 'array',
title: 'Variable Keys',
description: 'List of variable keys to export'
}
},
additionalProperties: false,
type: 'object',
required: ['variable_keys'],
title: 'VariableExportBody',
description: 'Variable export request body.'
} as const;

export const $VariableExportResponse = {
properties: {
key: {
type: 'string',
title: 'Key'
},
value: {
type: 'string',
title: 'Value'
},
description: {
anyOf: [
{
type: 'string'
},
{
type: 'null'
}
],
title: 'Description'
},
is_encrypted: {
type: 'boolean',
title: 'Is Encrypted'
}
},
type: 'object',
required: ['key', 'value', 'description', 'is_encrypted'],
title: 'VariableExportResponse',
description: 'Variable serializer for export (unmasked values).'
} as const;

export const $VariableResponse = {
properties: {
key: {
Expand Down
Loading
Loading