Skip to content

Commit 5123b9e

Browse files
authored
fix: Make search attribute type more lenient when parsing (#716)
* feat: produce deprecation when use activity method without the activity attribute (#677) * feat: produce deprecation when use activity method without the activity attribute * feat: add feature flag * fix: make psalm pass * fix: update baseline * feat: Make search attribute type more lenient when parsing
1 parent b90c12f commit 5123b9e

File tree

3 files changed

+159
-2
lines changed

3 files changed

+159
-2
lines changed

src/Common/SearchAttributes/ValueType.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,30 @@ enum ValueType: string
1616
case KeywordList = 'keyword_list';
1717
case Text = 'string';
1818
case Datetime = 'datetime';
19+
20+
/**
21+
* Parse a type string leniently, accepting the canonical form (e.g. "keyword"),
22+
* PascalCase form (e.g. "Keyword"), and SCREAMING_SNAKE_CASE proto enum form
23+
* (e.g. "INDEXED_VALUE_TYPE_KEYWORD").
24+
*/
25+
public static function fromMetadata(string $type): ?self
26+
{
27+
// Try canonical form first (e.g. "keyword", "int64")
28+
$result = self::tryFrom($type);
29+
if ($result !== null) {
30+
return $result;
31+
}
32+
33+
// PascalCase and SCREAMING_SNAKE_CASE forms
34+
return match ($type) {
35+
'Bool', 'INDEXED_VALUE_TYPE_BOOL' => self::Bool,
36+
'Double', 'INDEXED_VALUE_TYPE_DOUBLE' => self::Float,
37+
'Int', 'INDEXED_VALUE_TYPE_INT' => self::Int,
38+
'Keyword', 'INDEXED_VALUE_TYPE_KEYWORD' => self::Keyword,
39+
'KeywordList', 'INDEXED_VALUE_TYPE_KEYWORD_LIST' => self::KeywordList,
40+
'Text', 'INDEXED_VALUE_TYPE_TEXT' => self::Text,
41+
'Datetime', 'INDEXED_VALUE_TYPE_DATETIME' => self::Datetime,
42+
default => null,
43+
};
44+
}
1945
}

src/Common/TypedSearchAttributes.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,12 @@ public static function fromJsonArray(array $array): self
6262
$collection = new \SplObjectStorage();
6363
foreach ($array as $name => ['type' => $type, 'value' => $value]) {
6464
try {
65-
$vt = ValueType::from($type);
66-
$key = SearchAttributeKey::for($vt, $name);
65+
$valueType = ValueType::fromMetadata($type);
66+
if ($valueType === null) {
67+
continue;
68+
}
69+
70+
$key = SearchAttributeKey::for($valueType, $name);
6771
$collection->offsetSet($key, $key->valueSet($value)->value);
6872
} catch (\Throwable) {
6973
// Ignore invalid values.

tests/Unit/Common/TypedSearchAttributesTestCase.php

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Temporal\Tests\Unit\Common;
66

77
use Temporal\Common\SearchAttributes\SearchAttributeKey;
8+
use Temporal\Common\SearchAttributes\ValueType;
89
use Temporal\Common\TypedSearchAttributes;
910
use PHPUnit\Framework\TestCase;
1011

@@ -196,6 +197,92 @@ public function testFromJsonArray(): void
196197
self::assertSame(['foo', 'bar'], $collection->get(SearchAttributeKey::forKeywordList('name8')));
197198
}
198199

200+
public function testFromJsonArrayWithPascalCaseType(): void
201+
{
202+
$collection = TypedSearchAttributes::fromJsonArray([
203+
'name1' => [
204+
'type' => 'Bool',
205+
'value' => true,
206+
],
207+
'name2' => [
208+
'type' => 'Int',
209+
'value' => 42,
210+
],
211+
'name3' => [
212+
'type' => 'Double',
213+
'value' => 3.14,
214+
],
215+
'name4' => [
216+
'type' => 'Keyword',
217+
'value' => 'bar',
218+
],
219+
'name5' => [
220+
'type' => 'Text',
221+
'value' => 'foo',
222+
],
223+
'name6' => [
224+
'type' => 'Datetime',
225+
'value' => '2021-01-01T00:00:00+00:00',
226+
],
227+
'name7' => [
228+
'type' => 'KeywordList',
229+
'value' => ['foo', 'bar'],
230+
],
231+
]);
232+
233+
self::assertCount(7, $collection);
234+
self::assertTrue($collection->get(SearchAttributeKey::forBool('name1')));
235+
self::assertSame(42, $collection->get(SearchAttributeKey::forInteger('name2')));
236+
self::assertSame(3.14, $collection->get(SearchAttributeKey::forFloat('name3')));
237+
self::assertSame('bar', $collection->get(SearchAttributeKey::forKeyword('name4')));
238+
self::assertSame('foo', $collection->get(SearchAttributeKey::forText('name5')));
239+
self::assertInstanceOf(\DateTimeImmutable::class, $collection->get(SearchAttributeKey::forDatetime('name6')));
240+
self::assertSame(['foo', 'bar'], $collection->get(SearchAttributeKey::forKeywordList('name7')));
241+
}
242+
243+
public function testFromJsonArrayWithScreamingSnakeCaseType(): void
244+
{
245+
$collection = TypedSearchAttributes::fromJsonArray([
246+
'name1' => [
247+
'type' => 'INDEXED_VALUE_TYPE_BOOL',
248+
'value' => true,
249+
],
250+
'name2' => [
251+
'type' => 'INDEXED_VALUE_TYPE_INT',
252+
'value' => 42,
253+
],
254+
'name3' => [
255+
'type' => 'INDEXED_VALUE_TYPE_DOUBLE',
256+
'value' => 3.14,
257+
],
258+
'name4' => [
259+
'type' => 'INDEXED_VALUE_TYPE_KEYWORD',
260+
'value' => 'bar',
261+
],
262+
'name5' => [
263+
'type' => 'INDEXED_VALUE_TYPE_TEXT',
264+
'value' => 'foo',
265+
],
266+
'name6' => [
267+
'type' => 'INDEXED_VALUE_TYPE_DATETIME',
268+
'value' => '2021-01-01T00:00:00+00:00',
269+
],
270+
'name7' => [
271+
'type' => 'INDEXED_VALUE_TYPE_KEYWORD_LIST',
272+
'value' => ['foo', 'bar'],
273+
],
274+
]);
275+
276+
self::assertCount(7, $collection);
277+
self::assertTrue($collection->get(SearchAttributeKey::forBool('name1')));
278+
self::assertSame(42, $collection->get(SearchAttributeKey::forInteger('name2')));
279+
self::assertSame(3.14, $collection->get(SearchAttributeKey::forFloat('name3')));
280+
self::assertSame('bar', $collection->get(SearchAttributeKey::forKeyword('name4')));
281+
self::assertSame('foo', $collection->get(SearchAttributeKey::forText('name5')));
282+
self::assertInstanceOf(\DateTimeImmutable::class, $collection->get(SearchAttributeKey::forDatetime('name6')));
283+
self::assertSame(['foo', 'bar'], $collection->get(SearchAttributeKey::forKeywordList('name7')));
284+
}
285+
199286
public function testFromUntypedCollection(): void
200287
{
201288
$collection = TypedSearchAttributes::fromCollection([
@@ -219,6 +306,46 @@ public function testFromUntypedCollection(): void
219306
self::assertSame(['foo', 'bar'], $collection->get(SearchAttributeKey::forKeywordList('name8')));
220307
}
221308

309+
public function testValueTypeFromMetadataCanonical(): void
310+
{
311+
self::assertSame(ValueType::Bool, ValueType::fromMetadata('bool'));
312+
self::assertSame(ValueType::Float, ValueType::fromMetadata('float64'));
313+
self::assertSame(ValueType::Int, ValueType::fromMetadata('int64'));
314+
self::assertSame(ValueType::Keyword, ValueType::fromMetadata('keyword'));
315+
self::assertSame(ValueType::KeywordList, ValueType::fromMetadata('keyword_list'));
316+
self::assertSame(ValueType::Text, ValueType::fromMetadata('string'));
317+
self::assertSame(ValueType::Datetime, ValueType::fromMetadata('datetime'));
318+
}
319+
320+
public function testValueTypeFromMetadataPascalCase(): void
321+
{
322+
self::assertSame(ValueType::Bool, ValueType::fromMetadata('Bool'));
323+
self::assertSame(ValueType::Float, ValueType::fromMetadata('Double'));
324+
self::assertSame(ValueType::Int, ValueType::fromMetadata('Int'));
325+
self::assertSame(ValueType::Keyword, ValueType::fromMetadata('Keyword'));
326+
self::assertSame(ValueType::KeywordList, ValueType::fromMetadata('KeywordList'));
327+
self::assertSame(ValueType::Text, ValueType::fromMetadata('Text'));
328+
self::assertSame(ValueType::Datetime, ValueType::fromMetadata('Datetime'));
329+
}
330+
331+
public function testValueTypeFromMetadataScreamingSnakeCase(): void
332+
{
333+
self::assertSame(ValueType::Bool, ValueType::fromMetadata('INDEXED_VALUE_TYPE_BOOL'));
334+
self::assertSame(ValueType::Float, ValueType::fromMetadata('INDEXED_VALUE_TYPE_DOUBLE'));
335+
self::assertSame(ValueType::Int, ValueType::fromMetadata('INDEXED_VALUE_TYPE_INT'));
336+
self::assertSame(ValueType::Keyword, ValueType::fromMetadata('INDEXED_VALUE_TYPE_KEYWORD'));
337+
self::assertSame(ValueType::KeywordList, ValueType::fromMetadata('INDEXED_VALUE_TYPE_KEYWORD_LIST'));
338+
self::assertSame(ValueType::Text, ValueType::fromMetadata('INDEXED_VALUE_TYPE_TEXT'));
339+
self::assertSame(ValueType::Datetime, ValueType::fromMetadata('INDEXED_VALUE_TYPE_DATETIME'));
340+
}
341+
342+
public function testValueTypeFromMetadataUnknown(): void
343+
{
344+
self::assertNull(ValueType::fromMetadata('unknown'));
345+
self::assertNull(ValueType::fromMetadata(''));
346+
self::assertNull(ValueType::fromMetadata('INDEXED_VALUE_TYPE_UNKNOWN'));
347+
}
348+
222349
public function testValues()
223350
{
224351
$collection = TypedSearchAttributes::empty()

0 commit comments

Comments
 (0)