Simplify JSON schema, generation, copying and updating (#13427)

* Simplify JSON schema and only generate appsettings-schema.Umbraco.Cms.json

* Use Umbraco.JsonSchema.Extensions to dynamically add JSON schema references

* Move DependentUpon items to shared MSBuild props

* Update LangVersion to latest

* Update Umbraco.GitVersioning.Extensions to 0.2.0

* Remove JSON schemas on clean

* Remove Umbraco.JsonSchema.Core project

* Fix JSON schema nullability

* Ignore additional JSON schema files in template

* Update CompatibilitySuppressions.xml

* Remove GlobalSettings.UmbracoPath from JSON schema again

* Remove RemoveUmbracoJsonSchemaFiles target

* Update Umbraco.JsonSchema.Extensions to 0.2.0 and add weights

* Flatten generated JSON schema hierarchy

* Remove LicensesSettings from CMS codebase

* Change AdditionalParameters to IDictionary
This commit is contained in:
Ronald Barendse
2022-11-22 12:48:11 +01:00
committed by GitHub
parent 85842bde9f
commit 2e54579a2f
28 changed files with 214 additions and 409 deletions

2
.gitignore vendored
View File

@@ -101,7 +101,7 @@ preserve.belle
/tests/Umbraco.Tests.UnitTests/[Uu]mbraco/[Dd]ata/TEMP/
# Ignore auto-generated schema
/src/Umbraco.Cms.Targets/appsettings-schema.json
/src/Umbraco.Cms.Targets/tasks/
/src/Umbraco.Cms.Targets/appsettings-schema.*.json
/src/Umbraco.Web.UI/appsettings-schema.json
/src/Umbraco.Web.UI/appsettings-schema.*.json

View File

@@ -2,7 +2,7 @@
<Project>
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<LangVersion>preview</LangVersion>
<LangVersion>latest</LangVersion>
<Company>Umbraco HQ</Company>
<Authors>Umbraco</Authors>
<Copyright>Copyright © Umbraco $([System.DateTime]::Today.ToString('yyyy'))</Copyright>
@@ -41,7 +41,7 @@
<PackageReference Include="Nerdbank.GitVersioning" Version="3.5.113" PrivateAssets="all" IsImplicitlyDefined="true" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.406" PrivateAssets="all" IsImplicitlyDefined="true" />
<PackageReference Include="Umbraco.Code" Version="2.0.0" PrivateAssets="all" IsImplicitlyDefined="true" />
<PackageReference Include="Umbraco.GitVersioning.Extensions" Version="0.1.1" PrivateAssets="all" IsImplicitlyDefined="true" />
<PackageReference Include="Umbraco.GitVersioning.Extensions" Version="0.2.0" PrivateAssets="all" IsImplicitlyDefined="true" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,81 +0,0 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Models;
namespace JsonSchema;
internal class AppSettings
{
// ReSharper disable once InconsistentNaming
public CmsDefinition? CMS { get; set; }
/// <summary>
/// Configurations for the Umbraco CMS
/// </summary>
public class CmsDefinition
{
public ContentSettings? Content { get; set; }
public CoreDebugSettings? Debug { get; set; }
public ExceptionFilterSettings? ExceptionFilter { get; set; }
public ModelsBuilderSettings? ModelsBuilder { get; set; }
public GlobalSettings? Global { get; set; }
public HealthChecksSettings? HealthChecks { get; set; }
public HostingSettings? Hosting { get; set; }
public ImagingSettings? Imaging { get; set; }
public IndexCreatorSettings? Examine { get; set; }
public KeepAliveSettings? KeepAlive { get; set; }
public LoggingSettings? Logging { get; set; }
public NuCacheSettings? NuCache { get; set; }
public RequestHandlerSettings? RequestHandler { get; set; }
public RuntimeSettings? Runtime { get; set; }
public SecuritySettings? Security { get; set; }
public TourSettings? Tours { get; set; }
public TypeFinderSettings? TypeFinder { get; set; }
public WebRoutingSettings? WebRouting { get; set; }
public UmbracoPluginSettings? Plugins { get; set; }
public UnattendedSettings? Unattended { get; set; }
public RichTextEditorSettings? RichTextEditor { get; set; }
public RuntimeMinificationSettings? RuntimeMinification { get; set; }
public BasicAuthSettings? BasicAuth { get; set; }
public PackageMigrationSettings? PackageMigration { get; set; }
public LegacyPasswordMigrationSettings? LegacyPasswordMigration { get; set; }
public ContentDashboardSettings? ContentDashboard { get; set; }
public HelpPageSettings? HelpPage { get; set; }
public InstallDefaultDataSettings? DefaultDataCreation { get; set; }
public DataTypesSettings? DataTypes { get; set; }
public LicensesSettings? Licenses { get; set; }
public MarketplaceSettings? Marketplace { get; set; }
}
}

View File

@@ -1,13 +0,0 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
using NJsonSchema.Generation;
namespace JsonSchema
{
internal class NamespacePrefixedSchemaNameGenerator : DefaultSchemaNameGenerator
{
public override string Generate(Type type) => type.Namespace?.Replace(".", string.Empty) + base.Generate(type);
}
}

View File

@@ -1,16 +0,0 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using CommandLine;
namespace JsonSchema
{
internal class Options
{
[Option('m', "mainOutputFile", Required = false, HelpText = "Set path of the main output file.", Default = "../../../../Umbraco.Web.UI/appsettings-schema.json")]
public string MainOutputFile { get; set; } = null!;
[Option('f', "cmsOutputFile", Required = false, HelpText = "Set path of the cms output file.", Default = "../../../../Umbraco.Web.UI/appsettings-schema.umbraco.json")]
public string CmsOutputFile { get; set; } = null!;
}
}

View File

@@ -1,49 +0,0 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
using System.IO;
using System.Threading.Tasks;
using CommandLine;
namespace JsonSchema
{
internal class Program
{
public static async Task Main(string[] args)
{
try
{
await Parser.Default.ParseArguments<Options>(args)
.WithParsedAsync(Execute);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
private static async Task Execute(Options options)
{
var generator = new UmbracoJsonSchemaGenerator();
var cmsSchema = await generator.GenerateCmsFile();
await WriteSchemaToFile(cmsSchema, options.CmsOutputFile);
var schema = await generator.GenerateMainFile();
await WriteSchemaToFile(schema, options.MainOutputFile);
}
private static async Task WriteSchemaToFile(string schema, string filePath)
{
var mainPath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, filePath));
Console.WriteLine("Path to use {0}", mainPath);
Directory.CreateDirectory(Path.GetDirectoryName(mainPath)!);
Console.WriteLine("Ensured directory exists");
await File.WriteAllTextAsync(mainPath, schema);
Console.WriteLine("File written at {0}", mainPath);
}
}
}

View File

@@ -1,83 +0,0 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using Microsoft.Extensions.FileProviders;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NJsonSchema.Generation;
using Umbraco.Cms.Core.Configuration.Models;
namespace JsonSchema;
/// <summary>
/// Generator of the JsonSchema for AppSettings.json including A specific Umbraco version.
/// </summary>
public class UmbracoJsonSchemaGenerator
{
private static readonly HttpClient s_client = new();
private readonly JsonSchemaGenerator _innerGenerator;
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoJsonSchemaGenerator" /> class.
/// </summary>
public UmbracoJsonSchemaGenerator()
=> _innerGenerator = new JsonSchemaGenerator(new UmbracoJsonSchemaGeneratorSettings());
/// <summary>
/// Generates a json representing the JsonSchema for AppSettings.json including A specific Umbraco version..
/// </summary>
public async Task<string> GenerateMainFile()
{
JObject officialSchema = await GetOfficialAppSettingsSchema();
JObject externalFilePoints = GenerateSchemaWithExternalDefinitions();
officialSchema.Merge(externalFilePoints);
return officialSchema.ToString();
}
/// <summary>
/// Generates the CMS file
/// </summary>
/// <returns></returns>
public Task<string> GenerateCmsFile()
{
JObject cmsSchema = GenerateUmbracoSchema();
return Task.FromResult(cmsSchema.ToString());
}
private JObject GenerateSchemaWithExternalDefinitions()
{
var fileProvider = new EmbeddedFileProvider(GetType().Assembly);
IFileInfo schema = fileProvider.GetFileInfo("appsettings-schema.json");
using (Stream? stream = schema.CreateReadStream())
using (var reader = new StreamReader(stream))
{
return JsonConvert.DeserializeObject<JObject>(reader.ReadToEnd())!;
}
}
private async Task<JObject> GetOfficialAppSettingsSchema()
{
HttpResponseMessage response = await s_client.GetAsync("https://json.schemastore.org/appsettings.json")
.ConfigureAwait(false);
var result = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<JObject>(result)!;
}
private JObject GenerateUmbracoSchema()
{
NJsonSchema.JsonSchema schema = _innerGenerator.Generate(typeof(AppSettings));
// TODO: when the "UmbracoPath" setter is removed from "GlobalSettings" (scheduled for V12), remove this line as well
schema.Definitions["UmbracoCmsCoreConfigurationModelsGlobalSettings"]?.Properties?.Remove(nameof(GlobalSettings.UmbracoPath));return JsonConvert.DeserializeObject<JObject>(schema.ToJson())!;
}
}

View File

@@ -1,43 +0,0 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using NJsonSchema.Generation;
namespace JsonSchema
{
/// <inheritdoc />
public class UmbracoJsonSchemaGeneratorSettings : JsonSchemaGeneratorSettings
{
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoJsonSchemaGeneratorSettings"/> class.
/// </summary>
public UmbracoJsonSchemaGeneratorSettings()
{
AlwaysAllowAdditionalObjectProperties = true;
SerializerSettings = new JsonSerializerSettings()
{
ContractResolver = new WritablePropertiesOnlyResolver()
};
DefaultReferenceTypeNullHandling = ReferenceTypeNullHandling.NotNull;
SchemaNameGenerator = new NamespacePrefixedSchemaNameGenerator();
SerializerSettings.Converters.Add(new StringEnumConverter());
IgnoreObsoleteProperties = true;
GenerateExamples = true;
}
private class WritablePropertiesOnlyResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
return props.Where(p => p.Writable).ToList();
}
}
}
}

