1// Copyright 2015 go-swagger maintainers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package spec
16
17import (
18	"encoding/json"
19	"net/http"
20	"os"
21	"path/filepath"
22
23	"github.com/go-openapi/jsonreference"
24)
25
26// Refable is a struct for things that accept a $ref property
27type Refable struct {
28	Ref Ref
29}
30
31// MarshalJSON marshals the ref to json
32func (r Refable) MarshalJSON() ([]byte, error) {
33	return r.Ref.MarshalJSON()
34}
35
36// UnmarshalJSON unmarshalss the ref from json
37func (r *Refable) UnmarshalJSON(d []byte) error {
38	return json.Unmarshal(d, &r.Ref)
39}
40
41// Ref represents a json reference that is potentially resolved
42type Ref struct {
43	jsonreference.Ref
44}
45
46// RemoteURI gets the remote uri part of the ref
47func (r *Ref) RemoteURI() string {
48	if r.String() == "" {
49		return r.String()
50	}
51
52	u := *r.GetURL()
53	u.Fragment = ""
54	return u.String()
55}
56
57// IsValidURI returns true when the url the ref points to can be found
58func (r *Ref) IsValidURI(basepaths ...string) bool {
59	if r.String() == "" {
60		return true
61	}
62
63	v := r.RemoteURI()
64	if v == "" {
65		return true
66	}
67
68	if r.HasFullURL {
69		rr, err := http.Get(v)
70		if err != nil {
71			return false
72		}
73
74		return rr.StatusCode/100 == 2
75	}
76
77	if !(r.HasFileScheme || r.HasFullFilePath || r.HasURLPathOnly) {
78		return false
79	}
80
81	// check for local file
82	pth := v
83	if r.HasURLPathOnly {
84		base := "."
85		if len(basepaths) > 0 {
86			base = filepath.Dir(filepath.Join(basepaths...))
87		}
88		p, e := filepath.Abs(filepath.ToSlash(filepath.Join(base, pth)))
89		if e != nil {
90			return false
91		}
92		pth = p
93	}
94
95	fi, err := os.Stat(filepath.ToSlash(pth))
96	if err != nil {
97		return false
98	}
99
100	return !fi.IsDir()
101}
102
103// Inherits creates a new reference from a parent and a child
104// If the child cannot inherit from the parent, an error is returned
105func (r *Ref) Inherits(child Ref) (*Ref, error) {
106	ref, err := r.Ref.Inherits(child.Ref)
107	if err != nil {
108		return nil, err
109	}
110	return &Ref{Ref: *ref}, nil
111}
112
113// NewRef creates a new instance of a ref object
114// returns an error when the reference uri is an invalid uri
115func NewRef(refURI string) (Ref, error) {
116	ref, err := jsonreference.New(refURI)
117	if err != nil {
118		return Ref{}, err
119	}
120	return Ref{Ref: ref}, nil
121}
122
123// MustCreateRef creates a ref object but panics when refURI is invalid.
124// Use the NewRef method for a version that returns an error.
125func MustCreateRef(refURI string) Ref {
126	return Ref{Ref: jsonreference.MustCreateRef(refURI)}
127}
128
129// MarshalJSON marshals this ref into a JSON object
130func (r Ref) MarshalJSON() ([]byte, error) {
131	str := r.String()
132	if str == "" {
133		if r.IsRoot() {
134			return []byte(`{"$ref":""}`), nil
135		}
136		return []byte("{}"), nil
137	}
138	v := map[string]interface{}{"$ref": str}
139	return json.Marshal(v)
140}
141
142// UnmarshalJSON unmarshals this ref from a JSON object
143func (r *Ref) UnmarshalJSON(d []byte) error {
144	var v map[string]interface{}
145	if err := json.Unmarshal(d, &v); err != nil {
146		return err
147	}
148	return r.fromMap(v)
149}
150
151func (r *Ref) fromMap(v map[string]interface{}) error {
152	if v == nil {
153		return nil
154	}
155
156	if vv, ok := v["$ref"]; ok {
157		if str, ok := vv.(string); ok {
158			ref, err := jsonreference.New(str)
159			if err != nil {
160				return err
161			}
162			*r = Ref{Ref: ref}
163		}
164	}
165
166	return nil
167}
168