github.com/instill-ai/component@v0.16.0-beta/pkg/jsonref/ref.go (about) 1 // The following code is based on lestrrat's work, available at https://github.com/lestrrat-go/jsref. 2 3 package jsonref 4 5 import ( 6 "encoding/json" 7 "net/url" 8 "reflect" 9 10 "github.com/lestrrat-go/jspointer" 11 "github.com/lestrrat-go/pdebug" 12 "github.com/lestrrat-go/structinfo" 13 "github.com/pkg/errors" 14 ) 15 16 const ref = "$ref" 17 18 var refrv = reflect.ValueOf(ref) 19 20 var DefaultMaxRecursions = 10 21 22 // New creates a new Resolver 23 func New() *Resolver { 24 return &Resolver{MaxRecursions: DefaultMaxRecursions} 25 } 26 27 // AddProvider adds a new Provider to be searched for in case 28 // a JSON pointer with more than just the URI fragment is given. 29 func (r *Resolver) AddProvider(p Provider) error { 30 r.providers = append(r.providers, p) 31 return nil 32 } 33 34 type resolveCtx struct { 35 rlevel int // recurse level 36 maxrlevel int // max recurse level 37 object interface{} // the main object that was passed to `Resolve()` 38 recursive bool // should traverseExpandRefRecursive or not 39 seen []string // loop detection 40 } 41 42 // Resolve takes a target `v`, and a JSON pointer `spec`. 43 // spec is expected to be in the form of 44 // 45 // [scheme://[userinfo@]host/path[?query]]#fragment 46 // [scheme:opaque[?query]]#fragment 47 // 48 // where everything except for `#fragment` is optional. 49 // If the fragment is empty, an error is returned. 50 // 51 // If `spec` is the empty string, `v` is returned 52 // This method handles recursive JSON references. 53 // 54 // If `WithRecursiveResolution` option is given and its value is true, 55 // an attempt to resolve all references within the resulting object 56 // is made by traversing the structure recursively. Default is false 57 func (r *Resolver) Resolve(v interface{}, ptr string, options ...Option) (ret interface{}, err error) { 58 if pdebug.Enabled { 59 g := pdebug.Marker("Resolver.Resolve(%s)", ptr).BindError(&err) 60 defer g.End() 61 } 62 var recursiveResolution bool 63 for _, opt := range options { 64 switch opt.Ident() { 65 case identRecursiveResolution{}: 66 recursiveResolution = opt.Value().(bool) 67 } 68 } 69 70 ctx := resolveCtx{ 71 rlevel: 0, 72 maxrlevel: r.MaxRecursions, 73 object: v, 74 recursive: recursiveResolution, 75 seen: []string{}, 76 } 77 78 // First, expand the target as much as we can 79 v, err = expandRefRecursive(&ctx, r, v) 80 if err != nil { 81 return nil, errors.Wrap(err, "recursive search failed") 82 } 83 84 result, err := evalptr(&ctx, r, v, ptr) 85 if err != nil { 86 return nil, err 87 } 88 89 if recursiveResolution { 90 rv, err := traverseExpandRefRecursive(&ctx, r, reflect.ValueOf(result)) 91 if err != nil { 92 return nil, errors.Wrap(err, `failed to resolve result`) 93 } 94 result = rv.Interface() 95 } 96 97 return result, nil 98 } 99 100 func setPtrOrInterface(container, value reflect.Value) bool { 101 switch container.Kind() { 102 case reflect.Ptr: 103 if !value.CanAddr() { 104 return false 105 } 106 container.Set(value.Addr()) 107 case reflect.Interface: 108 container.Set(value) 109 default: 110 return false 111 } 112 return true 113 } 114 115 func traverseExpandRefRecursive(ctx *resolveCtx, r *Resolver, rv reflect.Value) (reflect.Value, error) { 116 if pdebug.Enabled { 117 g := pdebug.Marker("traverseExpandRefRecursive") 118 defer g.End() 119 } 120 121 switch rv.Kind() { 122 case reflect.Ptr, reflect.Interface: 123 rv = rv.Elem() 124 } 125 126 switch rv.Kind() { 127 case reflect.Array, reflect.Slice: 128 for i := 0; i < rv.Len(); i++ { 129 elem := rv.Index(i) 130 var elemcontainer reflect.Value 131 switch elem.Kind() { 132 case reflect.Ptr, reflect.Interface: 133 elemcontainer = elem 134 elem = elem.Elem() 135 } 136 137 // Need to check for elem being Valid, otherwise the 138 // subsequent call to Interface() will fail 139 if !elem.IsValid() { 140 continue 141 } 142 143 if elemcontainer.IsValid() { 144 if !elemcontainer.CanSet() { 145 continue 146 } 147 } 148 newv, err := expandRefRecursive(ctx, r, elem.Interface()) 149 if err != nil { 150 return zeroval, errors.Wrap(err, `failed to expand array/slice element`) 151 } 152 newrv, err := traverseExpandRefRecursive(ctx, r, reflect.ValueOf(newv)) 153 if err != nil { 154 return zeroval, errors.Wrap(err, `failed to recurse into array/slice element`) 155 } 156 157 if elemcontainer.IsValid() { 158 setPtrOrInterface(elemcontainer, newrv) 159 } else { 160 elem.Set(newrv) 161 } 162 } 163 case reflect.Map: 164 // No refs found in the map keys, but there could be more 165 // in the values 166 if _, err := findRef(rv.Interface()); err != nil { 167 for _, key := range rv.MapKeys() { 168 value, err := traverseExpandRefRecursive(ctx, r, rv.MapIndex(key)) 169 if err != nil { 170 return zeroval, errors.Wrap(err, `failed to traverse map value`) 171 } 172 rv.SetMapIndex(key, value) 173 } 174 return rv, nil 175 } 176 newv, err := expandRefRecursive(ctx, r, rv.Interface()) 177 if err != nil { 178 return zeroval, errors.Wrap(err, `failed to expand map element`) 179 } 180 return traverseExpandRefRecursive(ctx, r, reflect.ValueOf(newv)) 181 case reflect.Struct: 182 // No refs found in the map keys, but there could be more 183 // in the values 184 if _, err := findRef(rv.Interface()); err != nil { 185 for i := 0; i < rv.NumField(); i++ { 186 field := rv.Field(i) 187 value, err := traverseExpandRefRecursive(ctx, r, field) 188 if err != nil { 189 return zeroval, errors.Wrap(err, `failed to traverse struct field value`) 190 } 191 field.Set(value) 192 } 193 return rv, nil 194 } 195 newv, err := expandRefRecursive(ctx, r, rv.Interface()) 196 if err != nil { 197 return zeroval, errors.Wrap(err, `failed to expand struct element`) 198 } 199 return traverseExpandRefRecursive(ctx, r, reflect.ValueOf(newv)) 200 } 201 return rv, nil 202 } 203 204 // expands $ref with in v, until all $refs are expanded. 205 // note: DOES NOT recurse down into structures 206 func expandRefRecursive(ctx *resolveCtx, r *Resolver, v interface{}) (ret interface{}, err error) { 207 if pdebug.Enabled { 208 g := pdebug.Marker("expandRefRecursive") 209 defer g.End() 210 } 211 for { 212 ref, err := findRef(v) 213 if err != nil { 214 if pdebug.Enabled { 215 pdebug.Printf("No refs found. bailing out of loop") 216 } 217 break 218 } 219 220 if pdebug.Enabled { 221 pdebug.Printf("Found ref '%s'", ref) 222 } 223 224 newv, err := expandRef(ctx, r, v, ref) 225 if err != nil { 226 if pdebug.Enabled { 227 pdebug.Printf("Failed to expand ref '%s': %s", ref, err) 228 } 229 return nil, errors.Wrap(err, "failed to expand ref") 230 } 231 b, err := json.Marshal(newv) 232 if err != nil { 233 return nil, err 234 } 235 var i interface{} 236 err = json.Unmarshal(b, &i) 237 if err != nil { 238 return nil, err 239 } 240 241 for key, value := range v.(map[string]interface{}) { 242 if key != refrv.String() { 243 i.(map[string]interface{})[key] = value 244 } 245 } 246 v = i 247 } 248 249 return v, nil 250 } 251 252 func expandRef(ctx *resolveCtx, r *Resolver, v interface{}, ref string) (ret interface{}, err error) { 253 if pdebug.Enabled { 254 g := pdebug.Marker("expandRef %s", ref) 255 defer g.End() 256 } 257 ctx.rlevel++ 258 if ctx.rlevel > ctx.maxrlevel { 259 return nil, ErrMaxRecursion 260 } 261 262 defer func() { ctx.rlevel-- }() 263 264 for _, s := range ctx.seen { 265 if s == ref { 266 if pdebug.Enabled { 267 pdebug.Printf("reference loop detected %s", ref) 268 } 269 return nil, ErrReferenceLoop 270 } 271 } 272 273 u, err := url.Parse(ref) 274 if err != nil { 275 return nil, errors.Wrap(err, "failed to parse ref as URL") 276 } 277 278 ptr := "#" + u.Fragment 279 if u.Host == "" && u.Path == "" { 280 if pdebug.Enabled { 281 pdebug.Printf("ptr doesn't contain any host/path part, apply json pointer directly to object") 282 // pdebug.Printf(" %v", ctx.object) 283 } 284 return evalptr(ctx, r, ctx.object, ptr) 285 } 286 287 u.Fragment = "" 288 for _, p := range r.providers { 289 pv, err := p.Get(u) 290 if err == nil { 291 if pdebug.Enabled { 292 pdebug.Printf("Found object matching %s", u) 293 } 294 newseen := append([]string{}, ctx.seen...) 295 newseen = append(newseen, ref) 296 ctx2 := &resolveCtx{ 297 rlevel: ctx.rlevel, 298 maxrlevel: ctx.maxrlevel, 299 object: pv, 300 recursive: ctx.recursive, 301 seen: newseen, 302 } 303 pv, err := evalptr(ctx2, r, pv, ptr) 304 if err != nil { 305 return nil, errors.Wrap(err, "failed on ptr") 306 } 307 if !ctx.recursive { 308 return pv, nil 309 } 310 311 pv, err = expandRefRecursive(ctx2, r, pv) 312 if err != nil { 313 return nil, errors.Wrap(err, "failed to expand external reference") 314 } 315 rv, err := traverseExpandRefRecursive(ctx2, r, reflect.ValueOf(pv)) 316 if err != nil { 317 return nil, errors.Wrap(err, "failed to traverse external reference") 318 } 319 return rv.Interface(), nil 320 } 321 } 322 323 return nil, errors.New("element pointed by $ref '" + ref + "' not found") 324 } 325 326 func findRef(v interface{}) (ref string, err error) { 327 if pdebug.Enabled { 328 g := pdebug.Marker("findRef").BindError(&err) 329 defer g.End() 330 } 331 332 rv := reflect.ValueOf(v) 333 switch rv.Kind() { 334 case reflect.Interface, reflect.Ptr: 335 rv = rv.Elem() 336 } 337 338 if pdebug.Enabled { 339 pdebug.Printf("object is a '%s'", rv.Kind()) 340 } 341 342 // Find if we have a "$ref" element 343 var refv reflect.Value 344 switch rv.Kind() { 345 case reflect.Map: 346 refv = rv.MapIndex(refrv) 347 case reflect.Struct: 348 if fn := structinfo.StructFieldFromJSONName(rv, ref); fn != "" { 349 refv = rv.FieldByName(fn) 350 } 351 default: 352 return "", errors.New("element is not a map-like container") 353 } 354 355 if !refv.IsValid() { 356 return "", errors.New("$ref element not found") 357 } 358 359 switch refv.Kind() { 360 case reflect.Interface, reflect.Ptr: 361 refv = refv.Elem() 362 } 363 364 switch refv.Kind() { 365 case reflect.String: 366 // Empty string isn't a valid pointer 367 if refv.Len() <= 0 { 368 return "", errors.New("$ref element not found (empty)") 369 } 370 if refv.String() == "#" { 371 return "", errors.New("$ref to '#' skipped") 372 } 373 if pdebug.Enabled { 374 pdebug.Printf("Found ref '%s'", refv) 375 } 376 return refv.String(), nil 377 case reflect.Invalid: 378 return "", errors.New("$ref element not found") 379 default: 380 if pdebug.Enabled { 381 pdebug.Printf("'$ref' was found, but its kind is %s", refv.Kind()) 382 } 383 } 384 385 return "", errors.New("$ref element must be a string") 386 } 387 388 func evalptr(ctx *resolveCtx, r *Resolver, v interface{}, ptrspec string) (ret interface{}, err error) { 389 if pdebug.Enabled { 390 g := pdebug.Marker("evalptr(%s)", ptrspec).BindError(&err) 391 defer g.End() 392 } 393 394 // If the reference is empty, return v 395 if ptrspec == "" || ptrspec == "#" { 396 if pdebug.Enabled { 397 pdebug.Printf("Empty pointer, return v itself") 398 } 399 return v, nil 400 } 401 402 // Parse the spec. 403 u, err := url.Parse(ptrspec) 404 if err != nil { 405 return nil, errors.Wrap(err, "failed to parse reference spec") 406 } 407 408 ptr := u.Fragment 409 410 // We are evaluating the pointer part. That means if the 411 // Fragment portion is not set, there's no point in evaluating 412 if ptr == "" { 413 return nil, errors.Wrap(err, "empty json pointer") 414 } 415 416 p, err := jspointer.New(ptr) 417 if err != nil { 418 return nil, errors.Wrap(err, "failed create a new JSON pointer") 419 } 420 x, err := p.Get(v) 421 if err != nil { 422 return nil, errors.Wrap(err, "failed to fetch value") 423 } 424 425 if pdebug.Enabled { 426 pdebug.Printf("Evaulated JSON pointer, now checking if we can expand further") 427 } 428 // If this result contains more refs, expand that 429 return expandRefRecursive(ctx, r, x) 430 }