• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

temporalio / sdk-java / #175

pending completion
#175

push

github-actions

web-flow
Worker / Build Id versioning (#1786)

Implement new worker build id based versioning feature

236 of 236 new or added lines in 24 files covered. (100.0%)

18343 of 23697 relevant lines covered (77.41%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.27
/temporal-sdk/src/main/java/io/temporal/internal/common/SearchAttributesUtil.java
1
/*
2
 * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
3
 *
4
 * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5
 *
6
 * Modifications copyright (C) 2017 Uber Technologies, Inc.
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this material except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *   http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20

21
package io.temporal.internal.common;
22

23
import com.google.common.base.MoreObjects;
24
import io.temporal.api.common.v1.Payload;
25
import io.temporal.api.common.v1.SearchAttributes;
26
import io.temporal.api.enums.v1.IndexedValueType;
27
import io.temporal.common.SearchAttributeUpdate;
28
import java.util.Collections;
29
import java.util.HashMap;
30
import java.util.List;
31
import java.util.Map;
32
import javax.annotation.Nonnull;
33
import javax.annotation.Nullable;
34

35
public class SearchAttributesUtil {
×
36
  private static final SearchAttributePayloadConverter converter =
1✔
37
      SearchAttributePayloadConverter.INSTANCE;
38

39
  @Nullable
40
  public static SearchAttributes encodeTyped(
41
      @Nullable io.temporal.common.SearchAttributes searchAttributes) {
42
    if (searchAttributes == null || searchAttributes.size() == 0) {
1✔
43
      return null;
×
44
    }
45
    SearchAttributes.Builder builder = SearchAttributes.newBuilder();
1✔
46
    searchAttributes
1✔
47
        .getUntypedValues()
1✔
48
        .forEach(
1✔
49
            (key, value) ->
50
                builder.putIndexedFields(key.getName(), converter.encodeTyped(key, value)));
1✔
51
    return builder.build();
1✔
52
  }
53

54
  @Nonnull
55
  public static io.temporal.common.SearchAttributes decodeTyped(
56
      @Nullable SearchAttributes searchAttributes) {
57
    if (searchAttributes == null || searchAttributes.getIndexedFieldsCount() == 0) {
1✔
58
      return io.temporal.common.SearchAttributes.EMPTY;
×
59
    }
60
    io.temporal.common.SearchAttributes.Builder builder =
61
        io.temporal.common.SearchAttributes.newBuilder();
1✔
62
    searchAttributes
1✔
63
        .getIndexedFieldsMap()
1✔
64
        .forEach((key, value) -> converter.decodeTyped(builder, key, value));
1✔
65
    return builder.build();
1✔
66
  }
67

68
  @Nonnull
69
  public static SearchAttributes encodeTypedUpdates(
70
      SearchAttributeUpdate<?>... searchAttributeUpdates) {
71
    if (searchAttributeUpdates == null || searchAttributeUpdates.length == 0) {
1✔
72
      throw new IllegalArgumentException("At least one update required");
×
73
    }
74
    SearchAttributes.Builder builder = SearchAttributes.newBuilder();
1✔
75
    for (SearchAttributeUpdate<?> update : searchAttributeUpdates) {
1✔
76
      // Null is ok, it represents unset
77
      builder.putIndexedFields(
1✔
78
          update.getKey().getName(), converter.encodeTyped(update.getKey(), update.getValue()));
1✔
79
    }
80
    return builder.build();
1✔
81
  }
82

83
  @SuppressWarnings("deprecation")
84
  @Nonnull
85
  public static SearchAttributes encode(@Nonnull Map<String, ?> searchAttributes) {
86
    SearchAttributes.Builder builder = SearchAttributes.newBuilder();
1✔
87
    searchAttributes.forEach(
1✔
88
        (key, value) ->
89
            builder.putIndexedFields(
1✔
90
                key,
91
                converter.encode(
1✔
92
                    // Right now we don't have unset capability, so we get the same result by
93
                    // persisting an empty collection if null or empty collection is passed.
94
                    // See: https://github.com/temporalio/temporal/issues/561
95
                    MoreObjects.firstNonNull(
1✔
96
                        value, io.temporal.common.SearchAttribute.UNSET_VALUE))));
97

98
    return builder.build();
1✔
99
  }
100

101
  @Nonnull
102
  public static Map<String, List<?>> decode(@Nonnull SearchAttributes searchAttributes) {
103
    if (isEmpty(searchAttributes)) {
1✔
104
      return Collections.emptyMap();
×
105
    }
106
    Map<String, Payload> searchAttributesMap = searchAttributes.getIndexedFieldsMap();
1✔
107
    Map<String, List<?>> deserializedMap = new HashMap<>(searchAttributesMap.size() * 4 / 3);
1✔
108
    searchAttributesMap.forEach(
1✔
109
        (key, payload) -> {
110
          List<?> data = converter.decode(payload);
1✔
111
          if (data.size() > 0) {
1✔
112
            // User code should observe the empty collection as non-existent search attribute,
113
            // because
114
            // it's effectively the same.
115
            // We use an empty collection for "unset". See:
116
            // https://github.com/temporalio/temporal/issues/561
117
            deserializedMap.put(key, data);
1✔
118
          }
119
        });
1✔
120

121
    return deserializedMap;
1✔
122
  }
123

124
  @SuppressWarnings("unchecked")
125
  @Nullable
126
  public static <T> List<T> decode(
127
      @Nonnull SearchAttributes searchAttributes, @Nonnull String attributeName) {
128
    if (isEmpty(searchAttributes) || !searchAttributes.containsIndexedFields(attributeName)) {
1✔
129
      return null;
×
130
    }
131
    Payload payload = searchAttributes.getIndexedFieldsOrThrow(attributeName);
1✔
132
    List<?> data = converter.decode(payload);
1✔
133
    if (data.size() == 0) {
1✔
134
      // User code should observe the empty collection as non-existent search attribute, because
135
      // it's effectively the same.
136
      // We use an empty collection for "unset". See:
137
      // https://github.com/temporalio/temporal/issues/561
138
      return null;
1✔
139
    }
140
    return (List<T>) data;
1✔
141
  }
142

143
  @SuppressWarnings("unchecked")
144
  @Nullable
145
  public static <T> List<T> decodeAsType(
146
      @Nonnull SearchAttributes searchAttributes,
147
      @Nonnull String attributeName,
148
      @Nonnull IndexedValueType indexType) {
149
    if (isEmpty(searchAttributes) || !searchAttributes.containsIndexedFields(attributeName)) {
1✔
150
      return null;
×
151
    }
152
    Payload payload = searchAttributes.getIndexedFieldsOrThrow(attributeName);
1✔
153
    List<?> data = converter.decodeAsType(payload, indexType);
1✔
154
    if (data.size() == 0) {
1✔
155
      // User code should observe the empty collection as non-existent search attribute, because
156
      // it's effectively the same.
157
      // We use an empty collection for "unset". See:
158
      // https://github.com/temporalio/temporal/issues/561
159
      return null;
1✔
160
    }
161
    return (List<T>) data;
1✔
162
  }
163

164
  private static boolean isEmpty(SearchAttributes searchAttributes) {
165
    return searchAttributes.getIndexedFieldsCount() == 0;
1✔
166
  }
167
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc