errors.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. package gojsonschema
  2. import (
  3. "bytes"
  4. "sync"
  5. "text/template"
  6. )
  7. var errorTemplates = errorTemplate{template.New("errors-new"), sync.RWMutex{}}
  8. // template.Template is not thread-safe for writing, so some locking is done
  9. // sync.RWMutex is used for efficiently locking when new templates are created
  10. type errorTemplate struct {
  11. *template.Template
  12. sync.RWMutex
  13. }
  14. type (
  15. // FalseError. ErrorDetails: -
  16. FalseError struct {
  17. ResultErrorFields
  18. }
  19. // RequiredError indicates that a required field is missing
  20. // ErrorDetails: property string
  21. RequiredError struct {
  22. ResultErrorFields
  23. }
  24. // InvalidTypeError indicates that a field has the incorrect type
  25. // ErrorDetails: expected, given
  26. InvalidTypeError struct {
  27. ResultErrorFields
  28. }
  29. // NumberAnyOfError is produced in case of a failing "anyOf" validation
  30. // ErrorDetails: -
  31. NumberAnyOfError struct {
  32. ResultErrorFields
  33. }
  34. // NumberOneOfError is produced in case of a failing "oneOf" validation
  35. // ErrorDetails: -
  36. NumberOneOfError struct {
  37. ResultErrorFields
  38. }
  39. // NumberAllOfError is produced in case of a failing "allOf" validation
  40. // ErrorDetails: -
  41. NumberAllOfError struct {
  42. ResultErrorFields
  43. }
  44. // NumberNotError is produced if a "not" validation failed
  45. // ErrorDetails: -
  46. NumberNotError struct {
  47. ResultErrorFields
  48. }
  49. // MissingDependencyError is produced in case of a "missing dependency" problem
  50. // ErrorDetails: dependency
  51. MissingDependencyError struct {
  52. ResultErrorFields
  53. }
  54. // InternalError indicates an internal error
  55. // ErrorDetails: error
  56. InternalError struct {
  57. ResultErrorFields
  58. }
  59. // ConstError indicates a const error
  60. // ErrorDetails: allowed
  61. ConstError struct {
  62. ResultErrorFields
  63. }
  64. // EnumError indicates an enum error
  65. // ErrorDetails: allowed
  66. EnumError struct {
  67. ResultErrorFields
  68. }
  69. // ArrayNoAdditionalItemsError is produced if additional items were found, but not allowed
  70. // ErrorDetails: -
  71. ArrayNoAdditionalItemsError struct {
  72. ResultErrorFields
  73. }
  74. // ArrayMinItemsError is produced if an array contains less items than the allowed minimum
  75. // ErrorDetails: min
  76. ArrayMinItemsError struct {
  77. ResultErrorFields
  78. }
  79. // ArrayMaxItemsError is produced if an array contains more items than the allowed maximum
  80. // ErrorDetails: max
  81. ArrayMaxItemsError struct {
  82. ResultErrorFields
  83. }
  84. // ItemsMustBeUniqueError is produced if an array requires unique items, but contains non-unique items
  85. // ErrorDetails: type, i, j
  86. ItemsMustBeUniqueError struct {
  87. ResultErrorFields
  88. }
  89. // ArrayContainsError is produced if an array contains invalid items
  90. // ErrorDetails:
  91. ArrayContainsError struct {
  92. ResultErrorFields
  93. }
  94. // ArrayMinPropertiesError is produced if an object contains less properties than the allowed minimum
  95. // ErrorDetails: min
  96. ArrayMinPropertiesError struct {
  97. ResultErrorFields
  98. }
  99. // ArrayMaxPropertiesError is produced if an object contains more properties than the allowed maximum
  100. // ErrorDetails: max
  101. ArrayMaxPropertiesError struct {
  102. ResultErrorFields
  103. }
  104. // AdditionalPropertyNotAllowedError is produced if an object has additional properties, but not allowed
  105. // ErrorDetails: property
  106. AdditionalPropertyNotAllowedError struct {
  107. ResultErrorFields
  108. }
  109. // InvalidPropertyPatternError is produced if an pattern was found
  110. // ErrorDetails: property, pattern
  111. InvalidPropertyPatternError struct {
  112. ResultErrorFields
  113. }
  114. // InvalidPropertyNameError is produced if an invalid-named property was found
  115. // ErrorDetails: property
  116. InvalidPropertyNameError struct {
  117. ResultErrorFields
  118. }
  119. // StringLengthGTEError is produced if a string is shorter than the minimum required length
  120. // ErrorDetails: min
  121. StringLengthGTEError struct {
  122. ResultErrorFields
  123. }
  124. // StringLengthLTEError is produced if a string is longer than the maximum allowed length
  125. // ErrorDetails: max
  126. StringLengthLTEError struct {
  127. ResultErrorFields
  128. }
  129. // DoesNotMatchPatternError is produced if a string does not match the defined pattern
  130. // ErrorDetails: pattern
  131. DoesNotMatchPatternError struct {
  132. ResultErrorFields
  133. }
  134. // DoesNotMatchFormatError is produced if a string does not match the defined format
  135. // ErrorDetails: format
  136. DoesNotMatchFormatError struct {
  137. ResultErrorFields
  138. }
  139. // MultipleOfError is produced if a number is not a multiple of the defined multipleOf
  140. // ErrorDetails: multiple
  141. MultipleOfError struct {
  142. ResultErrorFields
  143. }
  144. // NumberGTEError is produced if a number is lower than the allowed minimum
  145. // ErrorDetails: min
  146. NumberGTEError struct {
  147. ResultErrorFields
  148. }
  149. // NumberGTError is produced if a number is lower than, or equal to the specified minimum, and exclusiveMinimum is set
  150. // ErrorDetails: min
  151. NumberGTError struct {
  152. ResultErrorFields
  153. }
  154. // NumberLTEError is produced if a number is higher than the allowed maximum
  155. // ErrorDetails: max
  156. NumberLTEError struct {
  157. ResultErrorFields
  158. }
  159. // NumberLTError is produced if a number is higher than, or equal to the specified maximum, and exclusiveMaximum is set
  160. // ErrorDetails: max
  161. NumberLTError struct {
  162. ResultErrorFields
  163. }
  164. // ConditionThenError is produced if a condition's "then" validation is invalid
  165. // ErrorDetails: -
  166. ConditionThenError struct {
  167. ResultErrorFields
  168. }
  169. // ConditionElseError is produced if a condition's "else" condition is invalid
  170. // ErrorDetails: -
  171. ConditionElseError struct {
  172. ResultErrorFields
  173. }
  174. )
  175. // newError takes a ResultError type and sets the type, context, description, details, value, and field
  176. func newError(err ResultError, context *JsonContext, value interface{}, locale locale, details ErrorDetails) {
  177. var t string
  178. var d string
  179. switch err.(type) {
  180. case *FalseError:
  181. t = "false"
  182. d = locale.False()
  183. case *RequiredError:
  184. t = "required"
  185. d = locale.Required()
  186. case *InvalidTypeError:
  187. t = "invalid_type"
  188. d = locale.InvalidType()
  189. case *NumberAnyOfError:
  190. t = "number_any_of"
  191. d = locale.NumberAnyOf()
  192. case *NumberOneOfError:
  193. t = "number_one_of"
  194. d = locale.NumberOneOf()
  195. case *NumberAllOfError:
  196. t = "number_all_of"
  197. d = locale.NumberAllOf()
  198. case *NumberNotError:
  199. t = "number_not"
  200. d = locale.NumberNot()
  201. case *MissingDependencyError:
  202. t = "missing_dependency"
  203. d = locale.MissingDependency()
  204. case *InternalError:
  205. t = "internal"
  206. d = locale.Internal()
  207. case *ConstError:
  208. t = "const"
  209. d = locale.Const()
  210. case *EnumError:
  211. t = "enum"
  212. d = locale.Enum()
  213. case *ArrayNoAdditionalItemsError:
  214. t = "array_no_additional_items"
  215. d = locale.ArrayNoAdditionalItems()
  216. case *ArrayMinItemsError:
  217. t = "array_min_items"
  218. d = locale.ArrayMinItems()
  219. case *ArrayMaxItemsError:
  220. t = "array_max_items"
  221. d = locale.ArrayMaxItems()
  222. case *ItemsMustBeUniqueError:
  223. t = "unique"
  224. d = locale.Unique()
  225. case *ArrayContainsError:
  226. t = "contains"
  227. d = locale.ArrayContains()
  228. case *ArrayMinPropertiesError:
  229. t = "array_min_properties"
  230. d = locale.ArrayMinProperties()
  231. case *ArrayMaxPropertiesError:
  232. t = "array_max_properties"
  233. d = locale.ArrayMaxProperties()
  234. case *AdditionalPropertyNotAllowedError:
  235. t = "additional_property_not_allowed"
  236. d = locale.AdditionalPropertyNotAllowed()
  237. case *InvalidPropertyPatternError:
  238. t = "invalid_property_pattern"
  239. d = locale.InvalidPropertyPattern()
  240. case *InvalidPropertyNameError:
  241. t = "invalid_property_name"
  242. d = locale.InvalidPropertyName()
  243. case *StringLengthGTEError:
  244. t = "string_gte"
  245. d = locale.StringGTE()
  246. case *StringLengthLTEError:
  247. t = "string_lte"
  248. d = locale.StringLTE()
  249. case *DoesNotMatchPatternError:
  250. t = "pattern"
  251. d = locale.DoesNotMatchPattern()
  252. case *DoesNotMatchFormatError:
  253. t = "format"
  254. d = locale.DoesNotMatchFormat()
  255. case *MultipleOfError:
  256. t = "multiple_of"
  257. d = locale.MultipleOf()
  258. case *NumberGTEError:
  259. t = "number_gte"
  260. d = locale.NumberGTE()
  261. case *NumberGTError:
  262. t = "number_gt"
  263. d = locale.NumberGT()
  264. case *NumberLTEError:
  265. t = "number_lte"
  266. d = locale.NumberLTE()
  267. case *NumberLTError:
  268. t = "number_lt"
  269. d = locale.NumberLT()
  270. case *ConditionThenError:
  271. t = "condition_then"
  272. d = locale.ConditionThen()
  273. case *ConditionElseError:
  274. t = "condition_else"
  275. d = locale.ConditionElse()
  276. }
  277. err.SetType(t)
  278. err.SetContext(context)
  279. err.SetValue(value)
  280. err.SetDetails(details)
  281. err.SetDescriptionFormat(d)
  282. details["field"] = err.Field()
  283. if _, exists := details["context"]; !exists && context != nil {
  284. details["context"] = context.String()
  285. }
  286. err.SetDescription(formatErrorDescription(err.DescriptionFormat(), details))
  287. }
  288. // formatErrorDescription takes a string in the default text/template
  289. // format and converts it to a string with replacements. The fields come
  290. // from the ErrorDetails struct and vary for each type of error.
  291. func formatErrorDescription(s string, details ErrorDetails) string {
  292. var tpl *template.Template
  293. var descrAsBuffer bytes.Buffer
  294. var err error
  295. errorTemplates.RLock()
  296. tpl = errorTemplates.Lookup(s)
  297. errorTemplates.RUnlock()
  298. if tpl == nil {
  299. errorTemplates.Lock()
  300. tpl = errorTemplates.New(s)
  301. if ErrorTemplateFuncs != nil {
  302. tpl.Funcs(ErrorTemplateFuncs)
  303. }
  304. tpl, err = tpl.Parse(s)
  305. errorTemplates.Unlock()
  306. if err != nil {
  307. return err.Error()
  308. }
  309. }
  310. err = tpl.Execute(&descrAsBuffer, details)
  311. if err != nil {
  312. return err.Error()
  313. }
  314. return descrAsBuffer.String()
  315. }