|
7 | 7 | "fmt"
|
8 | 8 | "strings"
|
9 | 9 |
|
10 |
| - "go4.org/bytereplacer" |
| 10 | + "golang.org/x/text/cases" |
| 11 | + "k8s.io/apimachinery/pkg/util/sets" |
11 | 12 |
|
12 | 13 | "github.com/operator-framework/operator-registry/alpha/property"
|
13 | 14 | )
|
@@ -100,25 +101,78 @@ func (m Meta) MarshalJSON() ([]byte, error) {
|
100 | 101 | }
|
101 | 102 |
|
102 | 103 | func (m *Meta) UnmarshalJSON(blob []byte) error {
|
103 |
| - blob = bytereplacer.New(`\u003c`, "<", `\u003e`, ">", `\u0026`, "&").Replace(blob) |
| 104 | + blobMap := map[string]interface{}{} |
| 105 | + if err := json.Unmarshal(blob, &blobMap); err != nil { |
| 106 | + return err |
| 107 | + } |
| 108 | + |
| 109 | + // TODO: this function ensures we do not break backwards compatibility with |
| 110 | + // the documented examples of FBC templates, which use upper camel case |
| 111 | + // for JSON field names. We need to decide if we want to continue supporting |
| 112 | + // case insensitive JSON field names, or if we want to enforce a specific |
| 113 | + // case-sensitive key value for each field. |
| 114 | + if err := extractUniqueMetaKeys(blobMap, m); err != nil { |
| 115 | + return err |
| 116 | + } |
| 117 | + |
| 118 | + buf := bytes.Buffer{} |
| 119 | + enc := json.NewEncoder(&buf) |
| 120 | + enc.SetEscapeHTML(false) |
| 121 | + if err := enc.Encode(blobMap); err != nil { |
| 122 | + return err |
| 123 | + } |
| 124 | + m.Blob = buf.Bytes() |
| 125 | + return nil |
| 126 | +} |
104 | 127 |
|
105 |
| - type tmp struct { |
106 |
| - Schema string `json:"schema"` |
107 |
| - Package string `json:"package,omitempty"` |
108 |
| - Name string `json:"name,omitempty"` |
109 |
| - Properties []property.Property `json:"properties,omitempty"` |
| 128 | +// extractUniqueMetaKeys enables a case-insensitive key lookup for the schema, package, and name |
| 129 | +// fields of the Meta struct. If the blobMap contains duplicate keys (that is, keys have the same folded value), |
| 130 | +// an error is returned. |
| 131 | +func extractUniqueMetaKeys(blobMap map[string]any, m *Meta) error { |
| 132 | + keySets := map[string]sets.Set[string]{} |
| 133 | + folder := cases.Fold() |
| 134 | + for key := range blobMap { |
| 135 | + foldKey := folder.String(key) |
| 136 | + if _, ok := keySets[foldKey]; !ok { |
| 137 | + keySets[foldKey] = sets.New[string]() |
| 138 | + } |
| 139 | + keySets[foldKey].Insert(key) |
110 | 140 | }
|
111 |
| - var t tmp |
112 |
| - if err := json.Unmarshal(blob, &t); err != nil { |
113 |
| - // TODO: return an error that includes the the full JSON message, |
114 |
| - // the offset of the error, and the error message. Let callers |
115 |
| - // decide how to format it. |
116 |
| - return errors.New(resolveUnmarshalErr(blob, err)) |
| 141 | + |
| 142 | + dupErrs := []error{} |
| 143 | + for foldedKey, keys := range keySets { |
| 144 | + if len(keys) != 1 { |
| 145 | + dupErrs = append(dupErrs, fmt.Errorf("duplicate keys for key %q: %v", foldedKey, sets.List(keys))) |
| 146 | + } |
| 147 | + } |
| 148 | + if len(dupErrs) > 0 { |
| 149 | + return errors.Join(dupErrs...) |
| 150 | + } |
| 151 | + |
| 152 | + metaMap := map[string]*string{ |
| 153 | + folder.String("schema"): &m.Schema, |
| 154 | + folder.String("package"): &m.Package, |
| 155 | + folder.String("name"): &m.Name, |
| 156 | + } |
| 157 | + |
| 158 | + for foldedKey, ptr := range metaMap { |
| 159 | + // if the folded key doesn't exist in the key set derived from the blobMap, that means |
| 160 | + // the key doesn't exist in the blobMap, so we can skip it |
| 161 | + if _, ok := keySets[foldedKey]; !ok { |
| 162 | + continue |
| 163 | + } |
| 164 | + |
| 165 | + // reset key to the unfolded key, which we know is the one that appears in the blobMap |
| 166 | + key := keySets[foldedKey].UnsortedList()[0] |
| 167 | + if _, ok := blobMap[key]; !ok { |
| 168 | + continue |
| 169 | + } |
| 170 | + v, ok := blobMap[key].(string) |
| 171 | + if !ok { |
| 172 | + return fmt.Errorf("expected value for key %q to be a string, got %t: %v", key, blobMap[key], blobMap[key]) |
| 173 | + } |
| 174 | + *ptr = v |
117 | 175 | }
|
118 |
| - m.Schema = t.Schema |
119 |
| - m.Package = t.Package |
120 |
| - m.Name = t.Name |
121 |
| - m.Blob = blob |
122 | 176 | return nil
|
123 | 177 | }
|
124 | 178 |
|
|
0 commit comments