Skip to content

Commit

Permalink
formally require Coze to error on duplicates; change ErrJSONDuplicate…
Browse files Browse the repository at this point in the history
… from variable into type; key unmarhal errors on duplicate
  • Loading branch information
zamicol committed Jul 10, 2023
1 parent 3752569 commit b4b9338
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 25 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ readability.
4. Provide defined cipher suites.

### Coze Fields
Coze JSON fields are case sensitive and unique. Coze defines standard fields
and applications may include additional fields as desired. All fields are
Coze defines standard fields and applications may include additional fields as
desired. Coze JSON fields are case sensitive and unique. All fields are
optional, but omitting standard fields may limit compatibility. Binary values
are encoded as [RFC 4648 base 64 URI canonical with padding truncated][RFC4648]
(b64ut). The Coze objects `pay`, `key`, and `coze` have respective standard
fields.
fields. Unmarshalling JSON with duplicate fields must error.

#### All Coze Standard Fields
![Coze Standard Fields](docs/img/coze_standard_fields.png)
Expand Down
11 changes: 4 additions & 7 deletions coze.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,15 +396,14 @@ func checkDuplicate(d *json.Decoder) error {
case '{':
keys := make(map[string]bool)
for d.More() {
// Get field key.
t, err := d.Token()
t, err := d.Token() // Get field key.
if err != nil {
return err
}

key := t.(string)
if keys[key] { // Check for duplicates.
return ErrJSONDuplicate
return ErrJSONDuplicate(fmt.Errorf("Coze: JSON duplicate field %q", key))
}
keys[key] = true

Expand Down Expand Up @@ -433,7 +432,5 @@ func checkDuplicate(d *json.Decoder) error {
return nil
}

// ErrJSONDuplicate is for applications that need to check for the JSON
// duplicate error. Alternatively, applications need to check for the error
// string, which may change.
var ErrJSONDuplicate = errors.New("Coze: JSON duplicate field name")
// ErrJSONDuplicate allows applications to check for JSON duplicate error.
type ErrJSONDuplicate error
27 changes: 13 additions & 14 deletions coze_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,11 @@ func ExamplePay_String_custom() {
func ExamplePay_UnmarshalJSON_duplicate() {
h := &Pay{}
msg := []byte(`{"alg":"ES256","alg":"ES384"}`)

err := json.Unmarshal(msg, h)
fmt.Println(err)

// Output:
// Coze: JSON duplicate field name
// Coze: JSON duplicate field "alg"
}

// Example demonstrating that unmarshalling a `coze` that has duplicate field
Expand All @@ -176,7 +175,7 @@ func ExampleCoze_UnmarshalJSON_duplicate() {
fmt.Println(err)

// Output:
// Coze: JSON duplicate field name
// Coze: JSON duplicate field "pay"
}

// ExampleCoze_embed demonstrates how to embed a JSON `coze` into a third party
Expand Down Expand Up @@ -429,25 +428,25 @@ func ExampleMarshal_jsonRawMessage() {
}

func Test_checkDuplicate(t *testing.T) {
// Duplicate, should error.
data := `{"a": "b", "a":true,"c":["field_3 string 1","field3 string2"], "d": {"e": 1, "e": 2}}`
// Happy path; no duplicate. Should not error.
data := `{"a": "b", "c":"d", "d": {"e": 1, "f": 2}}`
err := checkDuplicate(json.NewDecoder(strings.NewReader(data)))
if err != ErrJSONDuplicate {
t.Fatal("Should have found duplicate.")
if err != nil {
t.Fatal(err)
}

// Recursive check with duplicate in inner struct. Should error.
data = `{"a": "b", "c":"d", "d": {"e": 1, "e": 2}}`
// Duplicate, should error.
data = `{"a": "aValue", "a":true,"c":["field_3 string 1","field3 string2"], "d": {"e": 1, "e": 2}}`
err = checkDuplicate(json.NewDecoder(strings.NewReader(data)))
if err != ErrJSONDuplicate {
if err == nil {
t.Fatal("Should have found duplicate.")
}

// No duplicate. Should not error.
data = `{"a": "b", "c":"d", "d": {"e": 1, "f": 2}}`
// Recursive check with duplicate in inner struct. Should error.
data = `{"a": "aValue", "c":"cValue", "d": {"e": 1, "e": 2}}`
err = checkDuplicate(json.NewDecoder(strings.NewReader(data)))
if err != nil {
t.Fatal(err)
if err == nil {
t.Fatal("Recursive check should have found duplicate.")
}
}

Expand Down
7 changes: 6 additions & 1 deletion key.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,14 @@ func Thumbprint(c *Key) (tmb B64, err error) {

// UnmarshalJSON always populates `tmb` even if it isn't given.
func (c *Key) UnmarshalJSON(b []byte) error {
err := checkDuplicate(json.NewDecoder(bytes.NewReader(b)))
if err != nil {
return err
}

type key2 Key // Break infinite unmarshal loop
czk2 := new(key2)
err := json.Unmarshal(b, czk2)
err = json.Unmarshal(b, czk2)
if err != nil {
return err
}
Expand Down
12 changes: 12 additions & 0 deletions key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,18 @@ func ExampleKey_unmarshal() {
// {"alg":"ES256","iat":1623132000,"kid":"Zami's Majuscule Key.","tmb":"cLj8vsYtMBwYkzoFVZHBZo6SNL8wSdCIjCKAwXNuhOk","x":"2nTOaFVm2QLxmUO_SjgyscVHBtvHEfo2rq65MvgNRjORojq39Haq9rXNxvXxwba_Xj0F5vZibJR3isBdOWbo5g"}
}

// Example demonstrating that unmarshalling a `pay` that has duplicate field
// names results in an error.
func ExampleKey_UnmarshalJSON_duplicate() {
k := &Key{}
msg := []byte(`{"alg":"ES256","alg":"ES256"}`)
err := json.Unmarshal(msg, k)
fmt.Println(err)

// Output:
// Coze: JSON duplicate field "alg"
}

func ExampleKey_SignCoze() {
cz := new(Coze)
cz.Pay = json.RawMessage(GoldenPay)
Expand Down

0 comments on commit b4b9338

Please sign in to comment.