Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typed search attributes #1612

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
11 changes: 4 additions & 7 deletions packages/client/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ServiceError as GrpcServiceError, status as grpcStatus } from '@grpc/grpc-js';
import {
decodeSearchAttributes,
decodeTypedSearchAttributes,
LoadedDataConverter,
mapFromPayloads,
NamespaceNotFoundError,
searchAttributePayloadConverter,
SearchAttributes,
} from '@temporalio/common';
import { Replace } from '@temporalio/common/lib/type-helpers';
import { optionalTsToDate, requiredTsToDate } from '@temporalio/common/lib/time';
Expand Down Expand Up @@ -71,11 +71,8 @@ export async function executionInfoFromRaw<T>(
executionTime: optionalTsToDate(raw.executionTime),
closeTime: optionalTsToDate(raw.closeTime),
memo: await decodeMapFromPayloads(dataConverter, raw.memo?.fields),
searchAttributes: Object.fromEntries(
Object.entries(
mapFromPayloads(searchAttributePayloadConverter, raw.searchAttributes?.indexedFields ?? {}) as SearchAttributes
).filter(([_, v]) => v && v.length > 0) // Filter out empty arrays returned by pre 1.18 servers
),
searchAttributes: decodeSearchAttributes(raw.searchAttributes?.indexedFields),
typedSearchAttributes: decodeTypedSearchAttributes(raw.searchAttributes?.indexedFields),
parentExecution: raw.parentExecution
? {
workflowId: raw.parentExecution.workflowId!,
Expand Down
25 changes: 16 additions & 9 deletions packages/client/src/schedule-client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { status as grpcStatus } from '@grpc/grpc-js';
import { v4 as uuid4 } from 'uuid';
import { mapToPayloads, searchAttributePayloadConverter, Workflow } from '@temporalio/common';
import {
decodeSearchAttributes,
decodeTypedSearchAttributes,
encodeUnifiedSearchAttributes,
Workflow,
} from '@temporalio/common';
import { composeInterceptors, Headers } from '@temporalio/common/lib/interceptors';
import {
encodeMapToPayloads,
Expand Down Expand Up @@ -39,7 +44,6 @@
decodeScheduleRecentActions,
decodeScheduleRunningActions,
decodeScheduleSpec,
decodeSearchAttributes,
encodeScheduleAction,
encodeSchedulePolicies,
encodeScheduleSpec,
Expand Down Expand Up @@ -238,11 +242,12 @@
state: encodeScheduleState(opts.state),
},
memo: opts.memo ? { fields: await encodeMapToPayloads(this.dataConverter, opts.memo) } : undefined,
searchAttributes: opts.searchAttributes
? {
indexedFields: mapToPayloads(searchAttributePayloadConverter, opts.searchAttributes),
}
: undefined,
searchAttributes:
opts.searchAttributes || opts.typedSearchAttributes

Check warning on line 246 in packages/client/src/schedule-client.ts

View workflow job for this annotation

GitHub Actions / Lint and Prune / Lint and Prune

'searchAttributes' is deprecated. Use {@link typedSearchAttributes } instead
? {
indexedFields: encodeUnifiedSearchAttributes(opts.searchAttributes, opts.typedSearchAttributes),

Check warning on line 248 in packages/client/src/schedule-client.ts

View workflow job for this annotation

GitHub Actions / Lint and Prune / Lint and Prune

'searchAttributes' is deprecated. Use {@link typedSearchAttributes } instead
}
: undefined,
initialPatch: {
triggerImmediately: opts.state?.triggerImmediately
? { overlapPolicy: temporal.api.enums.v1.ScheduleOverlapPolicy.SCHEDULE_OVERLAP_POLICY_ALLOW_ALL }
Expand Down Expand Up @@ -388,7 +393,8 @@
workflowType: raw.info.workflowType.name,
},
memo: await decodeMapFromPayloads(this.dataConverter, raw.memo?.fields),
searchAttributes: decodeSearchAttributes(raw.searchAttributes),
searchAttributes: decodeSearchAttributes(raw.searchAttributes?.indexedFields),
typedSearchAttributes: decodeTypedSearchAttributes(raw.searchAttributes?.indexedFields),
state: {
paused: raw.info?.paused === true,
note: raw.info?.notes ?? undefined,
Expand Down Expand Up @@ -425,7 +431,8 @@
spec: decodeScheduleSpec(raw.schedule.spec),
action: await decodeScheduleAction(this.client.dataConverter, raw.schedule.action),
memo: await decodeMapFromPayloads(this.client.dataConverter, raw.memo?.fields),
searchAttributes: decodeSearchAttributes(raw.searchAttributes),
searchAttributes: decodeSearchAttributes(raw.searchAttributes?.indexedFields),
typedSearchAttributes: decodeTypedSearchAttributes(raw.searchAttributes?.indexedFields),
policies: {
// 'overlap' should never be missing on describe, as the server will replace UNSPECIFIED by an actual value
overlap: decodeScheduleOverlapPolicy(raw.schedule.policies?.overlapPolicy) ?? ScheduleOverlapPolicy.SKIP,
Expand Down
39 changes: 11 additions & 28 deletions packages/client/src/schedule-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import Long from 'long'; // eslint-disable-line import/no-named-as-default
import {
compileRetryPolicy,
decodeSearchAttributes,
decodeTypedSearchAttributes,
decompileRetryPolicy,
encodeUnifiedSearchAttributes,
extractWorkflowType,
LoadedDataConverter,
mapFromPayloads,
mapToPayloads,
searchAttributePayloadConverter,
SearchAttributes,
} from '@temporalio/common';
import { Headers } from '@temporalio/common/lib/interceptors';
import {
Expand Down Expand Up @@ -260,11 +259,12 @@
workflowTaskTimeout: msOptionalToTs(action.workflowTaskTimeout),
retryPolicy: action.retry ? compileRetryPolicy(action.retry) : undefined,
memo: action.memo ? { fields: await encodeMapToPayloads(dataConverter, action.memo) } : undefined,
searchAttributes: action.searchAttributes
? {
indexedFields: mapToPayloads(searchAttributePayloadConverter, action.searchAttributes),
}
: undefined,
searchAttributes:
action.searchAttributes || action.typedSearchAttributes

Check warning on line 263 in packages/client/src/schedule-helpers.ts

View workflow job for this annotation

GitHub Actions / Lint and Prune / Lint and Prune

'searchAttributes' is deprecated. Use {@link typedSearchAttributes } instead
? {
indexedFields: encodeUnifiedSearchAttributes(action.searchAttributes, action.typedSearchAttributes),

Check warning on line 265 in packages/client/src/schedule-helpers.ts

View workflow job for this annotation

GitHub Actions / Lint and Prune / Lint and Prune

'searchAttributes' is deprecated. Use {@link typedSearchAttributes } instead
}
: undefined,
header: { fields: headers },
},
};
Expand Down Expand Up @@ -326,14 +326,8 @@
args: await decodeArrayFromPayloads(dataConverter, pb.startWorkflow.input?.payloads),
memo: await decodeMapFromPayloads(dataConverter, pb.startWorkflow.memo?.fields),
retry: decompileRetryPolicy(pb.startWorkflow.retryPolicy),
searchAttributes: Object.fromEntries(
Object.entries(
mapFromPayloads(
searchAttributePayloadConverter,
pb.startWorkflow.searchAttributes?.indexedFields ?? {}
) as SearchAttributes
)
),
searchAttributes: decodeSearchAttributes(pb.startWorkflow.searchAttributes?.indexedFields),
typedSearchAttributes: decodeTypedSearchAttributes(pb.startWorkflow.searchAttributes?.indexedFields),
workflowExecutionTimeout: optionalTsToMs(pb.startWorkflow.workflowExecutionTimeout),
workflowRunTimeout: optionalTsToMs(pb.startWorkflow.workflowRunTimeout),
workflowTaskTimeout: optionalTsToMs(pb.startWorkflow.workflowTaskTimeout),
Expand All @@ -342,17 +336,6 @@
throw new TypeError('Unsupported schedule action');
}

export function decodeSearchAttributes(
pb: temporal.api.common.v1.ISearchAttributes | undefined | null
): SearchAttributes {
if (!pb?.indexedFields) return {};
return Object.fromEntries(
Object.entries(mapFromPayloads(searchAttributePayloadConverter, pb.indexedFields) as SearchAttributes).filter(
([_, v]) => v && v.length > 0
) // Filter out empty arrays returned by pre 1.18 servers
);
}

export function decodeScheduleRunningActions(
pb?: temporal.api.common.v1.IWorkflowExecution[] | null
): ScheduleExecutionStartWorkflowActionResult[] {
Expand Down
53 changes: 47 additions & 6 deletions packages/client/src/schedule-types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { checkExtends, Replace } from '@temporalio/common/lib/type-helpers';
import { Duration, SearchAttributes, Workflow } from '@temporalio/common';
import {
Duration,
SearchAttributes,
Workflow,
TypedSearchAttributePair,
TypedSearchAttributes,
} from '@temporalio/common';
import { makeProtoEnumConverters } from '@temporalio/common/lib/internal-workflow';
import type { temporal } from '@temporalio/proto';
import { WorkflowStartOptions } from './workflow-options';
Expand Down Expand Up @@ -70,9 +76,22 @@ export interface ScheduleOptions<A extends ScheduleOptionsAction = ScheduleOptio
* https://docs.temporal.io/docs/typescript/search-attributes
*
* Values are always converted using {@link JsonPayloadConverter}, even when a custom Data Converter is provided.
*
* @deprecated Use {@link typedSearchAttributes} instead.
*/
searchAttributes?: SearchAttributes;

/**
* Additional indexed information attached to the Schedule. More info:
* https://docs.temporal.io/docs/typescript/search-attributes
*
* Values are always converted using {@link JsonPayloadConverter}, even when a custom Data Converter is provided.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also mention that SA don't get encoded (i.e. they don't go through PayloadCodec). That's important as people may not realize that there are security implication in using SA. Please make sure that's fact is clear everywhere that a user would supply search attributes.

Copy link
Contributor Author

@THardy98 THardy98 Jan 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added small note that users shouldn't include sensitive information in SAs

*
* If both {@link searchAttributes} and {@link typedSearchAttributes} are provided, conflicting keys will be overwritten
* by {@link typedSearchAttributes}.
*/
typedSearchAttributes?: TypedSearchAttributePair[] | TypedSearchAttributes;

/**
* The initial state of the schedule, right after creation or update.
*/
Expand Down Expand Up @@ -129,7 +148,7 @@ export type CompiledScheduleOptions = Replace<
* The specification of an updated Schedule, as expected by {@link ScheduleHandle.update}.
*/
export type ScheduleUpdateOptions<A extends ScheduleOptionsAction = ScheduleOptionsAction> = Replace<
Omit<ScheduleOptions, 'scheduleId' | 'memo' | 'searchAttributes'>,
Omit<ScheduleOptions, 'scheduleId' | 'memo' | 'searchAttributes' | 'typedSearchAttributes'>,
{
action: A;
state: Omit<ScheduleOptions['state'], 'triggerImmediately' | 'backfill'>;
Expand Down Expand Up @@ -172,13 +191,23 @@ export interface ScheduleSummary {
memo?: Record<string, unknown>;

/**
* Additional indexed information attached to the Schedule.
* More info: https://docs.temporal.io/docs/typescript/search-attributes
* Additional indexed information attached to the Schedule. More info:
* https://docs.temporal.io/docs/typescript/search-attributes
*
* Values are always converted using {@link JsonPayloadConverter}, even when a custom Data Converter is provided.
*
* @deprecated Use {@link typedSearchAttributes} instead.
*/
searchAttributes?: SearchAttributes;

/**
* Additional indexed information attached to the Schedule. More info:
* https://docs.temporal.io/docs/typescript/search-attributes
*
* Values are always converted using {@link JsonPayloadConverter}, even when a custom Data Converter is provided.
*/
typedSearchAttributes?: TypedSearchAttributes;

state: {
/**
* Whether Schedule is currently paused.
Expand Down Expand Up @@ -284,13 +313,23 @@ export type ScheduleDescription = {
memo?: Record<string, unknown>;

/**
* Additional indexed information attached to the Schedule.
* More info: https://docs.temporal.io/docs/typescript/search-attributes
* Additional indexed information attached to the Schedule. More info:
* https://docs.temporal.io/docs/typescript/search-attributes
*
* Values are always converted using {@link JsonPayloadConverter}, even when a custom Data Converter is provided.
*
* @deprecated Use {@link typedSearchAttributes} instead.
*/
searchAttributes: SearchAttributes;

/**
* Additional indexed information attached to the Schedule. More info:
* https://docs.temporal.io/docs/typescript/search-attributes
*
* Values are always converted using {@link JsonPayloadConverter}, even when a custom Data Converter is provided.
*/
typedSearchAttributes: TypedSearchAttributes;

state: {
/**
* Whether Schedule is currently paused.
Expand Down Expand Up @@ -745,6 +784,7 @@ export type ScheduleOptionsStartWorkflowAction<W extends Workflow> = {
| 'args'
| 'memo'
| 'searchAttributes'
| 'typedSearchAttributes'
| 'retry'
| 'workflowExecutionTimeout'
| 'workflowRunTimeout'
Expand Down Expand Up @@ -776,6 +816,7 @@ export type ScheduleDescriptionStartWorkflowAction = ScheduleSummaryStartWorkflo
| 'args'
| 'memo'
| 'searchAttributes'
| 'typedSearchAttributes'
| 'retry'
| 'workflowExecutionTimeout'
| 'workflowRunTimeout'
Expand Down
4 changes: 3 additions & 1 deletion packages/client/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type * as grpc from '@grpc/grpc-js';
import type { SearchAttributes, SearchAttributeValue } from '@temporalio/common';
import type { SearchAttributes, SearchAttributeValue, TypedSearchAttributes } from '@temporalio/common';
import { makeProtoEnumConverters } from '@temporalio/common/lib/internal-workflow';
import * as proto from '@temporalio/proto';
import { Replace } from '@temporalio/common/lib/type-helpers';
Expand Down Expand Up @@ -47,7 +47,9 @@ export interface WorkflowExecutionInfo {
executionTime?: Date;
closeTime?: Date;
memo?: Record<string, unknown>;
/** @deprecated Use {@link typedSearchAttributes} instead. */
searchAttributes: SearchAttributes;
typedSearchAttributes: TypedSearchAttributes;
parentExecution?: Required<proto.temporal.api.common.v1.IWorkflowExecution>;
raw: RawWorkflowExecutionInfo;
}
Expand Down
26 changes: 14 additions & 12 deletions packages/client/src/workflow-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@
BaseWorkflowHandle,
CancelledFailure,
compileRetryPolicy,
mapToPayloads,
HistoryAndWorkflowId,
QueryDefinition,
RetryState,
searchAttributePayloadConverter,
SignalDefinition,
UpdateDefinition,
TerminatedFailure,
Expand All @@ -24,6 +22,7 @@
decodeRetryState,
encodeWorkflowIdConflictPolicy,
WorkflowIdConflictPolicy,
encodeUnifiedSearchAttributes,
} from '@temporalio/common';
import { composeInterceptors } from '@temporalio/common/lib/interceptors';
import { History } from '@temporalio/common/lib/proto-utils';
Expand Down Expand Up @@ -1218,11 +1217,12 @@
workflowStartDelay: options.startDelay,
retryPolicy: options.retry ? compileRetryPolicy(options.retry) : undefined,
memo: options.memo ? { fields: await encodeMapToPayloads(this.dataConverter, options.memo) } : undefined,
searchAttributes: options.searchAttributes
? {
indexedFields: mapToPayloads(searchAttributePayloadConverter, options.searchAttributes),
}
: undefined,
searchAttributes:
options.searchAttributes || options.typedSearchAttributes

Check warning on line 1221 in packages/client/src/workflow-client.ts

View workflow job for this annotation

GitHub Actions / Lint and Prune / Lint and Prune

'searchAttributes' is deprecated. Use {@link typedSearchAttributes } instead
? {
indexedFields: encodeUnifiedSearchAttributes(options.searchAttributes, options.typedSearchAttributes),

Check warning on line 1223 in packages/client/src/workflow-client.ts

View workflow job for this annotation

GitHub Actions / Lint and Prune / Lint and Prune

'searchAttributes' is deprecated. Use {@link typedSearchAttributes } instead
}
: undefined,
cronSchedule: options.cronSchedule,
header: { fields: headers },
};
Expand Down Expand Up @@ -1265,6 +1265,7 @@
protected async createStartWorkflowRequest(input: WorkflowStartInput): Promise<StartWorkflowExecutionRequest> {
const { options: opts, workflowType, headers } = input;
const { identity, namespace } = this.options;

return {
namespace,
identity,
Expand All @@ -1284,11 +1285,12 @@
workflowStartDelay: opts.startDelay,
retryPolicy: opts.retry ? compileRetryPolicy(opts.retry) : undefined,
memo: opts.memo ? { fields: await encodeMapToPayloads(this.dataConverter, opts.memo) } : undefined,
searchAttributes: opts.searchAttributes
? {
indexedFields: mapToPayloads(searchAttributePayloadConverter, opts.searchAttributes),
}
: undefined,
searchAttributes:
opts.searchAttributes || opts.typedSearchAttributes

Check warning on line 1289 in packages/client/src/workflow-client.ts

View workflow job for this annotation

GitHub Actions / Lint and Prune / Lint and Prune

'searchAttributes' is deprecated. Use {@link typedSearchAttributes } instead
? {
indexedFields: encodeUnifiedSearchAttributes(opts.searchAttributes, opts.typedSearchAttributes),

Check warning on line 1291 in packages/client/src/workflow-client.ts

View workflow job for this annotation

GitHub Actions / Lint and Prune / Lint and Prune

'searchAttributes' is deprecated. Use {@link typedSearchAttributes } instead
}
: undefined,
cronSchedule: opts.cronSchedule,
header: { fields: headers },
};
Expand Down
Loading
Loading