Skip to content

Commit

Permalink
Fix reserved search attribute name validation logic (#1398)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexshtin committed Mar 26, 2021
1 parent 9176647 commit 46920a1
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 58 deletions.
115 changes: 77 additions & 38 deletions common/searchattribute/defs.go
Expand Up @@ -38,34 +38,73 @@ const (
ExecutionTime = "ExecutionTime"
CloseTime = "CloseTime"
ExecutionStatus = "ExecutionStatus"

// TODO (alex): move these 3 to non-indexed.
HistoryLength = "HistoryLength"
TaskQueue = "TaskQueue"
Encoding = "Encoding"
TaskQueue = "TaskQueue"

// Valid non-indexed fields on ES.
HistoryLength = "HistoryLength"
Encoding = "Encoding"
Memo = "Memo"
VisibilityTaskKey = "VisibilityTaskKey"

// Attr is prefix of custom search attributes.
// Attr is prefix for search attributes.
Attr = "Attr"
// Custom search attributes.
CustomStringField = "CustomStringField"
CustomKeywordField = "CustomKeywordField"
CustomIntField = "CustomIntField"
CustomDoubleField = "CustomDoubleField"
CustomBoolField = "CustomBoolField"
CustomDatetimeField = "CustomDatetimeField"
// System search attributes.
TemporalChangeVersion = "TemporalChangeVersion"
BinaryChecksums = "BinaryChecksums"
CustomNamespace = "CustomNamespace"
Operator = "Operator"

// Default custom search attributes.
CustomStringField = "CustomStringField"
CustomKeywordField = "CustomKeywordField"
CustomIntField = "CustomIntField"
CustomDoubleField = "CustomDoubleField"
CustomBoolField = "CustomBoolField"
CustomDatetimeField = "CustomDatetimeField"
)

var (
// systemTypeMap is internal system search attributes.
systemTypeMap = map[string]enumspb.IndexedValueType{
// reservedFields are internal field names that can't be used as search attribute names.
reservedFields = map[string]struct{}{
NamespaceID: {},
WorkflowID: {},
RunID: {},
WorkflowType: {},
StartTime: {},
ExecutionTime: {},
CloseTime: {},
ExecutionStatus: {},
TaskQueue: {},

HistoryLength: {},
Encoding: {},
Memo: {},
VisibilityTaskKey: {},
}

// systemSearchAttributes are internal system search attributes which don't require Attr prefix.
systemSearchAttributes = map[string]struct{}{
NamespaceID: {},
WorkflowID: {},
RunID: {},
WorkflowType: {},
StartTime: {},
ExecutionTime: {},
CloseTime: {},
ExecutionStatus: {},
TaskQueue: {},
}

// builtInSearchAttributes are built-in internal search attributes.
builtInSearchAttributes = map[string]struct{}{
TemporalChangeVersion: {},
BinaryChecksums: {},
CustomNamespace: {},
Operator: {},
}

// defaultTypeMap defines default search attributes and their types.
defaultTypeMap = map[string]interface{}{
NamespaceID: enumspb.INDEXED_VALUE_TYPE_KEYWORD,
WorkflowID: enumspb.INDEXED_VALUE_TYPE_KEYWORD,
RunID: enumspb.INDEXED_VALUE_TYPE_KEYWORD,
Expand All @@ -74,41 +113,41 @@ var (
ExecutionTime: enumspb.INDEXED_VALUE_TYPE_INT,
CloseTime: enumspb.INDEXED_VALUE_TYPE_INT,
ExecutionStatus: enumspb.INDEXED_VALUE_TYPE_INT,
HistoryLength: enumspb.INDEXED_VALUE_TYPE_INT,
TaskQueue: enumspb.INDEXED_VALUE_TYPE_KEYWORD,
Encoding: enumspb.INDEXED_VALUE_TYPE_KEYWORD,
}

// defaultTypeMap defines all search attributes.
defaultTypeMap = newDefaultTypeMap()
)
CustomStringField: enumspb.INDEXED_VALUE_TYPE_STRING,
CustomKeywordField: enumspb.INDEXED_VALUE_TYPE_KEYWORD,
CustomIntField: enumspb.INDEXED_VALUE_TYPE_INT,
CustomDoubleField: enumspb.INDEXED_VALUE_TYPE_DOUBLE,
CustomBoolField: enumspb.INDEXED_VALUE_TYPE_BOOL,
CustomDatetimeField: enumspb.INDEXED_VALUE_TYPE_DATETIME,

func newDefaultTypeMap() map[string]interface{} {
result := map[string]interface{}{
CustomStringField: enumspb.INDEXED_VALUE_TYPE_STRING,
CustomKeywordField: enumspb.INDEXED_VALUE_TYPE_KEYWORD,
CustomIntField: enumspb.INDEXED_VALUE_TYPE_INT,
CustomDoubleField: enumspb.INDEXED_VALUE_TYPE_DOUBLE,
CustomBoolField: enumspb.INDEXED_VALUE_TYPE_BOOL,
CustomDatetimeField: enumspb.INDEXED_VALUE_TYPE_DATETIME,
TemporalChangeVersion: enumspb.INDEXED_VALUE_TYPE_KEYWORD,
BinaryChecksums: enumspb.INDEXED_VALUE_TYPE_KEYWORD,
CustomNamespace: enumspb.INDEXED_VALUE_TYPE_KEYWORD,
Operator: enumspb.INDEXED_VALUE_TYPE_KEYWORD,
}
for k, v := range systemTypeMap {
result[k] = v
}
return result
)

// IsReservedField return true if field name is system reserved.
func IsReservedField(fieldName string) bool {
_, ok := reservedFields[fieldName]
return ok
}

// GetDefaultTypeMap return default valid search attributes.
func GetDefaultTypeMap() map[string]interface{} {
return defaultTypeMap
// IsBuiltIn return true if search attribute is system built-in.
func IsBuiltIn(saName string) bool {
_, ok := builtInSearchAttributes[saName]
return ok
}

// IsSystem return true if search attribute is system.
// IsSystem return true if search attribute is system and doesn't require Attr prefix.
func IsSystem(saName string) bool {
_, ok := systemTypeMap[saName]
_, ok := systemSearchAttributes[saName]
return ok
}

// GetDefaultTypeMap return default valid search attributes.
func GetDefaultTypeMap() map[string]interface{} {
return defaultTypeMap
}
4 changes: 2 additions & 2 deletions common/searchattribute/validator.go
Expand Up @@ -106,8 +106,8 @@ func (v *Validator) Validate(searchAttributes *commonpb.SearchAttributes, namesp
}

// verify: saName is not system reserved
if IsSystem(saName) {
return serviceerror.NewInvalidArgument(fmt.Sprintf("%s is read-only Temporal reserved search attribute", saName))
if IsReservedField(saName) {
return serviceerror.NewInvalidArgument(fmt.Sprintf("%s is Temporal reserved field name", saName))
}
// verify: size of single value <= limit
dataSize := len(saPayload.GetData())
Expand Down
2 changes: 1 addition & 1 deletion common/searchattribute/validator_test.go
Expand Up @@ -110,7 +110,7 @@ func (s *searchAttributesValidatorSuite) TestValidateSearchAttributes() {
}
attr.IndexedFields = fields
err = validator.Validate(attr, namespace)
s.Equal("StartTime is read-only Temporal reserved search attribute", err.Error())
s.Equal("StartTime is Temporal reserved field name", err.Error())

fields = map[string]*commonpb.Payload{
"CustomKeywordField": payload.EncodeString("123456"),
Expand Down
2 changes: 0 additions & 2 deletions config/dynamicconfig/development_es.yaml
Expand Up @@ -44,9 +44,7 @@ frontend.validSearchAttributes:
ExecutionTime: "Int"
CloseTime: "Int"
ExecutionStatus: "Int"
HistoryLength: "Int"
TaskQueue: "Keyword"
Encoding: "Keyword"
CustomStringField: "String"
CustomKeywordField: "Keyword"
CustomIntField: "Int"
Expand Down
8 changes: 1 addition & 7 deletions schema/elasticsearch/v6/visibility/index_template.json
Expand Up @@ -37,16 +37,10 @@
"ExecutionStatus": {
"type": "long"
},

"HistoryLength": {
"type": "long"
},
"Encoding": {
"type": "keyword"
},
"TaskQueue": {
"type": "keyword"
},

"Attr": {
"properties": {
"TemporalChangeVersion": { "type": "keyword" },
Expand Down
8 changes: 1 addition & 7 deletions schema/elasticsearch/v7/visibility/index_template.json
Expand Up @@ -37,16 +37,10 @@
"ExecutionStatus": {
"type": "long"
},

"HistoryLength": {
"type": "long"
},
"Encoding": {
"type": "keyword"
},
"TaskQueue": {
"type": "keyword"
},

"Attr": {
"properties": {
"TemporalChangeVersion": {
Expand Down
2 changes: 1 addition & 1 deletion service/frontend/adminHandler.go
Expand Up @@ -162,7 +162,7 @@ func (adh *AdminHandler) AddSearchAttribute(ctx context.Context, request *admins
currentValidAttr, _ := adh.params.DynamicConfig.GetMapValue(
dynamicconfig.ValidSearchAttributes, nil, searchattribute.GetDefaultTypeMap())
for k, v := range searchAttr {
if searchattribute.IsSystem(k) {
if searchattribute.IsReservedField(k) || searchattribute.IsBuiltIn(k) {
return nil, adh.error(serviceerror.NewInvalidArgument(fmt.Sprintf(errKeyIsReservedBySystemMessage, k)), scope)
}
if _, exist := currentValidAttr[k]; exist {
Expand Down

0 comments on commit 46920a1

Please sign in to comment.