File: Settings\TemplateInfoReader.cs
Web Access
Project: src\src\sdk\src\TemplateEngine\Microsoft.TemplateEngine.Edge\Microsoft.TemplateEngine.Edge.csproj (Microsoft.TemplateEngine.Edge)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using Microsoft.TemplateEngine.Abstractions;
using Microsoft.TemplateEngine.Abstractions.Constraints;
using Microsoft.TemplateEngine.Abstractions.Parameters;
using Microsoft.TemplateEngine.Utils;

namespace Microsoft.TemplateEngine.Edge.Settings
{
    internal partial class TemplateInfo
    {
        internal class TemplateInfoReader
        {
            internal static TemplateInfo FromJObject(JsonObject entry)
            {
                string identity = entry.ToString(nameof(Identity)) ?? throw new ArgumentException($"{nameof(entry)} doesn't have {nameof(Identity)} property.", nameof(entry));
                string name = entry.ToString(nameof(Name)) ?? throw new ArgumentException($"{nameof(entry)} doesn't have {nameof(Name)} property.", nameof(entry));
                string mountPointUri = entry.ToString(nameof(MountPointUri)) ?? throw new ArgumentException($"{nameof(entry)} doesn't have {nameof(MountPointUri)} property.", nameof(entry));
                string configPlace = entry.ToString(nameof(ConfigPlace)) ?? throw new ArgumentException($"{nameof(entry)} doesn't have {nameof(ConfigPlace)} property.", nameof(entry));
                JsonNode? shortNameToken = entry.Get<JsonNode>(nameof(ShortNameList));
                IEnumerable<string> shortNames = shortNameToken.JTokenStringOrArrayToCollection([]);

                TemplateInfo info = new TemplateInfo(identity, name, shortNames, mountPointUri, configPlace)
                {
                    Author = entry.ToString(nameof(Author))
                };
                JsonArray? classificationsArray = entry.Get<JsonArray>(nameof(Classifications));
                if (classificationsArray != null)
                {
                    List<string> classifications = new List<string>();
                    foreach (JsonNode? item in classificationsArray)
                    {
                        if (item != null)
                        {
                            classifications.Add(item.GetValue<string>());
                        }
                    }
                    info.Classifications = classifications;
                }

                info.DefaultName = entry.ToString(nameof(DefaultName));
                info.PreferDefaultName = entry.ToBool(nameof(PreferDefaultName));
                info.Description = entry.ToString(nameof(Description));
                info.GeneratorId = Guid.Parse(entry.ToString(nameof(GeneratorId)));
                info.GroupIdentity = entry.ToString(nameof(GroupIdentity));
                info.Precedence = entry.ToInt32(nameof(Precedence));

                info.LocaleConfigPlace = entry.ToString(nameof(LocaleConfigPlace));
                info.HostConfigPlace = entry.ToString(nameof(HostConfigPlace));
                info.ThirdPartyNotices = entry.ToString(nameof(ThirdPartyNotices));

                JsonObject? baselineJObject = entry.Get<JsonObject>(nameof(ITemplateInfo.BaselineInfo));
                Dictionary<string, IBaselineInfo> baselineInfo = new Dictionary<string, IBaselineInfo>();
                if (baselineJObject != null)
                {
                    foreach (var item in baselineJObject)
                    {
                        var defaultOverrides = item.Value?.ToStringDictionary(propertyName: nameof(IBaselineInfo.DefaultOverrides));
                        if (defaultOverrides is null)
                        {
                            continue;
                        }

                        IBaselineInfo baseline = new BaselineInfo(defaultOverrides, item.Value.ToString(nameof(IBaselineInfo.Description)));
                        baselineInfo.Add(item.Key, baseline);
                    }
                    info.BaselineInfo = baselineInfo;
                }

                //read parameters
#pragma warning disable CS0618 // Type or member is obsolete
                JsonArray? parametersArray = entry.Get<JsonArray>(nameof(Parameters));
#pragma warning restore CS0618 // Type or member is obsolete
                if (parametersArray != null)
                {
                    List<ITemplateParameter> templateParameters = new List<ITemplateParameter>();
                    foreach (JsonNode? item in parametersArray)
                    {
                        if (item is JsonObject jObj)
                        {
                            templateParameters.Add(ParameterFromJObject(jObj));
                        }
                    }
                    info.ParameterDefinitions = new ParameterDefinitionSet(templateParameters);
                }

                //read tags
                // tags are just "name": "description"
                // e.g.: "language": "C#"
                JsonObject? tagsObject = entry.Get<JsonObject>(nameof(TagsCollection));
                if (tagsObject != null)
                {
                    Dictionary<string, string> tags = new Dictionary<string, string>();
                    foreach (var item in tagsObject)
                    {
                        tags.Add(item.Key, item.Value?.GetValue<string>() ?? string.Empty);
                    }
                    info.TagsCollection = tags;
                }

                info.HostData = entry.ToString(nameof(info.HostData));
                JsonArray? postActionsArray = entry.Get<JsonArray>(nameof(info.PostActions));
                if (postActionsArray != null)
                {
                    List<Guid> postActions = new List<Guid>();
                    foreach (JsonNode? item in postActionsArray)
                    {
                        if (item != null && Guid.TryParse(item.GetValue<string>(), out Guid id))
                        {
                            postActions.Add(id);
                        }
                    }
                    info.PostActions = postActions;
                }

                //read parameters
                JsonArray? constraintsArray = entry.Get<JsonArray>(nameof(info.Constraints));
                if (constraintsArray != null)
                {
                    List<TemplateConstraintInfo> constraints = new List<TemplateConstraintInfo>();
                    foreach (JsonNode? item in constraintsArray)
                    {
                        string? type = item.ToString(nameof(TemplateConstraintInfo.Type));
                        if (string.IsNullOrWhiteSpace(type))
                        {
                            throw new ArgumentException($"{nameof(entry)} has {nameof(info.Constraints)} property which item doesn't have {nameof(TemplateConstraintInfo.Type)}.", nameof(entry));
                        }
                        constraints.Add(new TemplateConstraintInfo(type!, item.ToString(nameof(TemplateConstraintInfo.Args))));
                    }
                    info.Constraints = constraints;
                }

                return info;
            }