View File

@@ -1,61 +0,0 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"Umbraco": {
"description": "The container of all Umbraco content",
"oneOf": [
{
"type": "null"
},
{
"properties": {
"CMS": {
"description": "Configuration of Umbraco CMS",
"oneOf": [
{
"type": "null"
},
{
"$ref": "appsettings-schema.Umbraco.Cms.json#/definitions/JsonSchemaCmsDefinition"
}
]
},
"Forms": {
"description": "Configuration of Umbraco Forms",
"oneOf": [
{
"type": "null"
},
{
"$ref": "appsettings-schema.Umbraco.Forms.json#/definitions/JsonSchemaFormsDefinition"
}
]
},
"Deploy": {
"description": "Configuration of Umbraco Deploy",
"oneOf": [
{
"type": "null"
},
{
"$ref": "appsettings-schema.Umbraco.Deploy.json#/definitions/JsonSchemaDeployDefinition"
}
]
},
"Workflow": {
"description": "Configuration of Umbraco Workflow",
"oneOf": [
{
"type": "null"
},
{
"$ref": "appsettings-schema.Umbraco.Workflow.json#/definitions/JsonSchemaWorkflowDefinition"
}
]
}
}
}
]
}
}
}

View File

@@ -16,18 +16,38 @@
<ItemGroup>
<Content Include="buildTransitive\**" PackagePath="buildTransitive" />
<Content Include="$(JsonSchemaPath)" PackagePath="" />
<Content Include="$(JsonSchemaCmsPath)" PackagePath="" />
</ItemGroup>
<!-- Generate appsettings-schema.json on build (and before copying to project) -->
<!-- Add JSON schema references (and include MSBuild task) -->
<PropertyGroup>
<JsonSchemaPath>$(MSBuildThisFileDirectory)appsettings-schema.json</JsonSchemaPath>
<JsonSchemaCmsPath>$(MSBuildThisFileDirectory)appsettings-schema.Umbraco.Cms.json</JsonSchemaCmsPath>
<JsonSchemaProjectPath>$(MSBuildThisFileDirectory)..\JsonSchema\</JsonSchemaProjectPath>
<_UmbracoCmsJsonSchemaReference>appsettings-schema.Umbraco.Cms.json</_UmbracoCmsJsonSchemaReference>
<NoWarn>NU5100;NU5128</NoWarn>
</PropertyGroup>
<Target Name="GenerateAppsettingsSchema" BeforeTargets="Build;CopyAppsettingsSchema" Condition="!Exists('$(JsonSchemaPath)') or !Exists('$(JsonSchemaCmsPath)')">
<Message Text="Generating appsettings-schema.json and appsettings-schema.Umbraco.Cms.json because it doesn't exist" Importance="high" />
<Exec WorkingDirectory="$(JsonSchemaProjectPath)" Command="dotnet run -c Release -- --mainOutputFile &quot;$(JsonSchemaPath)&quot; --cmsOutputFile &quot;$(JsonSchemaCmsPath)&quot;" />
<ItemGroup>
<PackageReference Include="Umbraco.JsonSchema.Extensions" Version="0.2.0" PrivateAssets="all" GeneratePathProperty="true" />
<None Include="$(PkgUmbraco_JsonSchema_Extensions)\tasks\netstandard2.0\**" Pack="true" PackagePath="tasks\netstandard2.0" Visible="false" />
<Content Include="$(_UmbracoCmsJsonSchemaReference)" PackagePath="" Visible="false" />
</ItemGroup>
<!-- We also need physical copies in the right location relative to the Umbraco.Cms.Targets.targets file, as that's directly referenced in projects -->
<Target Name="CopyUmbracoJsonSchemaExtensionsFiles" BeforeTargets="Build">
<ItemGroup>
<_UmbracoJsonSchemaExtensionsFiles Include="$(PkgUmbraco_JsonSchema_Extensions)\tasks\netstandard2.0\**" />
</ItemGroup>
<Copy SourceFiles="@(_UmbracoJsonSchemaExtensionsFiles)" DestinationFolder="$(MSBuildThisFileDirectory)tasks\netstandard2.0" SkipUnchangedFiles="true" />
</Target>
<ItemGroup>
<None Remove="tasks\**" />
</ItemGroup>
<!-- Generate JSON schema on build (and before copying to project) -->
<Target Name="GenerateAppsettingsSchema" BeforeTargets="Build;CopyUmbracoJsonSchemaFiles" Condition="!Exists('$(_UmbracoCmsJsonSchemaReference)')">
<Message Text="Generating $(_UmbracoCmsJsonSchemaReference) because it doesn't exist" Importance="high" />
<Exec WorkingDirectory="$(MSBuildThisFileDirectory)..\..\tools\Umbraco.JsonSchema" Command="dotnet run --configuration $(Configuration) -- --outputFile &quot;$(MSBuildThisFileDirectory)$(_UmbracoCmsJsonSchemaReference)&quot;" />
</Target>
<!-- Remove generated JSON schema on clean -->
<Target Name="RemoveAppsettingsSchema" AfterTargets="Clean" Condition="Exists('$(_UmbracoCmsJsonSchemaReference)')">
<Delete Files="$(_UmbracoCmsJsonSchemaReference)" />
</Target>
</Project>

