1// Copyright (C) MongoDB, Inc. 2014-present.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
7package bsonutil
8
9import (
10	"encoding/base64"
11	"fmt"
12	"time"
13
14	"github.com/mongodb/mongo-tools-common/json"
15	"github.com/mongodb/mongo-tools-common/util"
16	"go.mongodb.org/mongo-driver/bson"
17	"go.mongodb.org/mongo-driver/bson/primitive"
18)
19
20// ConvertLegacyExtJSONValueToBSON walks through a document or an array and
21// replaces any extended JSON value with its corresponding BSON type.
22func ConvertLegacyExtJSONValueToBSON(x interface{}) (interface{}, error) {
23	switch v := x.(type) {
24	case nil:
25		return nil, nil
26	case bool:
27		return v, nil
28	case map[string]interface{}: // document
29		for key, jsonValue := range v {
30			bsonValue, err := ParseLegacyExtJSONValue(jsonValue)
31			if err != nil {
32				return nil, err
33			}
34			v[key] = bsonValue
35		}
36		return v, nil
37	case bson.D:
38		for i := range v {
39			var err error
40			v[i].Value, err = ParseLegacyExtJSONValue(v[i].Value)
41			if err != nil {
42				return nil, err
43			}
44		}
45		return v, nil
46
47	case []interface{}: // array
48		for i, jsonValue := range v {
49			bsonValue, err := ParseLegacyExtJSONValue(jsonValue)
50			if err != nil {
51				return nil, err
52			}
53			v[i] = bsonValue
54		}
55		return v, nil
56
57	case string, float64, int32, int64:
58		return v, nil // require no conversion
59
60	case json.ObjectId: // ObjectId
61		s := string(v)
62		return primitive.ObjectIDFromHex(s)
63
64	case json.Decimal128:
65		return v.Decimal128, nil
66
67	case json.Date: // Date
68		n := int64(v)
69		return time.Unix(n/1e3, n%1e3*1e6), nil
70
71	case json.ISODate: // ISODate
72		n := string(v)
73		return util.FormatDate(n)
74
75	case json.NumberLong: // NumberLong
76		return int64(v), nil
77
78	case json.NumberInt: // NumberInt
79		return int32(v), nil
80
81	case json.NumberFloat: // NumberFloat
82		return float64(v), nil
83	case json.BinData: // BinData
84		data, err := base64.StdEncoding.DecodeString(v.Base64)
85		if err != nil {
86			return nil, err
87		}
88		return primitive.Binary{v.Type, data}, nil
89
90	case json.DBPointer: // DBPointer, for backwards compatibility
91		return primitive.DBPointer{v.Namespace, v.Id}, nil
92
93	case json.RegExp: // RegExp
94		return primitive.Regex{v.Pattern, v.Options}, nil
95
96	case json.Timestamp: // Timestamp
97		return primitive.Timestamp{T: v.Seconds, I: v.Increment}, nil
98
99	case json.JavaScript: // Javascript
100		if v.Scope != nil {
101			return primitive.CodeWithScope{Code: primitive.JavaScript(v.Code), Scope: v.Scope}, nil
102		}
103		return primitive.JavaScript(v.Code), nil
104
105	case json.MinKey: // MinKey
106		return primitive.MinKey{}, nil
107
108	case json.MaxKey: // MaxKey
109		return primitive.MaxKey{}, nil
110
111	case json.Undefined: // undefined
112		return primitive.Undefined{}, nil
113
114	default:
115		return nil, fmt.Errorf("conversion of JSON value '%v' of type '%T' not supported", v, v)
116	}
117}
118
119func convertKeys(v bson.M) (bson.M, error) {
120	for key, value := range v {
121		jsonValue, err := ConvertBSONValueToLegacyExtJSON(value)
122		if err != nil {
123			return nil, err
124		}
125		v[key] = jsonValue
126	}
127	return v, nil
128}
129
130func getConvertedKeys(v bson.M) (bson.M, error) {
131	out := bson.M{}
132	for key, value := range v {
133		jsonValue, err := GetBSONValueAsLegacyExtJSON(value)
134		if err != nil {
135			return nil, err
136		}
137		out[key] = jsonValue
138	}
139	return out, nil
140}
141
142func convertArray(v bson.A) ([]interface{}, error) {
143	for i, value := range v {
144		jsonValue, err := ConvertBSONValueToLegacyExtJSON(value)
145		if err != nil {
146			return nil, err
147		}
148		v[i] = jsonValue
149	}
150	return []interface{}(v), nil
151}
152
153// ConvertBSONValueToLegacyExtJSON walks through a document or an array and
154// converts any BSON value to its corresponding extended JSON type.
155// It returns the converted JSON document and any error encountered.
156func ConvertBSONValueToLegacyExtJSON(x interface{}) (interface{}, error) {
157	switch v := x.(type) {
158	case nil:
159		return nil, nil
160	case bool:
161		return v, nil
162
163	case *bson.M: // document
164		doc, err := convertKeys(*v)
165		if err != nil {
166			return nil, err
167		}
168		return doc, err
169	case bson.M: // document
170		return convertKeys(v)
171	case map[string]interface{}:
172		return convertKeys(v)
173	case bson.D:
174		for i, value := range v {
175			jsonValue, err := ConvertBSONValueToLegacyExtJSON(value.Value)
176			if err != nil {
177				return nil, err
178			}
179			v[i].Value = jsonValue
180		}
181		return MarshalD(v), nil
182	case MarshalD:
183		return v, nil
184	case bson.A: // array
185		return convertArray(v)
186	case []interface{}: // array
187		return convertArray(v)
188	case string:
189		return v, nil // require no conversion
190
191	case int:
192		return json.NumberInt(v), nil
193
194	case primitive.ObjectID: // ObjectId
195		return json.ObjectId(v.Hex()), nil
196
197	case primitive.Decimal128:
198		return json.Decimal128{v}, nil
199
200	case primitive.DateTime: // Date
201		return json.Date(v), nil
202
203	case time.Time: // Date
204		return json.Date(v.Unix()*1000 + int64(v.Nanosecond()/1e6)), nil
205
206	case int64: // NumberLong
207		return json.NumberLong(v), nil
208
209	case int32: // NumberInt
210		return json.NumberInt(v), nil
211
212	case float64:
213		return json.NumberFloat(v), nil
214
215	case float32:
216		return json.NumberFloat(float64(v)), nil
217
218	case []byte: // BinData (with generic type)
219		data := base64.StdEncoding.EncodeToString(v)
220		return json.BinData{0x00, data}, nil
221
222	case primitive.Binary: // BinData
223		data := base64.StdEncoding.EncodeToString(v.Data)
224		return json.BinData{v.Subtype, data}, nil
225
226	case primitive.DBPointer: // DBPointer
227		return json.DBPointer{v.DB, v.Pointer}, nil
228
229	case primitive.Regex: // RegExp
230		return json.RegExp{v.Pattern, v.Options}, nil
231
232	case primitive.Timestamp: // Timestamp
233		return json.Timestamp{
234			Seconds:   v.T,
235			Increment: v.I,
236		}, nil
237
238	case primitive.JavaScript: // JavaScript Code
239		return json.JavaScript{Code: string(v), Scope: nil}, nil
240
241	case primitive.CodeWithScope: // JavaScript Code w/ Scope
242		var scope interface{}
243		var err error
244		if v.Scope != nil {
245			scope, err = ConvertBSONValueToLegacyExtJSON(v.Scope)
246			if err != nil {
247				return nil, err
248			}
249		}
250		return json.JavaScript{string(v.Code), scope}, nil
251
252	case primitive.MaxKey: // MaxKey
253		return json.MaxKey{}, nil
254
255	case primitive.MinKey: // MinKey
256		return json.MinKey{}, nil
257
258	case primitive.Undefined: // undefined
259		return json.Undefined{}, nil
260
261	case primitive.Null: // Null
262		return nil, nil
263	}
264
265	return nil, fmt.Errorf("conversion of BSON value '%v' of type '%T' not supported", x, x)
266}
267
268// GetBSONValueAsLegacyExtJSON is equivalent to ConvertBSONValueToLegacyExtJSON, but does not mutate its argument.
269func GetBSONValueAsLegacyExtJSON(x interface{}) (interface{}, error) {
270	switch v := x.(type) {
271	case nil:
272		return nil, nil
273	case bool:
274		return v, nil
275
276	case *bson.M: // document
277		doc, err := getConvertedKeys(*v)
278		if err != nil {
279			return nil, err
280		}
281		return doc, err
282	case bson.M: // document
283		return getConvertedKeys(v)
284	case map[string]interface{}:
285		return getConvertedKeys(v)
286	case bson.D:
287		out := bson.D{}
288		for _, value := range v {
289			jsonValue, err := GetBSONValueAsLegacyExtJSON(value.Value)
290			if err != nil {
291				return nil, err
292			}
293			out = append(out, bson.E{
294				Key:  value.Key,
295				Value: jsonValue,
296			})
297		}
298		return MarshalD(out), nil
299	case MarshalD:
300		out, err := GetBSONValueAsLegacyExtJSON(bson.D(v))
301		if err != nil {
302			return nil, err
303		}
304		return MarshalD(out.(bson.D)), nil
305	case []interface{}: // array
306		out := []interface{}{}
307		for _, value := range v {
308			jsonValue, err := GetBSONValueAsLegacyExtJSON(value)
309			if err != nil {
310				return nil, err
311			}
312			out = append(out, jsonValue)
313		}
314		return out, nil
315
316	case string:
317		return v, nil // require no conversion
318
319	case int:
320		return json.NumberInt(v), nil
321
322	case primitive.ObjectID: // ObjectId
323		return json.ObjectId(v.Hex()), nil
324
325	case primitive.Decimal128:
326		return json.Decimal128{v}, nil
327
328	case primitive.DateTime: // Date
329		return json.Date(v), nil
330
331	case time.Time: // Date
332		return json.Date(v.Unix()*1000 + int64(v.Nanosecond()/1e6)), nil
333
334	case int64: // NumberLong
335		return json.NumberLong(v), nil
336
337	case int32: // NumberInt
338		return json.NumberInt(v), nil
339
340	case float64:
341		return json.NumberFloat(v), nil
342
343	case float32:
344		return json.NumberFloat(float64(v)), nil
345
346	case []byte: // BinData (with generic type)
347		data := base64.StdEncoding.EncodeToString(v)
348		return json.BinData{0x00, data}, nil
349
350	case primitive.Binary: // BinData
351		data := base64.StdEncoding.EncodeToString(v.Data)
352		return json.BinData{Type: v.Subtype, Base64: data}, nil
353
354	case primitive.DBPointer: // DBPointer
355		return json.DBPointer{v.DB, v.Pointer}, nil
356
357	case primitive.Regex: // RegExp
358		return json.RegExp{v.Pattern, v.Options}, nil
359
360	case primitive.Timestamp: // Timestamp
361		return json.Timestamp{
362			Seconds:   v.T,
363			Increment: v.I,
364		}, nil
365
366	case primitive.JavaScript: // JavaScript Code
367		return json.JavaScript{Code: string(v), Scope: nil}, nil
368
369	case primitive.CodeWithScope: // JavaScript Code w/ Scope
370		var scope interface{}
371		var err error
372		if v.Scope != nil {
373			scope, err = GetBSONValueAsLegacyExtJSON(v.Scope)
374			if err != nil {
375				return nil, err
376			}
377		}
378		return json.JavaScript{string(v.Code), scope}, nil
379
380	case primitive.MaxKey: // MaxKey
381		return json.MaxKey{}, nil
382
383	case primitive.MinKey: // MinKey
384		return json.MinKey{}, nil
385
386	case primitive.Undefined: // undefined
387		return json.Undefined{}, nil
388
389	case primitive.Null: // Null
390		return nil, nil
391	}
392
393	return nil, fmt.Errorf("conversion of BSON value '%v' of type '%T' not supported", x, x)
394}
395