Repository: getkin/kin-openapi
Pull request #96 (2019-05-29 14:31:43)

Define UUID format for string schema
This fixes error `Validating Swagger failed: Unsupported 'format' value 'uuid'`

fenollp: Yeah the spec https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md
says 
> Primitives have an optional modifier property: format. OAS uses several known formats to define in fine detail the data type being used. However, to support documentation needs, the format property is an open string-valued property, and can have any value. Formats such as "email", "uuid", and so on, MAY be used even though undefined by this specification. Types that are not accompanied by a format property follow the type definition in the JSON Schema. Tools that do not recognize a specific format MAY default back to the type alone, as if the format is not specified.

so as resolved on #79 albeit very expeditiously I don't think this lib must validate such formats by default (as opposed to e.g. `date-time`), however I'd love to have the formats defined in-lib and only activated at user's request.

So could you define an exported string constant in https://github.com/getkin/kin-openapi/blob/master/openapi3/schema_formats.go with the name `FormatOfStringForUUIDOfRFC4122`? Or something that has a similarly-easy-to-find-on-godoc-because-sorted prefix.
yaronius: @fenollp thank you for replying and reasonable remarks, I made changes and now uuid format is optional.
fenollp: Ah! Sorry I meant for `FormatOfStringForUUIDOfRFC4122` to be the value, not the uuid key :)
yaronius: @fenollp I see, updated
yaronius: @fenollp I tested my changes with various schema and I guess this change would not solve the issue with unsupported UUID format because all the supported formats are hardcoded [here](https://github.com/getkin/kin-openapi/blob/master/openapi3/schema.go#L500). I would suggest modifying the default case in `schema.go`:

```
default:
	// Try to check for custom defined formats
	if _, ok := SchemaStringFormats[format]; !ok {
		return unsupportedFormat(format)
	}
```

That works perfectly for cases when we define custom formats and looks flexible to me. What would you say about this?
fenollp: I believe this is the most correct way indeed! Thanks for taking the time :) 
Add this in and I'll merge the whole thing.
yaronius: Thanks, changes added

Patch:
diff --git a/openapi3/schema.go b/openapi3/schema.go
index 6fb768d..740fed8 100644
--- a/openapi3/schema.go
+++ b/openapi3/schema.go
@@ -195,6 +195,13 @@ func NewDateTimeSchema() *Schema {
 	}
 }
 
+func NewUuidSchema() *Schema {
+	return &Schema{
+		Type:   "string",
+		Format: "uuid",
+	}
+}
+
 func NewBytesSchema() *Schema {
 	return &Schema{
 		Type:   "string",
@@ -509,7 +516,10 @@ func (schema *Schema) validate(c context.Context, stack []*Schema) (err error) {
 			case "uri", "uri-reference", "iri", "iri-reference", "uri-template":
 			case "json-pointer", "relative-json-pointer":
 			default:
-				return unsupportedFormat(format)
+				// Try to check for custom defined formats
+				if _, ok := SchemaStringFormats[format]; !ok {
+					return unsupportedFormat(format)
+				}
 			}
 		}
 	case "array":
diff --git a/openapi3/schema_formats.go b/openapi3/schema_formats.go
index 9b2f316..746e408 100644
--- a/openapi3/schema_formats.go
+++ b/openapi3/schema_formats.go
@@ -5,6 +5,11 @@ import (
 	"regexp"
 )
 
+const (
+	// FormatOfStringForUUIDOfRFC4122 is an optional predefined format for UUID v1-v5 as specified by RFC4122
+	FormatOfStringForUUIDOfRFC4122 = `^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`
+)
+
 var SchemaStringFormats = make(map[string]*regexp.Regexp, 8)
 
 func DefineStringFormat(name string, pattern string) {


Test patch:
diff --git a/openapi3/schema_test.go b/openapi3/schema_test.go
index 8980c34..ba365eb 100644
--- a/openapi3/schema_test.go
+++ b/openapi3/schema_test.go
@@ -21,6 +21,7 @@ type schemaExample struct {
 }
 
 func TestSchemas(t *testing.T) {
+	openapi3.DefineStringFormat("uuid", openapi3.FormatOfStringForUUIDOfRFC4122)
 	for _, example := range schemaExamples {
 		t.Run(example.Title, testSchema(t, example))
 	}
@@ -236,6 +237,27 @@ var schemaExamples = []schemaExample{
 		},
 	},
 
+	{
+		Title:  "STRING: optional format 'uuid'",
+		Schema: openapi3.NewUuidSchema(),
+		Serialization: map[string]interface{}{
+			"type":   "string",
+			"format": "uuid",
+		},
+		AllValid: []interface{}{
+			"dd7d8481-81a3-407f-95f0-a2f1cb382a4b",
+			"dcba3901-2fba-48c1-9db2-00422055804e",
+			"ace8e3be-c254-4c10-8859-1401d9a9d52a",
+		},
+		AllInvalid: []interface{}{
+			nil,
+			"g39840b1-d0ef-446d-e555-48fcca50a90a",
+			"4cf3i040-ea14-4daa-b0b5-ea9329473519",
+			"aaf85740-7e27-4b4f-b4554-a03a43b1f5e3",
+			"56f5bff4-z4b6-48e6-a10d-b6cf66a83b04",
+		},
+	},
+
 	{
 		Title:  "STRING: format 'date-time'",
 		Schema: openapi3.NewDateTimeSchema(),