View File

@@ -9,4 +9,8 @@
<Using Include="Umbraco.Cms.Core.DependencyInjection" />
<Using Include="Umbraco.Extensions" />
</ItemGroup>
<ItemGroup>
<UmbracoJsonSchemaReferences Include="https://json.schemastore.org/appsettings.json" Weight="-100" />
<UmbracoJsonSchemaFiles Include="$(MSBuildThisFileDirectory)..\appsettings-schema.Umbraco.Cms.json" Weight="-90" />
</ItemGroup>
</Project>

View File

@@ -1,13 +1,32 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CopyAppsettingsSchema" BeforeTargets="Build">
<!-- Automatically nest files (this needs to be in this file, because the props file is imported too early) -->
<ItemGroup>
<_SchemaFiles Include="$(MSBuildThisFileDirectory)..\appsettings-schema.json" />
<_SchemaFiles Include="$(MSBuildThisFileDirectory)..\appsettings-schema.Umbraco.Cms.json" />
<Content Update="appsettings.*.json">
<DependentUpon>appsettings.json</DependentUpon>
</Content>
<Content Update="appsettings-schema.*.json">
<DependentUpon>appsettings-schema.json</DependentUpon>
</Content>
</ItemGroup>
<Message Text="Copying appsettings-schema.json and appsettings-schema.Umbraco.Cms.json files" Importance="high" />
<Copy SourceFiles="@(_SchemaFiles)" DestinationFolder="$(MSBuildProjectDirectory)" SkipUnchangedFiles="true" />
<!-- Copy JSON schema files into the project directory -->
<Target Name="CopyUmbracoJsonSchemaFiles" BeforeTargets="Build">
<Message Text="Copying JSON schema files into project directory: @(UmbracoJsonSchemaFiles->'%(Filename)%(Extension)')" Importance="high" />
<Copy SourceFiles="@(UmbracoJsonSchemaFiles)" DestinationFolder="$(MSBuildProjectDirectory)" SkipUnchangedFiles="true" />
</Target>
<!-- Add references to the JSON schema in the project directory -->
<UsingTask TaskName="JsonSchemaAddReferences" AssemblyFile="$(MSBuildThisFileDirectory)..\tasks\netstandard2.0\Umbraco.JsonSchema.Extensions.dll" />
<Target Name="AddUmbracoJsonSchemaReferences" BeforeTargets="Build" DependsOnTargets="CopyUmbracoJsonSchemaFiles">
<ItemGroup>
<!-- Include references to JSON schema files that are copied into the project directory (if they aren't already referenced and didn't opt-out) -->
<UmbracoJsonSchemaReferences Include="@(UmbracoJsonSchemaFiles->'%(Filename)%(Extension)#')" Exclude="@(UmbracoJsonSchemaReferences)" Condition="'%(UmbracoJsonSchemaFiles.Reference)' != 'false'" />
</ItemGroup>
<Message Text="Adding JSON schema references to appsettings-schema.json: @(UmbracoJsonSchemaReferences)" Importance="high" />
<JsonSchemaAddReferences JsonSchemaFile="$(MSBuildProjectDirectory)\appsettings-schema.json" References="@(UmbracoJsonSchemaReferences)" />
</Target>
<!-- Include App_Plugins content in output/publish directories -->
<Target Name="IncludeAppPluginsContent" BeforeTargets="GetCopyToOutputDirectoryItems;GetCopyToPublishDirectoryItems">
<ItemGroup>
<_AppPluginsFiles Include="App_Plugins\**" />
@@ -15,6 +34,7 @@
</ItemGroup>
</Target>
<!-- Include Umbraco folder content in output/publish directories -->
<Target Name="IncludeUmbracoFolderContent" BeforeTargets="GetCopyToOutputDirectoryItems;GetCopyToPublishDirectoryItems">
<ItemGroup>
<_UmbracoFolderFiles Include="umbraco\config\**" />

