Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor schema gen to generically create discriminated subschemas #704

Merged
merged 4 commits into from
Aug 10, 2021
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,18 @@ internal sealed class SchemaGenerator
{
public string GenerateSchema()
{
var settings = new JsonSchemaGeneratorSettings();
var schema = new JsonSchema();
var context = new GenerationContext(schema);
context.SetRoot<RootOptions>();

settings.SerializerSettings = new JsonSerializerSettings();
settings.SerializerSettings.Converters.Add(new StringEnumConverter());

JsonSchema schema = JsonSchema.FromType<RootOptions>(settings);
schema.Id = @"https://www.github.com/dotnet/dotnet-monitor";
schema.Title = "DotnetMonitorConfiguration";

JsonSchema jsonConsoleFormatterOptions = JsonSchema.FromType<JsonConsoleFormatterOptions>();
schema.Definitions.Add(nameof(JsonConsoleFormatterOptions), jsonConsoleFormatterOptions);

JsonSchema simpleConsoleFormatterOptions = JsonSchema.FromType<SimpleConsoleFormatterOptions>();
schema.Definitions.Add(nameof(SimpleConsoleFormatterOptions), simpleConsoleFormatterOptions);

JsonSchema systemdConsoleFormatterOptions = JsonSchema.FromType<ConsoleFormatterOptions>();
schema.Definitions.Add(nameof(ConsoleFormatterOptions), systemdConsoleFormatterOptions);

//Allow other properties in the schema.
schema.AdditionalPropertiesSchema = JsonSchema.CreateAnySchema();

AddConsoleLoggerFormatterSubSchemas(context);

//TODO Figure out a better way to add object defaults
schema.Definitions[nameof(EgressOptions)].Properties[nameof(EgressOptions.AzureBlobStorage)].Default = JsonSchema.CreateAnySchema();
schema.Definitions[nameof(EgressOptions)].Properties[nameof(EgressOptions.FileSystem)].Default = JsonSchema.CreateAnySchema();
Expand All @@ -57,64 +48,111 @@ public string GenerateSchema()
kvp.Value.Default = JsonSchema.CreateAnySchema();
}

JsonSchema jsonConsoleLoggerOptionsSchema = GenerateConsoleLoggerOptionsSchema(jsonConsoleFormatterOptions, ConsoleLoggerFormat.Json);
JsonSchema simpleConsoleLoggerOptionsSchema = GenerateConsoleLoggerOptionsSchema(simpleConsoleFormatterOptions, ConsoleLoggerFormat.Simple);
JsonSchema systemdConsoleLoggerOptionsSchema = GenerateConsoleLoggerOptionsSchema(systemdConsoleFormatterOptions, ConsoleLoggerFormat.Systemd);
JsonSchema defaultConsoleLoggerOptionsSchema = GenerateDefaultConsoleLoggerOptionsSchema(simpleConsoleFormatterOptions);

schema.Definitions[nameof(ConsoleLoggerOptions)].OneOf.Add(jsonConsoleLoggerOptionsSchema);
schema.Definitions[nameof(ConsoleLoggerOptions)].OneOf.Add(simpleConsoleLoggerOptionsSchema);
schema.Definitions[nameof(ConsoleLoggerOptions)].OneOf.Add(systemdConsoleLoggerOptionsSchema);
schema.Definitions[nameof(ConsoleLoggerOptions)].OneOf.Add(defaultConsoleLoggerOptionsSchema);

string schemaPayload = schema.ToJson();

//Normalize newlines embedded into json
schemaPayload = schemaPayload.Replace(@"\r\n", @"\n", StringComparison.Ordinal);
return schemaPayload;
}

public static JsonSchema GenerateConsoleLoggerOptionsSchema(JsonSchema consoleFormatterOptions, ConsoleLoggerFormat consoleLoggerFormat)
private static void AddConsoleLoggerFormatterSubSchemas(GenerationContext context)
{
JsonSchema consoleLoggerOptionsSchema = new JsonSchema();

JsonSchemaProperty formatterNameProperty = new JsonSchemaProperty();
JsonSchemaProperty formatterOptionsProperty = new JsonSchemaProperty();

JsonSchema formatterOptionsSchema = new JsonSchema();
formatterOptionsSchema.Reference = consoleFormatterOptions;

formatterOptionsProperty.Reference = formatterOptionsSchema;

formatterNameProperty.ExtensionData = new Dictionary<string, object>();
formatterNameProperty.ExtensionData.Add("const", consoleLoggerFormat.ToString());
AddConsoleLoggerOptionsSubSchema<JsonConsoleFormatterOptions>(context, ConsoleLoggerFormat.Json);
AddConsoleLoggerOptionsSubSchema<SimpleConsoleFormatterOptions>(context, ConsoleLoggerFormat.Simple);
AddConsoleLoggerOptionsSubSchema<ConsoleFormatterOptions>(context, ConsoleLoggerFormat.Systemd);
AddDefaultConsoleLoggerOptionsSubSchema(context);
}

