diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 437cde35f477..2773300caa3a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -113,6 +113,7 @@ repos: src/prefect/server/api/.*| src/prefect/server/schemas/.*| src/prefect/server/events/.*| + src/prefect/server/utilities/schemas/.*| ui-v2/package.json )$ pass_filenames: false diff --git a/docs/v3/api-ref/rest-api/server/schema.json b/docs/v3/api-ref/rest-api/server/schema.json index f3c4730b5430..423c0d506f72 100644 --- a/docs/v3/api-ref/rest-api/server/schema.json +++ b/docs/v3/api-ref/rest-api/server/schema.json @@ -9904,6 +9904,7 @@ } }, "type": "object", + "required": [], "title": "Artifact" }, "ArtifactCollection": { @@ -10030,7 +10031,10 @@ "type": "object", "required": [ "key", - "latest_id" + "latest_id", + "id", + "created", + "updated" ], "title": "ArtifactCollection" }, @@ -10896,7 +10900,10 @@ "required": [ "name", "trigger", - "actions" + "actions", + "id", + "created", + "updated" ], "title": "Automation" }, @@ -11557,7 +11564,10 @@ "type": "object", "required": [ "block_schema_id", - "block_type_id" + "block_type_id", + "id", + "created", + "updated" ], "title": "BlockDocument", "description": "An ORM representation of a block document." @@ -11899,7 +11909,10 @@ "type": "object", "required": [ "checksum", - "block_type_id" + "block_type_id", + "id", + "created", + "updated" ], "title": "BlockSchema", "description": "An ORM representation of a block schema." @@ -12203,7 +12216,10 @@ "type": "object", "required": [ "name", - "slug" + "slug", + "id", + "created", + "updated" ], "title": "BlockType", "description": "An ORM representation of a block type" @@ -14627,7 +14643,10 @@ "type": "object", "required": [ "tag", - "concurrency_limit" + "concurrency_limit", + "id", + "created", + "updated" ], "title": "ConcurrencyLimit", "description": "An ORM representation of a concurrency limit." @@ -14739,7 +14758,10 @@ "type": "object", "required": [ "name", - "limit" + "limit", + "id", + "created", + "updated" ], "title": "ConcurrencyLimitV2", "description": "An ORM representation of a v2 concurrency limit." @@ -15101,7 +15123,10 @@ "required": [ "token", "client", - "expiration" + "expiration", + "id", + "created", + "updated" ], "title": "CsrfToken" }, @@ -16260,7 +16285,10 @@ "type": "object", "required": [ "name", - "flow_id" + "flow_id", + "id", + "created", + "updated" ], "title": "DeploymentResponse" }, @@ -16345,7 +16373,10 @@ }, "type": "object", "required": [ - "schedule" + "schedule", + "id", + "created", + "updated" ], "title": "DeploymentSchedule" }, @@ -17345,7 +17376,10 @@ }, "type": "object", "required": [ - "name" + "name", + "id", + "created", + "updated" ], "title": "Flow", "description": "An ORM representation of flow data." @@ -18034,7 +18068,10 @@ }, "type": "object", "required": [ - "flow_id" + "flow_id", + "id", + "created", + "updated" ], "title": "FlowRun", "description": "An ORM representation of flow run data." @@ -19069,7 +19106,10 @@ "required": [ "flow_run_id", "key", - "value" + "value", + "id", + "created", + "updated" ], "title": "FlowRunInput" }, @@ -19152,7 +19192,10 @@ "required": [ "state_names", "tags", - "block_document_id" + "block_document_id", + "id", + "created", + "updated" ], "title": "FlowRunNotificationPolicy", "description": "An ORM representation of a flow run notification." @@ -19854,7 +19897,10 @@ }, "type": "object", "required": [ - "flow_id" + "flow_id", + "id", + "created", + "updated" ], "title": "FlowRunResponse" }, @@ -20036,7 +20082,10 @@ "required": [ "name", "limit", - "active_slots" + "active_slots", + "id", + "created", + "updated" ], "title": "GlobalConcurrencyLimitResponse", "description": "A response object for global concurrency limits." @@ -20411,7 +20460,10 @@ "name", "level", "message", - "timestamp" + "timestamp", + "id", + "created", + "updated" ], "title": "Log", "description": "An ORM representation of log data." @@ -21428,7 +21480,10 @@ }, "type": "object", "required": [ - "name" + "name", + "id", + "created", + "updated" ], "title": "SavedSearch", "description": "An ORM representation of saved search data. Represents a set of filter criteria." @@ -21783,7 +21838,8 @@ }, "type": "object", "required": [ - "type" + "type", + "id" ], "title": "State", "description": "Represents the state of a run." @@ -22466,7 +22522,10 @@ "type": "object", "required": [ "task_key", - "dynamic_key" + "dynamic_key", + "id", + "created", + "updated" ], "title": "TaskRun", "description": "An ORM representation of task run data." @@ -23419,7 +23478,10 @@ "type": "object", "required": [ "name", - "value" + "value", + "id", + "created", + "updated" ], "title": "Variable" }, @@ -23836,7 +23898,10 @@ "type": "object", "required": [ "name", - "type" + "type", + "id", + "created", + "updated" ], "title": "WorkPool", "description": "An ORM representation of a work pool" @@ -24196,7 +24261,10 @@ }, "type": "object", "required": [ - "name" + "name", + "id", + "created", + "updated" ], "title": "WorkQueue", "description": "An ORM representation of a work queue" @@ -24556,7 +24624,10 @@ }, "type": "object", "required": [ - "name" + "name", + "id", + "created", + "updated" ], "title": "WorkQueueResponse" }, @@ -24892,7 +24963,10 @@ "type": "object", "required": [ "name", - "work_pool_id" + "work_pool_id", + "id", + "created", + "updated" ], "title": "WorkerResponse" }, diff --git a/src/prefect/server/utilities/schemas/bases.py b/src/prefect/server/utilities/schemas/bases.py index 7c887656e0c9..4c1d48028a24 100644 --- a/src/prefect/server/utilities/schemas/bases.py +++ b/src/prefect/server/utilities/schemas/bases.py @@ -1,11 +1,13 @@ import datetime import os from abc import ABC, abstractmethod +from functools import partial from typing import TYPE_CHECKING, Any, ClassVar, Optional, TypeVar from uuid import UUID, uuid4 import pendulum from pydantic import BaseModel, ConfigDict, Field +from pydantic.config import JsonDict from typing_extensions import Self from prefect.types import DateTime @@ -181,6 +183,18 @@ def model_dump_for_orm( return deep +def _ensure_fields_required(field_names: list[str], schema: JsonDict) -> None: + for field_name in field_names: + if "required" not in schema: + schema["required"] = [] + if ( + (required := schema.get("required")) + and isinstance(required, list) + and field_name not in required + ): + required.append(field_name) + + class IDBaseModel(PrefectBaseModel): """ A PrefectBaseModel with an auto-generated UUID ID value. @@ -188,6 +202,10 @@ class IDBaseModel(PrefectBaseModel): The ID is reset on copy() and not included in equality comparisons. """ + model_config = ConfigDict( + json_schema_extra=partial(_ensure_fields_required, ["id"]) + ) + _reset_fields: ClassVar[set[str]] = {"id"} id: UUID = Field(default_factory=uuid4) @@ -203,7 +221,12 @@ class ORMBaseModel(IDBaseModel): _reset_fields: ClassVar[set[str]] = {"id", "created", "updated"} - model_config = ConfigDict(from_attributes=True) + model_config = ConfigDict( + from_attributes=True, + json_schema_extra=partial( + _ensure_fields_required, ["id", "created", "updated"] + ), + ) created: Optional[DateTime] = Field(default=None, repr=False) updated: Optional[DateTime] = Field(default=None, repr=False) diff --git a/ui-v2/package.json b/ui-v2/package.json index db5afb5bc3fb..30dfb95c91de 100644 --- a/ui-v2/package.json +++ b/ui-v2/package.json @@ -12,7 +12,7 @@ "format:check": "biome check", "format": "biome check --write", "preview": "vite preview", - "service-sync": "uv run ../scripts/generate_oss_openapi_schema.py && npx openapi-typescript oss_schema.json -o src/api/prefect.ts && rm oss_schema.json", + "service-sync": "uv run --with-editable ../. ../scripts/generate_oss_openapi_schema.py && npx openapi-typescript oss_schema.json -o src/api/prefect.ts && rm oss_schema.json", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build" }, diff --git a/ui-v2/src/api/prefect.ts b/ui-v2/src/api/prefect.ts index cdd65d4131ed..dc552d5a41d7 100644 --- a/ui-v2/src/api/prefect.ts +++ b/ui-v2/src/api/prefect.ts @@ -2926,11 +2926,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Key * @description An optional unique reference key for this artifact. @@ -3269,11 +3269,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; }; /** AutomationCreate */ AutomationCreate: { @@ -3418,11 +3418,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Name * @description The block document's name. Not required for anonymous block documents. @@ -3611,11 +3611,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Checksum * @description The block schema's unique checksum @@ -3745,11 +3745,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Name * @description A block type's name @@ -4855,11 +4855,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Tag * @description A tag the concurrency limit is applied to. @@ -4907,11 +4907,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Active * @description Whether the concurrency limit is active. @@ -5144,11 +5144,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Token * @description The CSRF token @@ -5471,11 +5471,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Name * @description The name of the deployment. @@ -5607,11 +5607,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Deployment Id * @description The deployment id associated with this schedule. @@ -6056,11 +6056,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Name * @description The name of the flow @@ -6207,11 +6207,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Name * @description The name of the flow run. Defaults to a random slug if not specified. @@ -6753,11 +6753,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Flow Run Id * Format: uuid @@ -6789,11 +6789,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Is Active * @description Whether the policy is currently active @@ -6960,11 +6960,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Name * @description The name of the flow run. Defaults to a random slug if not specified. @@ -7177,11 +7177,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Active * @description Whether the global concurrency limit is active. @@ -7369,11 +7369,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Name * @description The logger name. @@ -7950,11 +7950,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Name * @description The name of the saved search. @@ -8163,7 +8163,7 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; type: components["schemas"]["StateType"]; /** Name */ name?: string | null; @@ -8353,11 +8353,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** Name */ name?: string; /** @@ -8844,11 +8844,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Name * @description The name of the variable @@ -8987,11 +8987,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Name * @description The name of the work pool. @@ -9148,11 +9148,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Name * @description The name of the work queue. @@ -9299,11 +9299,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Name * @description The name of the work queue. @@ -9476,11 +9476,11 @@ export interface components { * Id * Format: uuid */ - id?: string; + id: string; /** Created */ - created?: string | null; + created: string | null; /** Updated */ - updated?: string | null; + updated: string | null; /** * Name * @description The name of the worker. diff --git a/ui-v2/src/components/concurrency/global-concurrency-view/dialog/delete-limit-dialog.tsx b/ui-v2/src/components/concurrency/global-concurrency-view/dialog/delete-limit-dialog.tsx index 510c695e7709..8bcf95f846cd 100644 --- a/ui-v2/src/components/concurrency/global-concurrency-view/dialog/delete-limit-dialog.tsx +++ b/ui-v2/src/components/concurrency/global-concurrency-view/dialog/delete-limit-dialog.tsx @@ -25,10 +25,7 @@ export const DeleteLimitDialog = ({ limit, onOpenChange, onDelete }: Props) => { const { deleteGlobalConcurrencyLimit, isPending } = useDeleteGlobalConcurrencyLimit(); - const handleOnClick = (id: string | undefined) => { - if (!id) { - throw new Error("'id' field expected in TaskRunConcurrencyLimit"); - } + const handleOnClick = (id: string) => { deleteGlobalConcurrencyLimit(id, { onSuccess: () => { toast({ description: "Concurrency limit deleted" }); diff --git a/ui-v2/src/components/flows/detail/deployment-columns.tsx b/ui-v2/src/components/flows/detail/deployment-columns.tsx index ad3dbecedabe..06b48a01a369 100644 --- a/ui-v2/src/components/flows/detail/deployment-columns.tsx +++ b/ui-v2/src/components/flows/detail/deployment-columns.tsx @@ -106,7 +106,7 @@ export const columns: ColumnDef[] = [ Custom run - void navigator.clipboard.writeText(row.original.id as string) + void navigator.clipboard.writeText(row.original.id) } > Copy ID diff --git a/ui-v2/src/components/ui/flow-run-acitivity-bar-graph/flow-run-activity-bar-graph.stories.tsx b/ui-v2/src/components/ui/flow-run-acitivity-bar-graph/flow-run-activity-bar-graph.stories.tsx index 9f2d6492d307..7c5b2739ae74 100644 --- a/ui-v2/src/components/ui/flow-run-acitivity-bar-graph/flow-run-activity-bar-graph.stories.tsx +++ b/ui-v2/src/components/ui/flow-run-acitivity-bar-graph/flow-run-activity-bar-graph.stories.tsx @@ -77,10 +77,14 @@ function createRandomEnrichedFlowRun(): React.ComponentProps< paused: faker.datatype.boolean(), status: faker.helpers.arrayElement(["READY", "NOT_READY"]), enforce_parameter_schema: faker.datatype.boolean(), + created: faker.date.past().toISOString(), + updated: faker.date.past().toISOString(), }, flow: { id: faker.string.uuid(), name: `${faker.finance.currencyName()} ${faker.commerce.product()}`, + created: faker.date.past().toISOString(), + updated: faker.date.past().toISOString(), }, }; } diff --git a/ui-v2/src/components/ui/state-badge/state-badge.test.tsx b/ui-v2/src/components/ui/state-badge/state-badge.test.tsx index 0ff9dee251b8..df15c7612a46 100644 --- a/ui-v2/src/components/ui/state-badge/state-badge.test.tsx +++ b/ui-v2/src/components/ui/state-badge/state-badge.test.tsx @@ -62,7 +62,7 @@ describe("StateBadge", () => { test.each(states)( "renders correct icon and classes for $type state", ({ type, name }) => { - render(); + render(); // Check if state name is rendered expect(screen.getByText(name)).toBeInTheDocument(); diff --git a/ui-v2/src/hooks/global-concurrency-limits.test.tsx b/ui-v2/src/hooks/global-concurrency-limits.test.tsx index 8d86ac261d8a..8b98bcc09960 100644 --- a/ui-v2/src/hooks/global-concurrency-limits.test.tsx +++ b/ui-v2/src/hooks/global-concurrency-limits.test.tsx @@ -196,6 +196,8 @@ describe("global concurrency limits hooks", () => { const UPDATED_LIMIT = { ...UPDATED_LIMIT_BODY, id: MOCK_UPDATE_LIMIT_ID, + created: "2021-01-01T00:00:00Z", + updated: "2021-01-01T00:00:00Z", }; // ------------ Mock API requests after queries are invalidated