View File

@@ -82,8 +82,8 @@ public class GlobalSettings
public string UmbracoPath
{
get => Constants.System.DefaultUmbracoPath;
[Obsolete($"{nameof(UmbracoPath)} is no longer configurable, property setter is scheduled for removal in V12")]
// NOTE: when removing this, also clean up the hardcoded removal of UmbracoPath in UmbracoJsonSchemaGenerator
[Obsolete($"{nameof(UmbracoPath)} is no longer configurable, this property setter is scheduled for removal in V12.")]
// NOTE: When removing this, also clean up the hardcoded removal of UmbracoPath in Umbraco.JsonSchema
set { }
}

View File

@@ -1,11 +0,0 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
namespace Umbraco.Cms.Core.Configuration.Models;
/// <summary>
/// Typed configuration options for license settings.
/// </summary>
public class LicensesSettings : Dictionary<string, string>
{
}

View File

@@ -12,5 +12,5 @@ public class MarketplaceSettings
/// <summary>
/// Gets or sets the additional parameters that are sent to the Marketplace.
/// </summary>
public Dictionary<string, string> AdditionalParameters { get; set; } = new ();
public IDictionary<string, string> AdditionalParameters { get; set; } = new Dictionary<string, string>();
}

View File

@@ -28,15 +28,6 @@
<RazorCompileOnPublish>false</RazorCompileOnPublish>
</PropertyGroup>
<ItemGroup>
<Content Update="appsettings.*.json">
<DependentUpon>appsettings.json</DependentUpon>
</Content>
<Content Update="appsettings-schema.*.json">
<DependentUpon>appsettings-schema.json</DependentUpon>
</Content>
</ItemGroup>
<Target Name="CopyAppsettingsTemplate" BeforeTargets="Build" Condition="!Exists('appsettings.json')">
<Message Text="Copying appsettings.template.json to appsettings.json because it doesn't exist" Importance="high" />
<Copy SourceFiles="appsettings.template.json" DestinationFiles="appsettings.json" />