consoleLoggerOptionsSchema.Properties.Add(nameof(ConsoleLoggerOptions.FormatterName), formatterNameProperty);
consoleLoggerOptionsSchema.Properties.Add(nameof(ConsoleLoggerOptions.FormatterOptions), formatterOptionsProperty);
private static void AddConsoleLoggerOptionsSubSchema<TOptions>(GenerationContext context, ConsoleLoggerFormat consoleLoggerFormat)
{
JsonSchema consoleLoggerOptionsSchema = new JsonSchema();
consoleLoggerOptionsSchema.RequiredProperties.Add(nameof(ConsoleLoggerOptions.FormatterName));

return consoleLoggerOptionsSchema;
JsonSchemaProperty formatterOptionsProperty = AddDiscriminatedSubSchema(
context.Schema.Definitions[nameof(ConsoleLoggerOptions)],
nameof(ConsoleLoggerOptions.FormatterName),
consoleLoggerFormat.ToString(),
nameof(ConsoleLoggerOptions.FormatterOptions),
consoleLoggerOptionsSchema);

formatterOptionsProperty.Reference = context.AddTypeIfNotExist<TOptions>();
}

public static JsonSchema GenerateDefaultConsoleLoggerOptionsSchema(JsonSchema consoleFormatterOptions)
private static void AddDefaultConsoleLoggerOptionsSubSchema(GenerationContext context)
{
JsonSchema consoleLoggerOptionsSchema = new JsonSchema();

JsonSchemaProperty formatterNameProperty = new JsonSchemaProperty();
JsonSchemaProperty formatterOptionsProperty = new JsonSchemaProperty();

JsonSchema formatterOptionsSchema = new JsonSchema();
formatterOptionsSchema.Reference = consoleFormatterOptions;

formatterOptionsProperty.Reference = formatterOptionsSchema;
formatterOptionsProperty.Reference = context.AddTypeIfNotExist<SimpleConsoleFormatterOptions>();

formatterNameProperty.Type = JsonObjectType.Null;
formatterNameProperty.Default = "Simple";

consoleLoggerOptionsSchema.Properties.Add(nameof(ConsoleLoggerOptions.FormatterName), formatterNameProperty);
consoleLoggerOptionsSchema.Properties.Add(nameof(ConsoleLoggerOptions.FormatterOptions), formatterOptionsProperty);

return consoleLoggerOptionsSchema;
context.Schema.Definitions[nameof(ConsoleLoggerOptions)].OneOf.Add(consoleLoggerOptionsSchema);
}

private static JsonSchemaProperty AddDiscriminatedSubSchema(
JsonSchema parentSchema,
string descriminatingPropertyName,
jander-msft marked this conversation as resolved.
Show resolved Hide resolved
string descriminatingPropertyValue,
string descriminatedPropertyName,
JsonSchema subSchema = null)
{
if (null == subSchema)
{
subSchema = new JsonSchema();
jander-msft marked this conversation as resolved.
Show resolved Hide resolved
}

JsonSchemaProperty descriminatingProperty = new JsonSchemaProperty();
jander-msft marked this conversation as resolved.
Show resolved Hide resolved
descriminatingProperty.ExtensionData = new Dictionary<string, object>();
descriminatingProperty.ExtensionData.Add("const", descriminatingPropertyValue);

subSchema.Properties.Add(descriminatingPropertyName, descriminatingProperty);

JsonSchemaProperty descriminatedProperty = new JsonSchemaProperty();

subSchema.Properties.Add(descriminatedPropertyName, descriminatedProperty);

parentSchema.OneOf.Add(subSchema);

return descriminatedProperty;
}

private class GenerationContext
{
private readonly JsonSchemaGenerator _generator;
private readonly JsonSchemaResolver _resolver;
private readonly JsonSchemaGeneratorSettings _settings;

public GenerationContext(JsonSchema rootSchema)
{
Schema = rootSchema;

_settings = new JsonSchemaGeneratorSettings();
_settings.SerializerSettings = new JsonSerializerSettings();
_settings.SerializerSettings.Converters.Add(new StringEnumConverter());

_resolver = new JsonSchemaResolver(rootSchema, _settings);
jander-msft marked this conversation as resolved.
Show resolved Hide resolved

_generator = new JsonSchemaGenerator(_settings);
}

public JsonSchema AddTypeIfNotExist<T>()
{
return _generator.Generate(typeof(T), _resolver);
}

public void SetRoot<T>()
{
_generator.Generate(Schema, typeof(T), _resolver);
}

public JsonSchema Schema { get; }
}
}
}