1package diff
2
3import (
4	"bytes"
5	"encoding/json"
6	"fmt"
7	"log"
8)
9
10// SpecChangeCode enumerates the various types of diffs from one spec to another
11type SpecChangeCode int
12
13const (
14	// NoChangeDetected - the specs have no changes
15	NoChangeDetected SpecChangeCode = iota
16	// DeletedProperty - A message property has been deleted in the new spec
17	DeletedProperty
18	// AddedProperty - A message property has been added in the new spec
19	AddedProperty
20	// AddedRequiredProperty - A required message property has been added in the new spec
21	AddedRequiredProperty
22	// DeletedOptionalParam - An endpoint parameter has been deleted in the new spec
23	DeletedOptionalParam
24	// ChangedDescripton - Changed a description
25	ChangedDescripton
26	// AddedDescripton - Added a description
27	AddedDescripton
28	// DeletedDescripton - Deleted a description
29	DeletedDescripton
30	// ChangedTag - Changed a tag
31	ChangedTag
32	// AddedTag - Added a tag
33	AddedTag
34	// DeletedTag - Deleted a tag
35	DeletedTag
36	// DeletedResponse - An endpoint response has been deleted in the new spec
37	DeletedResponse
38	// DeletedEndpoint - An endpoint has been deleted in the new spec
39	DeletedEndpoint
40	// DeletedDeprecatedEndpoint - A deprecated endpoint has been deleted in the new spec
41	DeletedDeprecatedEndpoint
42	// AddedRequiredParam - A required parameter has been added in the new spec
43	AddedRequiredParam
44	// DeletedRequiredParam - A required parameter has been deleted in the new spec
45	DeletedRequiredParam
46	// AddedEndpoint - An endpoint has been added in the new spec
47	AddedEndpoint
48	// WidenedType - An type has been changed to a more permissive type eg int->string
49	WidenedType
50	// NarrowedType - An type has been changed to a less permissive type eg string->int
51	NarrowedType
52	// ChangedToCompatibleType - An type has been changed to a compatible type eg password->string
53	ChangedToCompatibleType
54	// ChangedType - An type has been changed to a type whose relative compatibility cannot be determined
55	ChangedType
56	// AddedEnumValue - An enum type has had a new potential value added to it
57	AddedEnumValue
58	// DeletedEnumValue - An enum type has had a existing value removed from it
59	DeletedEnumValue
60	// AddedOptionalParam - A new optional parameter has been added to the new spec
61	AddedOptionalParam
62	// ChangedOptionalToRequired - An optional parameter is now required in the new spec
63	ChangedOptionalToRequired
64	// ChangedRequiredToOptional - An required parameter is now optional in the new spec
65	ChangedRequiredToOptional
66	// AddedResponse An endpoint has new response code in the new spec
67	AddedResponse
68	// AddedConsumesFormat - a new consumes format (json/xml/yaml etc) has been added in the new spec
69	AddedConsumesFormat
70	// DeletedConsumesFormat - an existing format has been removed in the new spec
71	DeletedConsumesFormat
72	// AddedProducesFormat - a new produces format (json/xml/yaml etc) has been added in the new spec
73	AddedProducesFormat
74	// DeletedProducesFormat - an existing produces format has been removed in the new spec
75	DeletedProducesFormat
76	// AddedSchemes - a new scheme has been added to the new spec
77	AddedSchemes
78	// DeletedSchemes - a scheme has been removed from the new spec
79	DeletedSchemes
80	// ChangedHostURL - the host url has been changed. If this is used in the client generation, then clients will break.
81	ChangedHostURL
82	// ChangedBasePath - the host base path has been changed. If this is used in the client generation, then clients will break.
83	ChangedBasePath
84	// AddedResponseHeader Added a header Item
85	AddedResponseHeader
86	// ChangedResponseHeader Added a header Item
87	ChangedResponseHeader
88	// DeletedResponseHeader Added a header Item
89	DeletedResponseHeader
90	// RefTargetChanged Changed a ref to point to a different object
91	RefTargetChanged
92	// RefTargetRenamed Renamed a ref to point to the same object
93	RefTargetRenamed
94	// DeletedConstraint Deleted a schema constraint
95	DeletedConstraint
96	// AddedConstraint Added a schema constraint
97	AddedConstraint
98	// DeletedDefinition removed one of the definitions
99	DeletedDefinition
100	// AddedDefinition removed one of the definitions
101	AddedDefinition
102)
103
104var toLongStringSpecChangeCode = map[SpecChangeCode]string{
105	NoChangeDetected:          "No Change detected",
106	AddedEndpoint:             "Added endpoint",
107	DeletedEndpoint:           "Deleted endpoint",
108	DeletedDeprecatedEndpoint: "Deleted a deprecated endpoint",
109	AddedRequiredProperty:     "Added required property",
110	DeletedProperty:           "Deleted property",
111	ChangedDescripton:         "Changed a description",
112	AddedDescripton:           "Added a description",
113	DeletedDescripton:         "Deleted a description",
114	ChangedTag:                "Changed a tag",
115	AddedTag:                  "Added a tag",
116	DeletedTag:                "Deleted a tag",
117	AddedProperty:             "Added property",
118	AddedOptionalParam:        "Added optional param",
119	AddedRequiredParam:        "Added required param",
120	DeletedOptionalParam:      "Deleted optional param",
121	DeletedRequiredParam:      "Deleted required param",
122	DeletedResponse:           "Deleted response",
123	AddedResponse:             "Added response",
124	WidenedType:               "Widened type",
125	NarrowedType:              "Narrowed type",
126	ChangedType:               "Changed type",
127	ChangedToCompatibleType:   "Changed type to equivalent type",
128	ChangedOptionalToRequired: "Changed optional param to required",
129	ChangedRequiredToOptional: "Changed required param to optional",
130	AddedEnumValue:            "Added possible enumeration(s)",
131	DeletedEnumValue:          "Deleted possible enumeration(s)",
132	AddedConsumesFormat:       "Added a consumes format",
133	DeletedConsumesFormat:     "Deleted a consumes format",
134	AddedProducesFormat:       "Added produces format",
135	DeletedProducesFormat:     "Deleted produces format",
136	AddedSchemes:              "Added schemes",
137	DeletedSchemes:            "Deleted schemes",
138	ChangedHostURL:            "Changed host URL",
139	ChangedBasePath:           "Changed base path",
140	AddedResponseHeader:       "Added response header",
141	ChangedResponseHeader:     "Changed response header",
142	DeletedResponseHeader:     "Deleted response header",
143	RefTargetChanged:          "Changed ref to different object",
144	RefTargetRenamed:          "Changed ref to renamed object",
145	DeletedConstraint:         "Deleted a schema constraint",
146	AddedConstraint:           "Added a schema constraint",
147	DeletedDefinition:         "Deleted a schema definition",
148	AddedDefinition:           "Added a schema definition",
149}
150
151var toStringSpecChangeCode = map[SpecChangeCode]string{
152	AddedEndpoint:             "AddedEndpoint",
153	NoChangeDetected:          "NoChangeDetected",
154	DeletedEndpoint:           "DeletedEndpoint",
155	DeletedDeprecatedEndpoint: "DeletedDeprecatedEndpoint",
156	AddedRequiredProperty:     "AddedRequiredProperty",
157	DeletedProperty:           "DeletedProperty",
158	AddedProperty:             "AddedProperty",
159	ChangedDescripton:         "ChangedDescription",
160	AddedDescripton:           "AddedDescription",
161	DeletedDescripton:         "DeletedDescription",
162	ChangedTag:                "ChangedTag",
163	AddedTag:                  "AddedTag",
164	DeletedTag:                "DeletedTag",
165	AddedOptionalParam:        "AddedOptionalParam",
166	AddedRequiredParam:        "AddedRequiredParam",
167	DeletedOptionalParam:      "DeletedRequiredParam",
168	DeletedRequiredParam:      "Deleted required param",
169	DeletedResponse:           "DeletedResponse",
170	AddedResponse:             "AddedResponse",
171	WidenedType:               "WidenedType",
172	NarrowedType:              "NarrowedType",
173	ChangedType:               "ChangedType",
174	ChangedToCompatibleType:   "ChangedToCompatibleType",
175	ChangedOptionalToRequired: "ChangedOptionalToRequiredParam",
176	ChangedRequiredToOptional: "ChangedRequiredToOptionalParam",
177	AddedEnumValue:            "AddedEnumValue",
178	DeletedEnumValue:          "DeletedEnumValue",
179	AddedConsumesFormat:       "AddedConsumesFormat",
180	DeletedConsumesFormat:     "DeletedConsumesFormat",
181	AddedProducesFormat:       "AddedProducesFormat",
182	DeletedProducesFormat:     "DeletedProducesFormat",
183	AddedSchemes:              "AddedSchemes",
184	DeletedSchemes:            "DeletedSchemes",
185	ChangedHostURL:            "ChangedHostURL",
186	ChangedBasePath:           "ChangedBasePath",
187	AddedResponseHeader:       "AddedResponseHeader",
188	ChangedResponseHeader:     "ChangedResponseHeader",
189	DeletedResponseHeader:     "DeletedResponseHeader",
190	RefTargetChanged:          "RefTargetChanged",
191	RefTargetRenamed:          "RefTargetRenamed",
192	DeletedConstraint:         "DeletedConstraint",
193	AddedConstraint:           "AddedConstraint",
194	DeletedDefinition:         "DeletedDefinition",
195	AddedDefinition:           "AddedDefinition",
196}
197
198var toIDSpecChangeCode = map[string]SpecChangeCode{}
199
200// Description returns an english version of this error
201func (s SpecChangeCode) Description() (result string) {
202	result, ok := toLongStringSpecChangeCode[s]
203	if !ok {
204		log.Printf("warning: No description for %v", s)
205		result = "UNDEFINED"
206	}
207	return
208}
209
210// MarshalJSON marshals the enum as a quoted json string
211func (s SpecChangeCode) MarshalJSON() ([]byte, error) {
212	return stringAsQuotedBytes(toStringSpecChangeCode[s])
213}
214
215// UnmarshalJSON unmashalls a quoted json string to the enum value
216func (s *SpecChangeCode) UnmarshalJSON(b []byte) error {
217	str, err := readStringFromByteStream(b)
218	if err != nil {
219		return err
220	}
221	// Note that if the string cannot be found then it will return an error to the caller.
222	val, ok := toIDSpecChangeCode[str]
223
224	if ok {
225		*s = val
226	} else {
227		return fmt.Errorf("unknown enum value. cannot unmarshal '%s'", str)
228	}
229	return nil
230}
231
232// Compatibility - whether this is a breaking or non-breaking change
233type Compatibility int
234
235const (
236	// Breaking this change could break existing clients
237	Breaking Compatibility = iota
238	// NonBreaking This is a backwards-compatible API change
239	NonBreaking
240)
241
242func (s Compatibility) String() string {
243	return toStringCompatibility[s]
244}
245
246var toStringCompatibility = map[Compatibility]string{
247	Breaking:    "Breaking",
248	NonBreaking: "NonBreaking",
249}
250
251var toIDCompatibility = map[string]Compatibility{}
252
253// MarshalJSON marshals the enum as a quoted json string
254func (s Compatibility) MarshalJSON() ([]byte, error) {
255	return stringAsQuotedBytes(toStringCompatibility[s])
256}
257
258// UnmarshalJSON unmashals a quoted json string to the enum value
259func (s *Compatibility) UnmarshalJSON(b []byte) error {
260	str, err := readStringFromByteStream(b)
261	if err != nil {
262		return err
263	}
264	// Note that if the string cannot be found then it will return an error to the caller.
265	val, ok := toIDCompatibility[str]
266
267	if ok {
268		*s = val
269	} else {
270		return fmt.Errorf("unknown enum value. cannot unmarshal '%s'", str)
271	}
272	return nil
273}
274
275func stringAsQuotedBytes(str string) ([]byte, error) {
276	buffer := bytes.NewBufferString(`"`)
277	buffer.WriteString(str)
278	buffer.WriteString(`"`)
279	return buffer.Bytes(), nil
280}
281
282func readStringFromByteStream(b []byte) (string, error) {
283	var j string
284	err := json.Unmarshal(b, &j)
285	if err != nil {
286		return "", err
287	}
288	return j, nil
289}
290
291func init() {
292	for key, val := range toStringSpecChangeCode {
293		toIDSpecChangeCode[val] = key
294	}
295	for key, val := range toStringCompatibility {
296		toIDCompatibility[val] = key
297	}
298}
299