View File

@@ -1,5 +1,5 @@
{
"$schema" : "./appsettings-schema.json",
"$schema": "appsettings-schema.json",
"Serilog": {
"MinimumLevel": {
"Default": "Information",

View File

@@ -1,5 +1,5 @@
{
"$schema": "./appsettings-schema.json",
"$schema": "appsettings-schema.json",
"ConnectionStrings": {
"umbracoDbDSN": ""
},

View File

@@ -459,8 +459,9 @@ $RECYCLE.BIN/
## Umbraco CMS
##
# JSON schema file for appsettings.json
# JSON schema files for appsettings.json
appsettings-schema.json
appsettings-schema.*.json
# Packages created from the backoffice (package.xml/package.zip)
/umbraco/Data/CreatedPackages/

View File

@@ -27,15 +27,6 @@
<RazorCompileOnPublish>false</RazorCompileOnPublish>
</PropertyGroup>
<ItemGroup>
<Content Update="appsettings.*.json">
<DependentUpon>appsettings.json</DependentUpon>
</Content>
<Content Update="appsettings-schema.*.json">
<DependentUpon>appsettings-schema.json</DependentUpon>
</Content>
</ItemGroup>
<Import Project="..\PACKAGE_PROJECT_NAME_FROM_TEMPLATE\build\PACKAGE_PROJECT_NAME_FROM_TEMPLATE.targets" Condition="'$(PackageProjectName)' != ''" />
<ItemGroup Condition="'$(PackageProjectName)' != ''">
<ProjectReference Include="..\PACKAGE_PROJECT_NAME_FROM_TEMPLATE\PACKAGE_PROJECT_NAME_FROM_TEMPLATE.csproj" />

View File

@@ -1,5 +1,5 @@
{
"$schema": "./appsettings-schema.json",
"$schema": "appsettings-schema.json",
"Serilog": {
"MinimumLevel": {
"Default": "Information"

View File

@@ -1,5 +1,5 @@
{
"$schema": "./appsettings-schema.json",
"$schema": "appsettings-schema.json",
"Serilog": {
"MinimumLevel": {
"Default": "Information",

View File

@@ -0,0 +1,7 @@
using CommandLine;
internal class Options
{
[Option("outputFile", Default = "appsettings-schema.Umbraco.Cms.json", HelpText = "Output file to save the generated JSON schema for Umbraco CMS.")]
public string OutputFile { get; set; } = null!;
}

View File

@@ -0,0 +1,14 @@
using CommandLine;
using Umbraco.Cms.Core.Configuration.Models;
await Parser.Default.ParseArguments<Options>(args).WithParsedAsync(async options =>
{
// Generate CMS schema
var jsonSchemaGenerator = new UmbracoJsonSchemaGenerator();
var jsonSchema = jsonSchemaGenerator.Generate(typeof(UmbracoCmsSchema));
// TODO: When the UmbracoPath setter is removed from GlobalSettings (scheduled for V12), remove this line as well
jsonSchema.Definitions[nameof(GlobalSettings)]?.Properties?.Remove(nameof(GlobalSettings.UmbracoPath));
await File.WriteAllTextAsync(options.OutputFile, jsonSchema.ToJson());
});

View File

@@ -8,14 +8,9 @@
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="NJsonSchema" Version="10.8.0" />
<PackageReference Include="System.Xml.XPath.XmlDocument" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Umbraco.Core\Umbraco.Core.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="appsettings-schema.json" />
<ProjectReference Include="..\..\src\Umbraco.Core\Umbraco.Core.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,81 @@
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Models;
internal class UmbracoCmsSchema
{
public UmbracoDefinition Umbraco { get; set; } = null!;
/// <summary>
/// Configuration container for all Umbraco products.
/// </summary>
public class UmbracoDefinition
{
public UmbracoCmsDefinition CMS { get; set; } = null!;
}
/// <summary>
/// Configuration of Umbraco CMS.
/// </summary>
public class UmbracoCmsDefinition
{
public ContentSettings Content { get; set; } = null!;
public CoreDebugSettings Debug { get; set; } = null!;
public ExceptionFilterSettings ExceptionFilter { get; set; } = null!;
public ModelsBuilderSettings ModelsBuilder { get; set; } = null!;
public GlobalSettings Global { get; set; } = null!;
public HealthChecksSettings HealthChecks { get; set; } = null!;
public HostingSettings Hosting { get; set; } = null!;
public ImagingSettings Imaging { get; set; } = null!;
public IndexCreatorSettings Examine { get; set; } = null!;
public KeepAliveSettings KeepAlive { get; set; } = null!;
public LoggingSettings Logging { get; set; } = null!;
public NuCacheSettings NuCache { get; set; } = null!;
public RequestHandlerSettings RequestHandler { get; set; } = null!;
public RuntimeSettings Runtime { get; set; } = null!;
public SecuritySettings Security { get; set; } = null!;
public TourSettings Tours { get; set; } = null!;
public TypeFinderSettings TypeFinder { get; set; } = null!;
public WebRoutingSettings WebRouting { get; set; } = null!;
public UmbracoPluginSettings Plugins { get; set; } = null!;
public UnattendedSettings Unattended { get; set; } = null!;
public RichTextEditorSettings RichTextEditor { get; set; } = null!;
public RuntimeMinificationSettings RuntimeMinification { get; set; } = null!;
public BasicAuthSettings BasicAuth { get; set; } = null!;
public PackageMigrationSettings PackageMigration { get; set; } = null!;
public LegacyPasswordMigrationSettings LegacyPasswordMigration { get; set; } = null!;
public ContentDashboardSettings ContentDashboard { get; set; } = null!;
public HelpPageSettings HelpPage { get; set; } = null!;
public InstallDefaultDataSettings DefaultDataCreation { get; set; } = null!;
public DataTypesSettings DataTypes { get; set; } = null!;
public MarketplaceSettings Marketplace { get; set; } = null!;
}
}

View File

@@ -0,0 +1,35 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using NJsonSchema.Generation;
/// <inheritdoc />
public class UmbracoJsonSchemaGenerator : JsonSchemaGenerator
{
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoJsonSchemaGenerator" /> class.
/// </summary>
public UmbracoJsonSchemaGenerator()
: base(new JsonSchemaGeneratorSettings()
{
AlwaysAllowAdditionalObjectProperties = true,
DefaultReferenceTypeNullHandling = ReferenceTypeNullHandling.NotNull,
FlattenInheritanceHierarchy = true,
IgnoreObsoleteProperties = true,
SerializerSettings = new JsonSerializerSettings()
{
ContractResolver = new WritablePropertiesOnlyResolver()
}
})
{
Settings.SerializerSettings.Converters.Add(new StringEnumConverter());
}
/// <inheritdoc />
private class WritablePropertiesOnlyResolver : DefaultContractResolver
{
/// <inheritdoc />
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
=> base.CreateProperties(type, memberSerialization).Where(p => p.Writable).ToList();
}
}

View File

@@ -81,7 +81,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Tests.UnitTests", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Web.Common", "src\Umbraco.Web.Common\Umbraco.Web.Common.csproj", "{79E4293D-C92C-4649-AEC8-F1EFD95BDEB1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonSchema", "src\JsonSchema\JsonSchema.csproj", "{2A5027D9-F71D-4957-929E-F7A56AA1B95A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.JsonSchema", "tools\Umbraco.JsonSchema\Umbraco.JsonSchema.csproj", "{2A5027D9-F71D-4957-929E-F7A56AA1B95A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.Persistence.Sqlite", "src\Umbraco.Cms.Persistence.Sqlite\Umbraco.Cms.Persistence.Sqlite.csproj", "{32F6A309-EC1E-4CDB-BA80-C804CF680BEE}"
EndProject
@@ -156,6 +156,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "styles", "styles", "{EA628A
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.Imaging.ImageSharp", "src\Umbraco.Cms.Imaging.ImageSharp\Umbraco.Cms.Imaging.ImageSharp.csproj", "{C280181E-597B-4AA5-82E7-D7017E928749}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{05878304-40EB-4F84-B40B-91BDB70DE094}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -325,6 +327,7 @@ Global
{D6319409-777A-4BD0-93ED-B2DFD805B32C} = {B5BD12C1-A454-435E-8A46-FF4A364C0382}
{A499779C-1B3B-48A8-B551-458E582E6E96} = {B5BD12C1-A454-435E-8A46-FF4A364C0382}
{9102ABDF-E537-4E46-B525-C9ED4833EED0} = {B5BD12C1-A454-435E-8A46-FF4A364C0382}
{2A5027D9-F71D-4957-929E-F7A56AA1B95A} = {05878304-40EB-4F84-B40B-91BDB70DE094}
{05C1D0C8-C592-468F-AF8F-A299B9B3A903} = {6D72A60B-0542-4AA9-A493-DD4179E838A1}
{0946531B-F06D-415B-A4E3-6CBFF5DB1C12} = {995D9EFA-8BB1-4333-80AD-C525A06FD984}
{CBCE0A1E-BF29-49A6-9581-EAB3587D823A} = {995D9EFA-8BB1-4333-80AD-C525A06FD984}