From de9e0055a5067ee3e3a6105a207b7c78e8ea8668 Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Fri, 17 May 2024 14:53:30 -0500 Subject: [PATCH] User metadata on workflows/events for user interface usage (#371) --- buf.yaml | 2 + openapi/openapiv2.json | 42 ++++++++++++++ openapi/openapiv3.yaml | 56 +++++++++++++++++++ temporal/api/command/v1/message.proto | 13 +++++ temporal/api/history/v1/message.proto | 10 ++++ temporal/api/sdk/v1/user_metadata.proto | 49 ++++++++++++++++ temporal/api/sdk/v1/workflow_metadata.proto | 13 ++--- temporal/api/workflow/v1/message.proto | 7 +++ .../workflowservice/v1/request_response.proto | 9 +++ 9 files changed, 194 insertions(+), 7 deletions(-) create mode 100644 temporal/api/sdk/v1/user_metadata.proto diff --git a/buf.yaml b/buf.yaml index 499b2b65..29a1f6ba 100644 --- a/buf.yaml +++ b/buf.yaml @@ -11,6 +11,8 @@ breaking: - WIRE_JSON ignore: - google + # Remove this after user metadata is merged + - temporal/api/sdk/v1/workflow_metadata.proto lint: use: - DEFAULT diff --git a/openapi/openapiv2.json b/openapi/openapiv2.json index 38116b75..f1054376 100644 --- a/openapi/openapiv2.json +++ b/openapi/openapiv2.json @@ -2970,6 +2970,10 @@ "skipGenerateWorkflowTask": { "type": "boolean", "description": "Indicates that a new workflow task should not be generated when this signal is received." + }, + "userMetadata": { + "$ref": "#/definitions/v1UserMetadata", + "description": "Metadata on the workflow if it is started. This is carried over to the WorkflowExecutionInfo\nfor use by user interfaces to display the fixed as-of-start summary and details of the\nworkflow." } } }, @@ -3132,6 +3136,10 @@ "$ref": "#/definitions/v1Callback" }, "description": "Callbacks to be called by the server when this workflow reaches a terminal state.\nIf the workflow continues-as-new, these callbacks will be carried over to the new execution.\nCallback addresses must be whitelisted in the server's dynamic configuration." + }, + "userMetadata": { + "$ref": "#/definitions/v1UserMetadata", + "description": "Metadata on the workflow if it is started. This is carried over to the WorkflowExecutionInfo\nfor use by user interfaces to display the fixed as-of-start summary and details of the\nworkflow." } } }, @@ -4344,6 +4352,10 @@ "commandType": { "$ref": "#/definitions/v1CommandType" }, + "userMetadata": { + "$ref": "#/definitions/v1UserMetadata", + "description": "Metadata on the command. This is sometimes carried over to the history event if one is\ncreated as a result of the command. Most commands won't have this information, and how this\ninformation is used is dependent upon the interface that reads it.\n\nCurrent well-known uses:\n * start_child_workflow_execution_command_attributes - populates\n temporal.api.workflow.v1.WorkflowExecutionInfo.user_metadata where the summary and details\n are used by user interfaces to show fixed as-of-start workflow summary and details.\n * start_timer_command_attributes - populates temporal.api.history.v1.HistoryEvent for timer\n started where the summary is used to identify the timer." + }, "scheduleActivityTaskCommandAttributes": { "$ref": "#/definitions/v1ScheduleActivityTaskCommandAttributes" }, @@ -5274,6 +5286,10 @@ "type": "boolean", "description": "Set to true when the SDK may ignore the event as it does not impact workflow state or\ninformation in any way that the SDK need be concerned with. If an SDK encounters an event\ntype which it does not understand, it must error unless this is true. If it is true, it's\nacceptable for the event type and/or attributes to be uninterpretable." }, + "userMetadata": { + "$ref": "#/definitions/v1UserMetadata", + "description": "Metadata on the event. This is often carried over from commands and client calls. Most events\nwon't have this information, and how this information is used is dependent upon the interface\nthat reads it.\n\nCurrent well-known uses:\n * workflow_execution_started_event_attributes - summary and details from start workflow.\n * timer_started_event_attributes - summary represents an identifier for the timer for use by\n user interfaces." + }, "workflowExecutionStartedEventAttributes": { "$ref": "#/definitions/v1WorkflowExecutionStartedEventAttributes" }, @@ -5964,6 +5980,10 @@ }, "header": { "$ref": "#/definitions/v1Header" + }, + "userMetadata": { + "$ref": "#/definitions/v1UserMetadata", + "description": "Metadata on the workflow if it is started. This is carried over to the WorkflowExecutionConfig\nfor use by user interfaces to display the fixed as-of-start summary and details of the\nworkflow." } }, "description": "NewWorkflowExecutionInfo is a shared message that encapsulates all the\nrequired arguments to starting a workflow in different contexts." @@ -8103,6 +8123,10 @@ "$ref": "#/definitions/v1Callback" }, "description": "Callbacks to be called by the server when this workflow reaches a terminal state.\nIf the workflow continues-as-new, these callbacks will be carried over to the new execution.\nCallback addresses must be whitelisted in the server's dynamic configuration." + }, + "userMetadata": { + "$ref": "#/definitions/v1UserMetadata", + "description": "Metadata on the workflow if it is started. This is carried over to the WorkflowExecutionInfo\nfor use by user interfaces to display the fixed as-of-start summary and details of the\nworkflow." } } }, @@ -8692,6 +8716,20 @@ } } }, + "v1UserMetadata": { + "type": "object", + "properties": { + "summary": { + "$ref": "#/definitions/v1Payload", + "description": "Short-form text that provides a summary. This payload should be a \"json/plain\"-encoded payload\nthat is a single JSON string for use in user interfaces. User interface formatting may not\napply to this text when used in \"title\" situations. The payload data section is limited to 400\nbytes by default." + }, + "details": { + "$ref": "#/definitions/v1Payload", + "description": "Long-form text that provides details. This payload should be a \"json/plain\"-encoded payload\nthat is a single JSON string for use in user interfaces. User interface formatting may apply to\nthis text in common use. The payload data section is limited to 20000 bytes by default." + } + }, + "description": "Information a user can set, often for use by user interfaces." + }, "v1VersionInfo": { "type": "object", "properties": { @@ -8834,6 +8872,10 @@ }, "defaultWorkflowTaskTimeout": { "type": "string" + }, + "userMetadata": { + "$ref": "#/definitions/v1UserMetadata", + "description": "User metadata provided on start workflow." } } }, diff --git a/openapi/openapiv3.yaml b/openapi/openapiv3.yaml index a58216b0..f385e643 100644 --- a/openapi/openapiv3.yaml +++ b/openapi/openapiv3.yaml @@ -3576,6 +3576,18 @@ components: information in any way that the SDK need be concerned with. If an SDK encounters an event type which it does not understand, it must error unless this is true. If it is true, it's acceptable for the event type and/or attributes to be uninterpretable. + userMetadata: + allOf: + - $ref: '#/components/schemas/UserMetadata' + description: |- + Metadata on the event. This is often carried over from commands and client calls. Most events + won't have this information, and how this information is used is dependent upon the interface + that reads it. + + Current well-known uses: + * workflow_execution_started_event_attributes - summary and details from start workflow. + * timer_started_event_attributes - summary represents an identifier for the timer for use by + user interfaces. workflowExecutionStartedEventAttributes: $ref: '#/components/schemas/WorkflowExecutionStartedEventAttributes' workflowExecutionCompletedEventAttributes: @@ -4051,6 +4063,13 @@ components: $ref: '#/components/schemas/SearchAttributes' header: $ref: '#/components/schemas/Header' + userMetadata: + allOf: + - $ref: '#/components/schemas/UserMetadata' + description: |- + Metadata on the workflow if it is started. This is carried over to the WorkflowExecutionConfig + for use by user interfaces to display the fixed as-of-start summary and details of the + workflow. description: |- NewWorkflowExecutionInfo is a shared message that encapsulates all the required arguments to starting a workflow in different contexts. @@ -5642,6 +5661,13 @@ components: skipGenerateWorkflowTask: type: boolean description: Indicates that a new workflow task should not be generated when this signal is received. + userMetadata: + allOf: + - $ref: '#/components/schemas/UserMetadata' + description: |- + Metadata on the workflow if it is started. This is carried over to the WorkflowExecutionInfo + for use by user interfaces to display the fixed as-of-start summary and details of the + workflow. SignalWithStartWorkflowExecutionResponse: type: object properties: @@ -5939,6 +5965,13 @@ components: Callbacks to be called by the server when this workflow reaches a terminal state. If the workflow continues-as-new, these callbacks will be carried over to the new execution. Callback addresses must be whitelisted in the server's dynamic configuration. + userMetadata: + allOf: + - $ref: '#/components/schemas/UserMetadata' + description: |- + Metadata on the workflow if it is started. This is carried over to the WorkflowExecutionInfo + for use by user interfaces to display the fixed as-of-start summary and details of the + workflow. StartWorkflowExecutionResponse: type: object properties: @@ -6444,6 +6477,25 @@ components: description: The `WORKFLOW_TASK_COMPLETED` event which this command was reported with searchAttributes: $ref: '#/components/schemas/SearchAttributes' + UserMetadata: + type: object + properties: + summary: + allOf: + - $ref: '#/components/schemas/Payload' + description: |- + Short-form text that provides a summary. This payload should be a "json/plain"-encoded payload + that is a single JSON string for use in user interfaces. User interface formatting may not + apply to this text when used in "title" situations. The payload data section is limited to 400 + bytes by default. + details: + allOf: + - $ref: '#/components/schemas/Payload' + description: |- + Long-form text that provides details. This payload should be a "json/plain"-encoded payload + that is a single JSON string for use in user interfaces. User interface formatting may apply to + this text in common use. The payload data section is limited to 20000 bytes by default. + description: Information a user can set, often for use by user interfaces. VersionInfo: type: object properties: @@ -6569,6 +6621,10 @@ components: defaultWorkflowTaskTimeout: pattern: ^-?(?:0|[1-9][0-9]{0,11})(?:\.[0-9]{1,9})?s$ type: string + userMetadata: + allOf: + - $ref: '#/components/schemas/UserMetadata' + description: User metadata provided on start workflow. WorkflowExecutionContinuedAsNewEventAttributes: type: object properties: diff --git a/temporal/api/command/v1/message.proto b/temporal/api/command/v1/message.proto index 9d9b6584..3b8c9d9f 100644 --- a/temporal/api/command/v1/message.proto +++ b/temporal/api/command/v1/message.proto @@ -38,6 +38,7 @@ import "temporal/api/enums/v1/command_type.proto"; import "temporal/api/common/v1/message.proto"; import "temporal/api/failure/v1/message.proto"; import "temporal/api/taskqueue/v1/message.proto"; +import "temporal/api/sdk/v1/user_metadata.proto"; message ScheduleActivityTaskCommandAttributes { string activity_id = 1; @@ -267,6 +268,18 @@ message RequestCancelNexusOperationCommandAttributes { message Command { temporal.api.enums.v1.CommandType command_type = 1; + // Metadata on the command. This is sometimes carried over to the history event if one is + // created as a result of the command. Most commands won't have this information, and how this + // information is used is dependent upon the interface that reads it. + // + // Current well-known uses: + // * start_child_workflow_execution_command_attributes - populates + // temporal.api.workflow.v1.WorkflowExecutionInfo.user_metadata where the summary and details + // are used by user interfaces to show fixed as-of-start workflow summary and details. + // * start_timer_command_attributes - populates temporal.api.history.v1.HistoryEvent for timer + // started where the summary is used to identify the timer. + temporal.api.sdk.v1.UserMetadata user_metadata = 301; + // The command details. The type must match that in `command_type`. oneof attributes { ScheduleActivityTaskCommandAttributes schedule_activity_task_command_attributes = 2; StartTimerCommandAttributes start_timer_command_attributes = 3; diff --git a/temporal/api/history/v1/message.proto b/temporal/api/history/v1/message.proto index f5689e7e..194b51fe 100644 --- a/temporal/api/history/v1/message.proto +++ b/temporal/api/history/v1/message.proto @@ -44,6 +44,7 @@ import "temporal/api/taskqueue/v1/message.proto"; import "temporal/api/update/v1/message.proto"; import "temporal/api/workflow/v1/message.proto"; import "temporal/api/sdk/v1/task_complete_metadata.proto"; +import "temporal/api/sdk/v1/user_metadata.proto"; // Always the first event in workflow history message WorkflowExecutionStartedEventAttributes { @@ -892,6 +893,15 @@ message HistoryEvent { // type which it does not understand, it must error unless this is true. If it is true, it's // acceptable for the event type and/or attributes to be uninterpretable. bool worker_may_ignore = 300; + // Metadata on the event. This is often carried over from commands and client calls. Most events + // won't have this information, and how this information is used is dependent upon the interface + // that reads it. + // + // Current well-known uses: + // * workflow_execution_started_event_attributes - summary and details from start workflow. + // * timer_started_event_attributes - summary represents an identifier for the timer for use by + // user interfaces. + temporal.api.sdk.v1.UserMetadata user_metadata = 301; // The event details. The type must match that in `event_type`. oneof attributes { WorkflowExecutionStartedEventAttributes workflow_execution_started_event_attributes = 6; diff --git a/temporal/api/sdk/v1/user_metadata.proto b/temporal/api/sdk/v1/user_metadata.proto new file mode 100644 index 00000000..307051f0 --- /dev/null +++ b/temporal/api/sdk/v1/user_metadata.proto @@ -0,0 +1,49 @@ +// The MIT License +// +// Copyright (c) 2024 Temporal Technologies Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +syntax = "proto3"; + +package temporal.api.sdk.v1; + +option go_package = "go.temporal.io/api/sdk/v1;sdk"; +option java_package = "io.temporal.api.sdk.v1"; +option java_multiple_files = true; +option java_outer_classname = "UserMetadataProto"; +option ruby_package = "Temporalio::Api::Sdk::V1"; +option csharp_namespace = "Temporalio.Api.Sdk.V1"; + + +import "temporal/api/common/v1/message.proto"; + +// Information a user can set, often for use by user interfaces. +message UserMetadata { + // Short-form text that provides a summary. This payload should be a "json/plain"-encoded payload + // that is a single JSON string for use in user interfaces. User interface formatting may not + // apply to this text when used in "title" situations. The payload data section is limited to 400 + // bytes by default. + temporal.api.common.v1.Payload summary = 1; + + // Long-form text that provides details. This payload should be a "json/plain"-encoded payload + // that is a single JSON string for use in user interfaces. User interface formatting may apply to + // this text in common use. The payload data section is limited to 20000 bytes by default. + temporal.api.common.v1.Payload details = 2; +} \ No newline at end of file diff --git a/temporal/api/sdk/v1/workflow_metadata.proto b/temporal/api/sdk/v1/workflow_metadata.proto index e45fe09e..e042b8b4 100644 --- a/temporal/api/sdk/v1/workflow_metadata.proto +++ b/temporal/api/sdk/v1/workflow_metadata.proto @@ -35,6 +35,9 @@ option csharp_namespace = "Temporalio.Api.Sdk.V1"; message WorkflowMetadata { // Metadata provided at declaration or creation time. WorkflowDefinition definition = 1; + // Current long-form details of the workflow's state. This is used by user interfaces to show + // long-form text. This text may be formatted by the user interface. + string current_details = 2; } // (-- api-linter: core::0203::optional=disabled --) @@ -42,13 +45,9 @@ message WorkflowDefinition { // A name scoped by the task queue that maps to this workflow definition. // If missing, this workflow is a dynamic workflow. string type = 1; - // An optional workflow description provided by the application. - // By convention, external tools may interpret its first part, - // i.e., ending with a line break, as a summary of the description. - string description = 2; - repeated WorkflowInteractionDefinition query_definitions = 3; - repeated WorkflowInteractionDefinition signal_definitions = 4; - repeated WorkflowInteractionDefinition update_definitions = 5; + repeated WorkflowInteractionDefinition query_definitions = 2; + repeated WorkflowInteractionDefinition signal_definitions = 3; + repeated WorkflowInteractionDefinition update_definitions = 4; } // (-- api-linter: core::0123::resource-annotation=disabled diff --git a/temporal/api/workflow/v1/message.proto b/temporal/api/workflow/v1/message.proto index 9b7a6477..ffad7bd8 100644 --- a/temporal/api/workflow/v1/message.proto +++ b/temporal/api/workflow/v1/message.proto @@ -40,6 +40,7 @@ import "temporal/api/enums/v1/workflow.proto"; import "temporal/api/common/v1/message.proto"; import "temporal/api/failure/v1/message.proto"; import "temporal/api/taskqueue/v1/message.proto"; +import "temporal/api/sdk/v1/user_metadata.proto"; message WorkflowExecutionInfo { temporal.api.common.v1.WorkflowExecution execution = 1; @@ -97,6 +98,8 @@ message WorkflowExecutionConfig { google.protobuf.Duration workflow_execution_timeout = 2; google.protobuf.Duration workflow_run_timeout = 3; google.protobuf.Duration default_workflow_task_timeout = 4; + // User metadata provided on start workflow. + temporal.api.sdk.v1.UserMetadata user_metadata = 5; } message PendingActivityInfo { @@ -197,6 +200,10 @@ message NewWorkflowExecutionInfo { temporal.api.common.v1.Memo memo = 11; temporal.api.common.v1.SearchAttributes search_attributes = 12; temporal.api.common.v1.Header header = 13; + // Metadata on the workflow if it is started. This is carried over to the WorkflowExecutionConfig + // for use by user interfaces to display the fixed as-of-start summary and details of the + // workflow. + temporal.api.sdk.v1.UserMetadata user_metadata = 14; } // CallbackInfo contains the state of an attached workflow callback. diff --git a/temporal/api/workflowservice/v1/request_response.proto b/temporal/api/workflowservice/v1/request_response.proto index 3176cb01..f245dd58 100644 --- a/temporal/api/workflowservice/v1/request_response.proto +++ b/temporal/api/workflowservice/v1/request_response.proto @@ -56,6 +56,7 @@ import "temporal/api/update/v1/message.proto"; import "temporal/api/version/v1/message.proto"; import "temporal/api/batch/v1/message.proto"; import "temporal/api/sdk/v1/task_complete_metadata.proto"; +import "temporal/api/sdk/v1/user_metadata.proto"; import "temporal/api/nexus/v1/message.proto"; import "google/protobuf/duration.proto"; @@ -192,6 +193,10 @@ message StartWorkflowExecutionRequest { // If the workflow continues-as-new, these callbacks will be carried over to the new execution. // Callback addresses must be whitelisted in the server's dynamic configuration. repeated temporal.api.common.v1.Callback completion_callbacks = 21; + // Metadata on the workflow if it is started. This is carried over to the WorkflowExecutionInfo + // for use by user interfaces to display the fixed as-of-start summary and details of the + // workflow. + temporal.api.sdk.v1.UserMetadata user_metadata = 23; } message StartWorkflowExecutionResponse { @@ -676,6 +681,10 @@ message SignalWithStartWorkflowExecutionRequest { google.protobuf.Duration workflow_start_delay = 20; // Indicates that a new workflow task should not be generated when this signal is received. bool skip_generate_workflow_task = 21; + // Metadata on the workflow if it is started. This is carried over to the WorkflowExecutionInfo + // for use by user interfaces to display the fixed as-of-start summary and details of the + // workflow. + temporal.api.sdk.v1.UserMetadata user_metadata = 23; } message SignalWithStartWorkflowExecutionResponse {