            /// <summary>
            /// Parses <see cref="ITemplateParameter"/> from <see cref="JsonObject"/>.
            /// </summary>
            /// <param name="jObject"></param>
            private static ITemplateParameter ParameterFromJObject(JsonObject jObject)
            {
                string? name = jObject.ToString(nameof(ITemplateParameter.Name));
                if (string.IsNullOrWhiteSpace(name))
                {
                    throw new ArgumentException($"{nameof(ITemplateParameter.Name)} property should not be null or whitespace", nameof(jObject));
                }

                string type = jObject.ToString(nameof(ITemplateParameter.Type)) ?? "parameter";
                string dataType = jObject.ToString(nameof(ITemplateParameter.DataType)) ?? "string";
                string? description = jObject.ToString(nameof(ITemplateParameter.Description));

                string? defaultValue = jObject.ToString(nameof(ITemplateParameter.DefaultValue));
                string? defaultIfOptionWithoutValue = jObject.ToString(nameof(ITemplateParameter.DefaultIfOptionWithoutValue));
                string? displayName = jObject.ToString(nameof(ITemplateParameter.DisplayName));
                bool isName = jObject.ToBool(nameof(ITemplateParameter.IsName));
                bool allowMultipleValues = jObject.ToBool(nameof(ITemplateParameter.AllowMultipleValues));

                Dictionary<string, ParameterChoice>? choices = null;

                if (dataType.Equals("choice", StringComparison.OrdinalIgnoreCase))
                {
                    choices = new Dictionary<string, ParameterChoice>(StringComparer.OrdinalIgnoreCase);
                    JsonObject? cdToken = jObject.Get<JsonObject>(nameof(ITemplateParameter.Choices));
                    if (cdToken != null)
                    {
                        foreach (var cdPair in cdToken)
                        {
                            choices.Add(
                                cdPair.Key,
                                new ParameterChoice(
                                    cdPair.Value.ToString(nameof(ParameterChoice.DisplayName)),
                                    cdPair.Value.ToString(nameof(ParameterChoice.Description))));
                        }
                    }
                }

                TemplateParameterPrecedence precedence = jObject.ToTemplateParameterPrecedence(nameof(ITemplateParameter.Precedence));

                return new CacheTemplateParameter(
                    new TemplateParameter(name!, type, dataType)
                    {
                        DisplayName = displayName,
                        Precedence = precedence,
                        IsName = isName,
                        DefaultValue = defaultValue,
                        DefaultIfOptionWithoutValue = defaultIfOptionWithoutValue,
                        Description = description,
                        AllowMultipleValues = allowMultipleValues,
                        Choices = choices
                    });
            }

            /// <summary>
            /// This class is overload on <see cref="ITemplateParameter"/> controlling JSON serialization for template parameters in cache.
            /// Not all the members are required to be serialized.
            /// </summary>
            private class CacheTemplateParameter : ITemplateParameter
            {
                private readonly ITemplateParameter _parameter;

                internal CacheTemplateParameter(ITemplateParameter parameter)
                {
                    _parameter = parameter;
                }

                public string? Description => _parameter.Description;

                [JsonPropertyName("Name")]
                public string Name => _parameter.Name;

                [JsonPropertyName("Precedence")]
                public TemplateParameterPrecedence Precedence => _parameter.Precedence;

                [JsonPropertyName("Type")]
                public string Type => _parameter.Type;

                [JsonPropertyName("IsName")]
                public bool IsName => _parameter.IsName;

                [JsonPropertyName("DefaultValue")]
                public string? DefaultValue => _parameter.DefaultValue;

                [JsonPropertyName("DefaultIfOptionWithoutValue")]
                public string? DefaultIfOptionWithoutValue => _parameter.DefaultIfOptionWithoutValue;

                [JsonPropertyName("DataType")]
                public string DataType => _parameter.DataType;

                [JsonPropertyName("Choices")]
                public IReadOnlyDictionary<string, ParameterChoice>? Choices => _parameter.Choices;

                [JsonPropertyName("DisplayName")]
                public string? DisplayName => _parameter.DisplayName;

                [JsonPropertyName("AllowMultipleValues")]
                public bool AllowMultipleValues => _parameter.AllowMultipleValues;

                [Obsolete]
                [JsonIgnore]
                public TemplateParameterPriority Priority => _parameter.Priority;

                [Obsolete]
                [JsonIgnore]
                public string? Documentation => _parameter.Documentation;

                public bool Equals(ITemplateParameter other) => _parameter.Equals(other);
            }
        }
    }
}