Exemplo n.º 1
0
// ParseSpecialKeys takes a JSON document and inspects it for any extended JSON
// type (e.g $numberLong) and replaces any such values with the corresponding
// BSON type.
func ParseSpecialKeys(special interface{}) (interface{}, error) {
	// first ensure we are using a correct document type
	var doc map[string]interface{}
	switch v := special.(type) {
	case bson.D:
		doc = v.Map()
	case map[string]interface{}:
		doc = v
	default:
		return nil, fmt.Errorf("%v (type %T) is not valid input to ParseSpecialKeys", special, special)
	}
	// check document to see if it is special
	switch len(doc) {
	case 1: // document has a single field
		if jsonValue, ok := doc["$date"]; ok {
			switch v := jsonValue.(type) {
			case string:
				return util.FormatDate(v)
			case bson.D:
				asMap := v.Map()
				if jsonValue, ok := asMap["$numberLong"]; ok {
					n, err := parseNumberLongField(jsonValue)
					if err != nil {
						return nil, err
					}
					return time.Unix(n/1e3, n%1e3*1e6), err
				}
				return nil, errors.New("expected $numberLong field in $date")
			case map[string]interface{}:
				if jsonValue, ok := v["$numberLong"]; ok {
					n, err := parseNumberLongField(jsonValue)
					if err != nil {
						return nil, err
					}
					return time.Unix(n/1e3, n%1e3*1e6), err
				}
				return nil, errors.New("expected $numberLong field in $date")

			case json.Number:
				n, err := v.Int64()
				return time.Unix(n/1e3, n%1e3*1e6), err
			case float64:
				n := int64(v)
				return time.Unix(n/1e3, n%1e3*1e6), nil
			case int32:
				n := int64(v)
				return time.Unix(n/1e3, n%1e3*1e6), nil
			case int64:
				return time.Unix(v/1e3, v%1e3*1e6), nil

			case json.ISODate:
				return v, nil

			default:
				return nil, errors.New("invalid type for $date field")
			}
		}

		if jsonValue, ok := doc["$code"]; ok {
			switch v := jsonValue.(type) {
			case string:
				return bson.JavaScript{Code: v}, nil
			default:
				return nil, errors.New("expected $code field to have string value")
			}
		}

		if jsonValue, ok := doc["$oid"]; ok {
			switch v := jsonValue.(type) {
			case string:
				if !bson.IsObjectIdHex(v) {
					return nil, errors.New("expected $oid field to contain 24 hexadecimal character")
				}
				return bson.ObjectIdHex(v), nil

			default:
				return nil, errors.New("expected $oid field to have string value")
			}
		}

		if jsonValue, ok := doc["$numberLong"]; ok {
			return parseNumberLongField(jsonValue)
		}

		if jsonValue, ok := doc["$numberInt"]; ok {
			switch v := jsonValue.(type) {
			case string:
				// all of decimal, hex, and octal are supported here
				n, err := strconv.ParseInt(v, 0, 32)
				return int32(n), err

			default:
				return nil, errors.New("expected $numberInt field to have string value")
			}
		}

		if jsonValue, ok := doc["$timestamp"]; ok {
			ts := json.Timestamp{}

			var tsDoc map[string]interface{}
			switch internalDoc := jsonValue.(type) {
			case map[string]interface{}:
				tsDoc = internalDoc
			case bson.D:
				tsDoc = internalDoc.Map()
			default:
				return nil, errors.New("expected $timestamp key to have internal document")
			}

			if seconds, ok := tsDoc["t"]; ok {
				if asUint32, err := util.ToUInt32(seconds); err == nil {
					ts.Seconds = asUint32
				} else {
					return nil, errors.New("expected $timestamp 't' field to be a numeric type")
				}
			} else {
				return nil, errors.New("expected $timestamp to have 't' field")
			}
			if inc, ok := tsDoc["i"]; ok {
				if asUint32, err := util.ToUInt32(inc); err == nil {
					ts.Increment = asUint32
				} else {
					return nil, errors.New("expected $timestamp 'i' field to be  a numeric type")
				}
			} else {
				return nil, errors.New("expected $timestamp to have 'i' field")
			}
			// see BSON spec for details on the bit fiddling here
			return bson.MongoTimestamp(int64(ts.Seconds)<<32 | int64(ts.Increment)), nil
		}

		if jsonValue, ok := doc["$numberDecimal"]; ok {
			switch v := jsonValue.(type) {
			case string:
				return bson.ParseDecimal128(v)
			default:
				return nil, errors.New("expected $numberDecimal field to have string value")
			}
		}

		if _, ok := doc["$undefined"]; ok {
			return bson.Undefined, nil
		}

		if _, ok := doc["$maxKey"]; ok {
			return bson.MaxKey, nil
		}

		if _, ok := doc["$minKey"]; ok {
			return bson.MinKey, nil
		}

	case 2: // document has two fields
		if jsonValue, ok := doc["$code"]; ok {
			code := bson.JavaScript{}
			switch v := jsonValue.(type) {
			case string:
				code.Code = v
			default:
				return nil, errors.New("expected $code field to have string value")
			}

			if jsonValue, ok = doc["$scope"]; ok {
				switch v2 := jsonValue.(type) {
				case map[string]interface{}, bson.D:
					x, err := ParseSpecialKeys(v2)
					if err != nil {
						return nil, err
					}
					code.Scope = x
					return code, nil
				default:
					return nil, errors.New("expected $scope field to contain map")
				}
			} else {
				return nil, errors.New("expected $scope field with $code field")
			}
		}

		if jsonValue, ok := doc["$regex"]; ok {
			regex := bson.RegEx{}

			switch pattern := jsonValue.(type) {
			case string:
				regex.Pattern = pattern

			default:
				return nil, errors.New("expected $regex field to have string value")
			}
			if jsonValue, ok = doc["$options"]; !ok {
				return nil, errors.New("expected $options field with $regex field")
			}

			switch options := jsonValue.(type) {
			case string:
				regex.Options = options

			default:
				return nil, errors.New("expected $options field to have string value")
			}

			// Validate regular expression options
			for i := range regex.Options {
				switch o := regex.Options[i]; o {
				default:
					return nil, fmt.Errorf("invalid regular expression option '%v'", o)

				case 'g', 'i', 'm', 's': // allowed
				}
			}
			return regex, nil
		}

		if jsonValue, ok := doc["$binary"]; ok {
			binary := bson.Binary{}

			switch data := jsonValue.(type) {
			case string:
				bytes, err := base64.StdEncoding.DecodeString(data)
				if err != nil {
					return nil, err
				}
				binary.Data = bytes

			default:
				return nil, errors.New("expected $binary field to have string value")
			}
			if jsonValue, ok = doc["$type"]; !ok {
				return nil, errors.New("expected $type field with $binary field")
			}

			switch typ := jsonValue.(type) {
			case string:
				kind, err := hex.DecodeString(typ)
				if err != nil {
					return nil, err
				} else if len(kind) != 1 {
					return nil, errors.New("expected single byte (as hexadecimal string) for $type field")
				}
				binary.Kind = kind[0]

			default:
				return nil, errors.New("expected $type field to have string value")
			}
			return binary, nil
		}

		if jsonValue, ok := doc["$ref"]; ok {
			dbRef := mgo.DBRef{}

			switch data := jsonValue.(type) {
			case string:
				dbRef.Collection = data
			default:
				return nil, errors.New("expected string for $ref field")
			}
			if jsonValue, ok = doc["$id"]; ok {
				switch v2 := jsonValue.(type) {
				case map[string]interface{}, bson.D:
					x, err := ParseSpecialKeys(v2)
					if err != nil {
						return nil, fmt.Errorf("error parsing $id field: %v", err)
					}
					dbRef.Id = x
				default:
					dbRef.Id = v2
				}
				return dbRef, nil
			}
		}
	case 3:
		if jsonValue, ok := doc["$ref"]; ok {
			dbRef := mgo.DBRef{}

			switch data := jsonValue.(type) {
			case string:
				dbRef.Collection = data
			default:
				return nil, errors.New("expected string for $ref field")
			}
			if jsonValue, ok = doc["$id"]; ok {
				switch v2 := jsonValue.(type) {
				case map[string]interface{}, bson.D:
					x, err := ParseSpecialKeys(v2)
					if err != nil {
						return nil, fmt.Errorf("error parsing $id field: %v", err)
					}
					dbRef.Id = x
				default:
					dbRef.Id = v2
				}
				if dbValue, ok := doc["$db"]; ok {
					switch v3 := dbValue.(type) {
					case string:
						dbRef.Database = v3
					default:
						return nil, errors.New("expected string for $db field")
					}
					return dbRef, nil
				}
			}
		}
	}

	// nothing matched, so we recurse deeper
	switch v := special.(type) {
	case bson.D:
		return GetExtendedBsonD(v)
	case map[string]interface{}:
		return ConvertJSONValueToBSON(v)
	default:
		return nil, fmt.Errorf("%v (type %T) is not valid input to ParseSpecialKeys", special, special)
	}
}
Exemplo n.º 2
0
func TestFieldParsers(t *testing.T) {
	testutil.VerifyTestType(t, testutil.UnitTestType)

	Convey("Using FieldAutoParser", t, func() {
		var p, _ = NewFieldParser(ctAuto, "")
		var value interface{}
		var err error

		Convey("parses integers when it can", func() {
			value, err = p.Parse("2147483648")
			So(value.(int64), ShouldEqual, int64(2147483648))
			So(err, ShouldBeNil)
			value, err = p.Parse("42")
			So(value.(int32), ShouldEqual, 42)
			So(err, ShouldBeNil)
			value, err = p.Parse("-2147483649")
			So(value.(int64), ShouldEqual, int64(-2147483649))
		})
		Convey("parses decimals when it can", func() {
			value, err = p.Parse("3.14159265")
			So(value.(float64), ShouldEqual, 3.14159265)
			So(err, ShouldBeNil)
			value, err = p.Parse("0.123123")
			So(value.(float64), ShouldEqual, 0.123123)
			So(err, ShouldBeNil)
			value, err = p.Parse("-123456.789")
			So(value.(float64), ShouldEqual, -123456.789)
			So(err, ShouldBeNil)
			value, err = p.Parse("-1.")
			So(value.(float64), ShouldEqual, -1.0)
			So(err, ShouldBeNil)
		})
		Convey("leaves everything else as a string", func() {
			value, err = p.Parse("12345-6789")
			So(value.(string), ShouldEqual, "12345-6789")
			So(err, ShouldBeNil)
			value, err = p.Parse("06/02/1997")
			So(value.(string), ShouldEqual, "06/02/1997")
			So(err, ShouldBeNil)
			value, err = p.Parse("")
			So(value.(string), ShouldEqual, "")
			So(err, ShouldBeNil)
		})
	})

	Convey("Using FieldBooleanParser", t, func() {
		var p, _ = NewFieldParser(ctBoolean, "")
		var value interface{}
		var err error

		Convey("parses representations of true correctly", func() {
			value, err = p.Parse("true")
			So(value.(bool), ShouldBeTrue)
			So(err, ShouldBeNil)
			value, err = p.Parse("TrUe")
			So(value.(bool), ShouldBeTrue)
			So(err, ShouldBeNil)
			value, err = p.Parse("1")
			So(value.(bool), ShouldBeTrue)
			So(err, ShouldBeNil)
		})
		Convey("parses representations of false correctly", func() {
			value, err = p.Parse("false")
			So(value.(bool), ShouldBeFalse)
			So(err, ShouldBeNil)
			value, err = p.Parse("FaLsE")
			So(value.(bool), ShouldBeFalse)
			So(err, ShouldBeNil)
			value, err = p.Parse("0")
			So(value.(bool), ShouldBeFalse)
			So(err, ShouldBeNil)
		})
		Convey("does not parse other boolean representations", func() {
			_, err = p.Parse("")
			So(err, ShouldNotBeNil)
			_, err = p.Parse("t")
			So(err, ShouldNotBeNil)
			_, err = p.Parse("f")
			So(err, ShouldNotBeNil)
			_, err = p.Parse("yes")
			So(err, ShouldNotBeNil)
			_, err = p.Parse("no")
			So(err, ShouldNotBeNil)
		})
	})

	Convey("Using FieldBinaryParser", t, func() {
		var value interface{}
		var err error

		Convey("using hex encoding", func() {
			var p, _ = NewFieldParser(ctBinary, "hex")
			Convey("parses valid hex values correctly", func() {
				value, err = p.Parse("400a11")
				So(value.([]byte), ShouldResemble, []byte{64, 10, 17})
				So(err, ShouldBeNil)
				value, err = p.Parse("400A11")
				So(value.([]byte), ShouldResemble, []byte{64, 10, 17})
				So(err, ShouldBeNil)
				value, err = p.Parse("0b400A11")
				So(value.([]byte), ShouldResemble, []byte{11, 64, 10, 17})
				So(err, ShouldBeNil)
				value, err = p.Parse("")
				So(value.([]byte), ShouldResemble, []byte{})
				So(err, ShouldBeNil)
			})
		})
		Convey("using base32 encoding", func() {
			var p, _ = NewFieldParser(ctBinary, "base32")
			Convey("parses valid base32 values correctly", func() {
				value, err = p.Parse("")
				So(value.([]uint8), ShouldResemble, []uint8{})
				So(err, ShouldBeNil)
				value, err = p.Parse("MZXW6YTBOI======")
				So(value.([]uint8), ShouldResemble, []uint8{102, 111, 111, 98, 97, 114})
				So(err, ShouldBeNil)
			})
		})
		Convey("using base64 encoding", func() {
			var p, _ = NewFieldParser(ctBinary, "base64")
			Convey("parses valid base64 values correctly", func() {
				value, err = p.Parse("")
				So(value.([]uint8), ShouldResemble, []uint8{})
				So(err, ShouldBeNil)
				value, err = p.Parse("Zm9vYmFy")
				So(value.([]uint8), ShouldResemble, []uint8{102, 111, 111, 98, 97, 114})
				So(err, ShouldBeNil)
			})
		})
	})

	Convey("Using FieldDateParser", t, func() {
		var value interface{}
		var err error

		Convey("with Go's format", func() {
			var p, _ = NewFieldParser(ctDateGo, "01/02/2006 3:04:05pm MST")
			Convey("parses valid timestamps correctly", func() {
				value, err = p.Parse("01/04/2000 5:38:10pm UTC")
				So(value.(time.Time), ShouldResemble, time.Date(2000, 1, 4, 17, 38, 10, 0, time.UTC))
				So(err, ShouldBeNil)
			})
			Convey("does not parse invalid dates", func() {
				_, err = p.Parse("01/04/2000 5:38:10pm")
				So(err, ShouldNotBeNil)
				_, err = p.Parse("01/04/2000 5:38:10 pm UTC")
				So(err, ShouldNotBeNil)
				_, err = p.Parse("01/04/2000")
				So(err, ShouldNotBeNil)
			})
		})
		Convey("with MS's format", func() {
			var p, _ = NewFieldParser(ctDateMS, "MM/dd/yyyy h:mm:sstt")
			Convey("parses valid timestamps correctly", func() {
				value, err = p.Parse("01/04/2000 5:38:10PM")
				So(value.(time.Time), ShouldResemble, time.Date(2000, 1, 4, 17, 38, 10, 0, time.UTC))
				So(err, ShouldBeNil)
			})
			Convey("does not parse invalid dates", func() {
				_, err = p.Parse("01/04/2000 :) 05:38:10PM")
				So(err, ShouldNotBeNil)
				_, err = p.Parse("01/04/2000 005:38:10PM")
				So(err, ShouldNotBeNil)
				_, err = p.Parse("01/04/2000 5:38:10 PM")
				So(err, ShouldNotBeNil)
				_, err = p.Parse("01/04/2000")
				So(err, ShouldNotBeNil)
			})
		})
		Convey("with Oracle's format", func() {
			var p, _ = NewFieldParser(ctDateOracle, "mm/Dd/yYYy hh:MI:SsAm")
			Convey("parses valid timestamps correctly", func() {
				value, err = p.Parse("01/04/2000 05:38:10PM")
				So(value.(time.Time), ShouldResemble, time.Date(2000, 1, 4, 17, 38, 10, 0, time.UTC))
				So(err, ShouldBeNil)
			})
			Convey("does not parse invalid dates", func() {
				_, err = p.Parse("01/04/2000 :) 05:38:10PM")
				So(err, ShouldNotBeNil)
				_, err = p.Parse("01/04/2000 005:38:10PM")
				So(err, ShouldNotBeNil)
				_, err = p.Parse("01/04/2000 5:38:10 PM")
				So(err, ShouldNotBeNil)
				_, err = p.Parse("01/04/2000")
				So(err, ShouldNotBeNil)
			})
		})
	})

	Convey("Using FieldDoubleParser", t, func() {
		var p, _ = NewFieldParser(ctDouble, "")
		var value interface{}
		var err error

		Convey("parses valid decimal values correctly", func() {
			value, err = p.Parse("3.14159265")
			So(value.(float64), ShouldEqual, 3.14159265)
			So(err, ShouldBeNil)
			value, err = p.Parse("0.123123")
			So(value.(float64), ShouldEqual, 0.123123)
			So(err, ShouldBeNil)
			value, err = p.Parse("-123456.789")
			So(value.(float64), ShouldEqual, -123456.789)
			So(err, ShouldBeNil)
			value, err = p.Parse("-1.")
			So(value.(float64), ShouldEqual, -1.0)
			So(err, ShouldBeNil)
		})
		Convey("does not parse invalid numbers", func() {
			_, err = p.Parse("")
			So(err, ShouldNotBeNil)
			_, err = p.Parse("1.1.1")
			So(err, ShouldNotBeNil)
			_, err = p.Parse("1-2.0")
			So(err, ShouldNotBeNil)
			_, err = p.Parse("80-")
			So(err, ShouldNotBeNil)
		})
	})

	Convey("Using FieldInt32Parser", t, func() {
		var p, _ = NewFieldParser(ctInt32, "")
		var value interface{}
		var err error

		Convey("parses valid integer values correctly", func() {
			value, err = p.Parse("2147483647")
			So(value.(int32), ShouldEqual, 2147483647)
			So(err, ShouldBeNil)
			value, err = p.Parse("42")
			So(value.(int32), ShouldEqual, 42)
			So(err, ShouldBeNil)
			value, err = p.Parse("-2147483648")
			So(value.(int32), ShouldEqual, -2147483648)
		})
		Convey("does not parse invalid numbers", func() {
			_, err = p.Parse("")
			So(err, ShouldNotBeNil)
			_, err = p.Parse("42.0")
			So(err, ShouldNotBeNil)
			_, err = p.Parse("1-2")
			So(err, ShouldNotBeNil)
			_, err = p.Parse("80-")
			So(err, ShouldNotBeNil)
			value, err = p.Parse("2147483648")
			So(err, ShouldNotBeNil)
			value, err = p.Parse("-2147483649")
			So(err, ShouldNotBeNil)
		})
	})

	Convey("Using FieldInt64Parser", t, func() {
		var p, _ = NewFieldParser(ctInt64, "")
		var value interface{}
		var err error

		Convey("parses valid integer values correctly", func() {
			value, err = p.Parse("2147483648")
			So(value.(int64), ShouldEqual, int64(2147483648))
			So(err, ShouldBeNil)
			value, err = p.Parse("42")
			So(value.(int64), ShouldEqual, 42)
			So(err, ShouldBeNil)
			value, err = p.Parse("-2147483649")
			So(value.(int64), ShouldEqual, int64(-2147483649))
		})
		Convey("does not parse invalid numbers", func() {
			_, err = p.Parse("")
			So(err, ShouldNotBeNil)
			_, err = p.Parse("42.0")
			So(err, ShouldNotBeNil)
			_, err = p.Parse("1-2")
			So(err, ShouldNotBeNil)
			_, err = p.Parse("80-")
			So(err, ShouldNotBeNil)
		})
	})

	Convey("Using FieldDecimalParser", t, func() {
		var p, _ = NewFieldParser(ctDecimal, "")
		var err error

		Convey("parses valid decimal values correctly", func() {
			for _, ts := range []string{"12235.2355", "42", "0", "-124", "-124.55"} {
				testVal, err := bson.ParseDecimal128(ts)
				So(err, ShouldBeNil)
				parsedValue, err := p.Parse(ts)
				So(err, ShouldBeNil)

				So(testVal, ShouldResemble, parsedValue.(bson.Decimal128))
			}
		})
		Convey("does not parse invalid decimal values", func() {
			for _, ts := range []string{"", "1-2", "abcd"} {
				_, err = p.Parse(ts)
				So(err, ShouldNotBeNil)
			}
		})
	})

	Convey("Using FieldStringParser", t, func() {
		var p, _ = NewFieldParser(ctString, "")
		var value interface{}
		var err error

		Convey("parses strings as strings only", func() {
			value, err = p.Parse("42")
			So(value.(string), ShouldEqual, "42")
			So(err, ShouldBeNil)
			value, err = p.Parse("true")
			So(value.(string), ShouldEqual, "true")
			So(err, ShouldBeNil)
			value, err = p.Parse("")
			So(value.(string), ShouldEqual, "")
			So(err, ShouldBeNil)
		})
	})

}
Exemplo n.º 3
0
func (ip *FieldDecimalParser) Parse(in string) (interface{}, error) {
	return bson.ParseDecimal128(in)
}