feat: generate definition for complex types

This commit is contained in:
Stevan Freeborn
2024-06-30 14:42:10 -05:00
parent 96ed89c9ce
commit 057803a8f9
2 changed files with 109 additions and 5 deletions
@@ -46,7 +46,6 @@ static class JsonSchemaGenerator
}
var attribute = parameter.GetCustomAttribute<FunctionParameterAttribute>();
var paramName = attribute?.Name ?? parameter.Name;
var paramDescription = attribute?.Description ?? string.Empty;
var paramRequired = attribute?.Required ?? !parameter.HasDefaultValue;
@@ -78,7 +77,7 @@ static class JsonSchemaGenerator
{
return new JsonObject()
{
[RefKey] = $"#/definitions/{type.FullName}"
[RefKey] = GetDefinitionPath(type)
};
}
@@ -151,14 +150,65 @@ static class JsonSchemaGenerator
private static JsonObject GenerateTypeDefinitionSchema(Type type, JsonObject inputSchema)
{
var definitions = inputSchema[DefinitionsKey] ?? new JsonObject();
var typeSchema = new JsonObject();
var typeSchema = new JsonObject()
{
[TypeKey] = ObjectType
};
// TODO: Implement schema generation for complex types
List<MemberInfo> members = [];
var bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
members.AddRange(type.GetProperties(bindingFlags));
members.AddRange(type.GetFields(bindingFlags));
var memberProperties = new JsonObject();
var memberRequiredProperties = new JsonArray();
foreach (var member in members)
{
JsonObject memberProperty;
var memberType = member switch
{
PropertyInfo property => property.PropertyType,
FieldInfo field => field.FieldType,
_ => throw new InvalidOperationException("Member is not a property or field")
};
// TODO: Provide attribute to allow specifying...
// 1. Name
// 2. Description
// 3. Required
// 4. Default values
// 5. Possible values
var memberPropertyName = memberType.Name;
var memberDescription = string.Empty;
var memberRequired = Nullable.GetUnderlyingType(memberType) is null;
memberProperty = definitions.AsObject().ContainsKey(memberType.FullName)
? new JsonObject()
{
[RefKey] = GetDefinitionPath(memberType)
}
: GenerateParameterTypeSchema(memberType, inputSchema);
if (memberRequired)
{
memberRequiredProperties.Add(memberPropertyName);
}
memberProperty[DescriptionKey] = memberDescription;
memberProperties[memberPropertyName] = memberProperty;
}
typeSchema[PropertiesKey] = memberProperties;
typeSchema[RequiredPropertiesKey] = memberRequiredProperties;
definitions[type.FullName] = typeSchema;
inputSchema[DefinitionsKey] = definitions;
return new JsonObject()
{
[RefKey] = $"#/definitions/{type.FullName}"
[RefKey] = GetDefinitionPath(type)
};
}
private static string GetDefinitionPath(Type type) => $"#/definitions/{type.FullName}";
}
@@ -447,7 +447,61 @@ public class JsonSchemaGeneratorTestData : IEnumerable<object[]>
},
}
};
// class parameter type with no attributes
yield return new object[]
{
Tool.CreateFromFunction(TestToolName,TestToolDescription,(Person person) => person),
new JsonObject()
{
["type"] = "object",
["definitions"] = new JsonObject()
{
[$"{typeof(Person).FullName}"] = new JsonObject()
{
["type"] = "object",
["properties"] = new JsonObject()
{
["String"] = new JsonObject()
{
["type"] = "string",
["description"] = string.Empty
},
["Int32"] = new JsonObject()
{
["type"] = "integer",
["description"] = string.Empty
}
},
["required"] = new JsonArray()
{
"String",
"Int32"
}
}
},
["properties"] = new JsonObject()
{
["person"] = new JsonObject()
{
["$ref"] = $"#/definitions/{typeof(Person).FullName}",
["description"] = string.Empty
}
},
["required"] = new JsonArray()
{
"person",
},
}
};
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
class Person
{
public string Name { get; } = string.Empty;
public int Age { get; }
}