mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
## What changed? <!-- Describe what has changed in this PR --> Unalias and validate search attributes when import workflow history. ## Why? <!-- Tell your future self why have you made these changes --> Search attributes in workflow histostory JSON file might use aliases (if history was exported using `temporal` cli) or be invalid (name/type/length). Previously import blindly accepted any search attributes and then background visibility task processing could fail. Now search attributes are validated and unaliased (if needed) before getting to the history service. ## How did you test it? <!-- How have you verified this change? Tested locally? Added a unit test? Checked in staging env? --> Added new unit tests for both valid and invalid cases. ## Potential risks <!-- Assuming the worst case, what can be broken when deploying this change to production? --> No risks. ## Documentation <!-- Have you made sure this change doesn't falsify anything currently stated in `docs/`? If significant new behavior is added, have you described that in `docs/`? --> No.
159 lines
4.3 KiB
Go
159 lines
4.3 KiB
Go
// The MIT License
|
|
//
|
|
// Copyright (c) 2020 Temporal Technologies Inc. All rights reserved.
|
|
//
|
|
// Copyright (c) 2020 Uber Technologies, Inc.
|
|
//
|
|
// 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.
|
|
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
"reflect"
|
|
"regexp"
|
|
"strings"
|
|
"text/template"
|
|
|
|
historypb "go.temporal.io/api/history/v1"
|
|
)
|
|
|
|
type (
|
|
eventData struct {
|
|
AttributesTypeName string
|
|
}
|
|
|
|
searchAttributesHelpersData struct {
|
|
Events []eventData
|
|
}
|
|
)
|
|
|
|
var (
|
|
// Is used to find attribute getters and extract the event type (match[1]).
|
|
attributesGetterRegex = regexp.MustCompile("^Get(.+EventAttributes)$")
|
|
)
|
|
|
|
func main() {
|
|
outPathFlag := flag.String("out", ".", "path to write generated files")
|
|
licenseFlag := flag.String("copyright_file", "LICENSE", "path to license to copy into header")
|
|
flag.Parse()
|
|
|
|
licenseText := readLicenseFile(*licenseFlag)
|
|
|
|
eventHelpersFile := path.Join(*outPathFlag, "event_gen.go")
|
|
callWithFile(generateSearchAttributesEventHelpers, eventHelpersFile, licenseText)
|
|
}
|
|
|
|
func generateSearchAttributesEventHelpers(w io.Writer) {
|
|
|
|
writeSearchAttributesEventHelpers(w, `
|
|
package searchattribute
|
|
|
|
import (
|
|
commonpb "go.temporal.io/api/common/v1"
|
|
historypb "go.temporal.io/api/history/v1"
|
|
)
|
|
|
|
func SetToEvent(event *historypb.HistoryEvent, sas *commonpb.SearchAttributes) bool {
|
|
switch e := event.GetAttributes().(type) {
|
|
{{- range .Events}}
|
|
case *historypb.HistoryEvent_{{.AttributesTypeName}}:
|
|
e.{{.AttributesTypeName}}.SearchAttributes = sas
|
|
return true
|
|
{{- end}}
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func GetFromEvent(event *historypb.HistoryEvent) (*commonpb.SearchAttributes, bool) {
|
|
switch e := event.GetAttributes().(type) {
|
|
{{- range .Events}}
|
|
case *historypb.HistoryEvent_{{.AttributesTypeName}}:
|
|
return e.{{.AttributesTypeName}}.GetSearchAttributes(), true
|
|
{{- end}}
|
|
default:
|
|
return nil, false
|
|
}
|
|
}
|
|
`)
|
|
}
|
|
|
|
func writeSearchAttributesEventHelpers(w io.Writer, tmpl string) {
|
|
sahd := searchAttributesHelpersData{}
|
|
|
|
historyEventT := reflect.TypeOf((*historypb.HistoryEvent)(nil))
|
|
|
|
for i := 0; i < historyEventT.NumMethod(); i++ {
|
|
attributesGetter := historyEventT.Method(i)
|
|
matches := attributesGetterRegex.FindStringSubmatch(attributesGetter.Name)
|
|
if len(matches) < 2 {
|
|
continue
|
|
}
|
|
if attributesGetter.Type.NumOut() != 1 {
|
|
continue
|
|
}
|
|
if _, found := attributesGetter.Type.Out(0).MethodByName("GetSearchAttributes"); !found {
|
|
continue
|
|
}
|
|
|
|
ed := eventData{
|
|
AttributesTypeName: matches[1],
|
|
}
|
|
sahd.Events = append(sahd.Events, ed)
|
|
}
|
|
|
|
fatalIfErr(template.Must(template.New("code").Parse(tmpl)).Execute(w, sahd))
|
|
}
|
|
|
|
func callWithFile(generator func(io.Writer), outFile string, licenseText string) {
|
|
w, err := os.Create(outFile)
|
|
fatalIfErr(err)
|
|
defer func() {
|
|
fatalIfErr(w.Close())
|
|
}()
|
|
_, err = fmt.Fprintf(w, "%s\n// Code generated by cmd/tools/gensearchattributehelpers. DO NOT EDIT.\n", licenseText)
|
|
fatalIfErr(err)
|
|
generator(w)
|
|
}
|
|
|
|
func readLicenseFile(filePath string) string {
|
|
text, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
var lines []string
|
|
for _, line := range strings.Split(string(text), "\n") {
|
|
lines = append(lines, strings.TrimRight("// "+line, " "))
|
|
}
|
|
return strings.Join(lines, "\n") + "\n"
|
|
}
|
|
|
|
func fatalIfErr(err error) {
|
|
if err != nil {
|
|
//nolint:revive // calls to log.Fatal only in main() or init() functions (revive)
|
|
log.Fatal(err)
|
|
}
|
|
}
|