diff --git a/helpers/schema_compiler.go b/helpers/schema_compiler.go index 6218912..9fc6cec 100644 --- a/helpers/schema_compiler.go +++ b/helpers/schema_compiler.go @@ -187,6 +187,26 @@ func transformNullableSchema(schema map[string]interface{}) map[string]interface } } } + allOf, hasAllOf := schema["allOf"] + if hasAllOf { + delete(schema, "allOf") + oneOfAdditions := []interface{}{ + map[string]interface{}{ + "allOf": allOf, + }, + map[string]interface{}{ + "type": "null", + }, + } + var oneOfSlice []interface{} + oneOf, hasOneOf := schema["oneOf"] + if hasOneOf { + oneOfSlice, _ = oneOf.([]interface{}) + } + oneOfSlice = append(oneOfSlice, oneOfAdditions...) + schema["oneOf"] = oneOfSlice + } + return schema } diff --git a/helpers/schema_compiler_test.go b/helpers/schema_compiler_test.go index 0cfc103..c5965b3 100644 --- a/helpers/schema_compiler_test.go +++ b/helpers/schema_compiler_test.go @@ -456,6 +456,132 @@ func TestTransformNullableSchema_ArrayTypeWithNull(t *testing.T) { assert.False(t, hasNullable) } +func TestTransformNullableSchema_NullableAllOf(t *testing.T) { + schema := map[string]interface{}{ + "type": []interface{}{"object"}, + "allOf": []interface{}{ + map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "type": "string", + }, + }, + }, + }, + "nullable": true, + } + + result := transformNullableSchema(schema) + + schemaType, ok := result["type"] + require.True(t, ok) + + typeArray, ok := schemaType.([]interface{}) + require.True(t, ok) + assert.Contains(t, typeArray, "object") + assert.Contains(t, typeArray, "null") + + oneOf, ok := result["oneOf"] + require.True(t, ok) + + oneOfSlice, ok := oneOf.([]interface{}) + require.True(t, ok) + + assert.Len(t, oneOfSlice, 2) + assert.Contains(t, oneOfSlice, map[string]interface{}{ + "allOf": []interface{}{ + map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "type": "string", + }, + }, + }, + }, + }) + assert.Contains(t, oneOfSlice, map[string]interface{}{ + "type": "null", + }) + + _, hasNullable := result["nullable"] + assert.False(t, hasNullable) +} + +func TestTransformNullableSchema_NullableAllOfWithExistingOneOf(t *testing.T) { + schema := map[string]interface{}{ + "type": []interface{}{"object"}, + "allOf": []interface{}{ + map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "type": "string", + }, + }, + }, + }, + "oneOf": []interface{}{ + map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "type": "string", + "const": []any{"val"}, + }, + }, + }, + }, + "nullable": true, + } + + result := transformNullableSchema(schema) + + schemaType, ok := result["type"] + require.True(t, ok) + + typeArray, ok := schemaType.([]interface{}) + require.True(t, ok) + assert.Contains(t, typeArray, "object") + assert.Contains(t, typeArray, "null") + + oneOf, ok := result["oneOf"] + require.True(t, ok) + + oneOfSlice, ok := oneOf.([]interface{}) + require.True(t, ok) + + assert.Len(t, oneOfSlice, 3) + assert.Contains(t, oneOfSlice, map[string]interface{}{ + "allOf": []interface{}{ + map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "type": "string", + }, + }, + }, + }, + }) + assert.Contains(t, oneOfSlice, map[string]interface{}{ + "type": "null", + }) + assert.Contains(t, oneOfSlice, map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "type": "string", + "const": []any{"val"}, + }, + }, + }) + + _, hasNullable := result["nullable"] + assert.False(t, hasNullable) +} + func TestTransformSchemaForCoercion_ValidJSON(t *testing.T) { input := []byte(`{ "type": "boolean"