public void CanReadHostDataFromITemplateInfo() { IEngineEnvironmentSettings engineEnvironmentSettings = _environmentSettingsHelper.CreateEnvironment(virtualize: true); HostSpecificDataLoader hostSpecificDataLoader = new HostSpecificDataLoader(engineEnvironmentSettings); Assert.True(engineEnvironmentSettings.TryGetMountPoint(Directory.GetCurrentDirectory(), out IMountPoint? mountPoint)); Assert.NotNull(mountPoint); IFile?dataFile = mountPoint !.FileInfo("/Resources/dotnetcli.host.json"); Assert.NotNull(dataFile); JObject json = ReadJObjectFromIFile(dataFile !); ITemplateInfo template = A.Fake <ITemplateInfo>(builder => builder.Implements <ITemplateInfoHostJsonCache>().Implements <ITemplateInfo>()); A.CallTo(() => ((ITemplateInfoHostJsonCache)template).HostData).Returns(json); HostSpecificTemplateData data = hostSpecificDataLoader.ReadHostSpecificTemplateData(template); Assert.NotNull(data); Assert.False(data.IsHidden); Assert.Equal(2, data.UsageExamples?.Count); Assert.Contains("--framework netcoreapp3.1 --langVersion '9.0'", data.UsageExamples); Assert.Equal(4, data.SymbolInfo?.Count); Assert.Contains("TargetFrameworkOverride", data.HiddenParameterNames); Assert.Contains("Framework", data.ParametersToAlwaysShow); Assert.True(data.LongNameOverrides.ContainsKey("skipRestore")); Assert.Equal("no-restore", data.LongNameOverrides["skipRestore"]); Assert.True(data.ShortNameOverrides.ContainsKey("skipRestore")); Assert.Equal("", data.ShortNameOverrides["skipRestore"]); Assert.Equal("no-restore", data.DisplayNameForParameter("skipRestore")); }
public void ReparseForTemplate(ITemplateInfo templateInfo, HostSpecificTemplateData hostSpecificTemplateData) { Dictionary <string, string> templateParamValues = new Dictionary <string, string>(); Dictionary <string, IList <string> > remainingParams = new Dictionary <string, IList <string> >(); foreach (KeyValuePair <string, string> inputParam in _rawParameterInputs) { ITemplateParameter matchedParam = default(ITemplateParameter); if (templateInfo.Parameters != null) { matchedParam = templateInfo.Parameters?.FirstOrDefault(x => string.Equals(x.Name, inputParam.Key)); } if (matchedParam != default(ITemplateParameter)) { templateParamValues.Add(inputParam.Key, inputParam.Value); } else { remainingParams.Add(inputParam.Key, new List <string>()); } } InputTemplateParams = templateParamValues; RemainingParameters = remainingParams; RemainingArguments = remainingParams.Keys.ToList(); _allParametersForTemplate = templateInfo.Parameters.Select(x => x.Name).ToList(); }
public void ReparseForTemplate(ITemplateInfo templateInfo, HostSpecificTemplateData hostSpecificTemplateData) { // The params getting filtered out are "standard" to dotnet new - they get explicitly setup in the command // and their flags cannot be overridden by host specific configuration. // type & language: These are "tags" in template.json, which become params in the templateInfo object. // name: Gets added as a param in SimpleConfigModel - to facilitate the built in value forms for name. // name can also be explicitly specified in the template.json - for custom value forms on name. List <ITemplateParameter> filteredParams = templateInfo.Parameters.Where(x => !string.Equals(x.Name, "type", StringComparison.OrdinalIgnoreCase) && !string.Equals(x.Name, "language", StringComparison.OrdinalIgnoreCase) && !string.Equals(x.Name, "name", StringComparison.OrdinalIgnoreCase)) .ToList(); Command _templateSpecificCommand; try { _templateSpecificCommand = CommandParserSupport.CreateNewCommandWithArgsForTemplate( _commandName, _templateNameArg, filteredParams, hostSpecificTemplateData.LongNameOverrides, hostSpecificTemplateData.ShortNameOverrides, out IReadOnlyDictionary <string, IReadOnlyList <string> > templateParamMap); _currentCommand = _templateSpecificCommand; ParseArgs(); // this must happen after ParseArgs(), which resets _templateParamCanonicalToVariantMap _templateParamCanonicalToVariantMap = templateParamMap; Dictionary <string, string> templateParamValues = new Dictionary <string, string>(); foreach (KeyValuePair <string, IReadOnlyList <string> > paramInfo in _templateParamCanonicalToVariantMap) { string paramName = paramInfo.Key; string firstVariant = paramInfo.Value[0]; // This returns true if the arg was specified, irrespective of whether it has a value. // If the arg was specified, it goes in the list. // Null valued args are important - they facilitate bools & other value-optional args. if (_parseResult.TryGetArgumentValueAtPath(out string argValue, new[] { _commandName, firstVariant })) { templateParamValues.Add(paramName, argValue); } } _templateParamValues = templateParamValues; } catch (Exception ex) { throw new CommandParserException("Error parsing input parameters", string.Join(" ", _args), ex); } }
public void ReturnDefaultForInvalidEntry() { IEngineEnvironmentSettings engineEnvironmentSettings = _environmentSettingsHelper.CreateEnvironment(virtualize: true); HostSpecificDataLoader hostSpecificDataLoader = new HostSpecificDataLoader(engineEnvironmentSettings); ITemplateInfo template = A.Fake <ITemplateInfo>(builder => builder.Implements <ITemplateInfoHostJsonCache>().Implements <ITemplateInfo>()); A.CallTo(() => ((ITemplateInfoHostJsonCache)template).HostData).Returns(null); HostSpecificTemplateData data = hostSpecificDataLoader.ReadHostSpecificTemplateData(template); Assert.NotNull(data); Assert.Equal(HostSpecificTemplateData.Default, data); }
public void ReturnDefaultForInvalidFile() { IEngineEnvironmentSettings engineEnvironmentSettings = _environmentSettingsHelper.CreateEnvironment(virtualize: true); HostSpecificDataLoader hostSpecificDataLoader = new HostSpecificDataLoader(engineEnvironmentSettings); ITemplateInfo template = A.Fake <ITemplateInfo>(); A.CallTo(() => template.MountPointUri).Returns(Directory.GetCurrentDirectory()); A.CallTo(() => template.HostConfigPlace).Returns("unknown"); HostSpecificTemplateData data = hostSpecificDataLoader.ReadHostSpecificTemplateData(template); Assert.NotNull(data); Assert.Equal(HostSpecificTemplateData.Default, data); }
public void CreateDataForTemplatePack(IInstalledPackInfo packInfo, IReadOnlyList <ITemplateInfo> templateList, IEngineEnvironmentSettings environment) { IHostSpecificDataLoader hostDataLoader = new HostSpecificDataLoader(environment.SettingsLoader); foreach (ITemplateInfo template in templateList) { HostSpecificTemplateData hostData = hostDataLoader.ReadHostSpecificTemplateData(template); // store the host data if it has any info that could affect searching for this template. if (hostData.IsHidden || hostData.SymbolInfo.Count > 0) { _hostDataForPackByTemplate[template.Identity] = hostData; } } }
public void TestPerformCoreTemplateQuery_UnknownParameterNameInvalidatesMatch() { List <ITemplateInfo> templatesToSearch = new List <ITemplateInfo>(); templatesToSearch.Add(new TemplateInfo() { ShortName = "foo", Name = "Foo template", Identity = "foo.test", GroupIdentity = "foo.test.template", Precedence = 100, Tags = new Dictionary <string, ICacheTag>(StringComparer.OrdinalIgnoreCase), CacheParameters = new Dictionary <string, ICacheParameter>() { { "bar", new CacheParameter() }, } }); INewCommandInput userInputs = new MockNewCommandInput( new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase) { { "baz", null } } ) { TemplateName = "foo" }; IHostSpecificDataLoader hostSpecificDataLoader = new MockHostSpecificDataLoader(); TemplateListResolutionResult matchResult = TemplateListResolver.PerformCoreTemplateQuery(templatesToSearch, hostSpecificDataLoader, userInputs, null); Assert.Equal(null, matchResult.MatchedTemplatesWithSecondaryMatchInfo); Assert.Equal(1, matchResult.UnambiguousTemplateGroupToUse.Count); // TODO: // These 2 lines are the analogues of what happens in New3Command.EnterSingularTemplateManipulationFlowAsync() // as a final verification for the template. // This is really a shortcoming of the resolver, this check should get factored into it. // But we'll need to add a new match category so we know that the unambiguous template "matched", except the extra user parameters. HostSpecificTemplateData hostTemplateData = hostSpecificDataLoader.ReadHostSpecificTemplateData(matchResult.UnambiguousTemplateGroupToUse[0].Info); userInputs.ReparseForTemplate(matchResult.UnambiguousTemplateGroupToUse[0].Info, hostTemplateData); Assert.False(TemplateListResolver.ValidateRemainingParameters(userInputs, out IReadOnlyList <string> invalidParams)); Assert.Equal(1, invalidParams.Count); Assert.Equal("baz", invalidParams[0]); }
private static bool GenerateUsageForTemplate(ITemplateInfo templateInfo, IHostSpecificDataLoader hostDataLoader, string commandName) { HostSpecificTemplateData hostTemplateData = hostDataLoader.ReadHostSpecificTemplateData(templateInfo); if (hostTemplateData.UsageExamples != null) { if (hostTemplateData.UsageExamples.Count == 0) { return(false); } Reporter.Output.WriteLine($" dotnet {commandName} {templateInfo.ShortName} {hostTemplateData.UsageExamples[0]}"); return(true); } Reporter.Output.Write($" dotnet {commandName} {templateInfo.ShortName}"); IReadOnlyList <ITemplateParameter> allParameterDefinitions = templateInfo.Parameters; IEnumerable <ITemplateParameter> filteredParams = TemplateParameterHelpBase.FilterParamsForHelp(allParameterDefinitions, hostTemplateData.HiddenParameterNames, parametersToAlwaysShow: hostTemplateData.ParametersToAlwaysShow); foreach (ITemplateParameter parameter in filteredParams) { if (string.Equals(parameter.DataType, "bool", StringComparison.OrdinalIgnoreCase) && string.Equals(parameter.DefaultValue, "false", StringComparison.OrdinalIgnoreCase)) { continue; } else if (string.Equals(parameter.DataType, "string", StringComparison.OrdinalIgnoreCase)) { continue; } else if (string.Equals(parameter.DataType, "choice", StringComparison.OrdinalIgnoreCase) && parameter.Choices.Count == 1) { continue; } string displayParameter = hostTemplateData.DisplayNameForParameter(parameter.Name); Reporter.Output.Write($" --{displayParameter}"); if (!string.IsNullOrEmpty(parameter.DefaultValue) && !string.Equals(parameter.DataType, "bool", StringComparison.OrdinalIgnoreCase)) { Reporter.Output.Write($" {parameter.DefaultValue}"); } } Reporter.Output.WriteLine(); return(true); }
public void ReparseForTemplate(ITemplateInfo templateInfo, HostSpecificTemplateData hostSpecificTemplateData) { List <ITemplateParameter> filteredParams = templateInfo.Parameters.Where(x => !string.Equals(x.Name, "type", StringComparison.OrdinalIgnoreCase) && !string.Equals(x.Name, "language", StringComparison.OrdinalIgnoreCase)) .ToList(); Command _templateSpecificCommand; try { _templateSpecificCommand = CommandParserSupport.CreateNewCommandWithArgsForTemplate( _commandName, _templateNameArg, filteredParams, hostSpecificTemplateData.LongNameOverrides, hostSpecificTemplateData.ShortNameOverrides, out IReadOnlyDictionary <string, IReadOnlyList <string> > templateParamMap); _currentCommand = _templateSpecificCommand; ParseArgs(); // this must happen after ParseArgs(), which resets _templateParamCanonicalToVariantMap _templateParamCanonicalToVariantMap = templateParamMap; Dictionary <string, string> templateParamValues = new Dictionary <string, string>(); foreach (KeyValuePair <string, IReadOnlyList <string> > paramInfo in _templateParamCanonicalToVariantMap) { string paramName = paramInfo.Key; string firstVariant = paramInfo.Value[0]; // This returns true if the arg was specified, irrespective of whether it has a value. // If the arg was specified, it goes in the list. // Null valued args are important - they facilitate bools & other value-optional args. if (_parseResult.TryGetArgumentValueAtPath(out string argValue, new[] { _commandName, firstVariant })) { templateParamValues.Add(paramName, argValue); } } _templateParamValues = templateParamValues; } catch (Exception ex) { throw new CommandParserException("Error parsing input parameters", string.Join(" ", _args), ex); } }
public void CanSerializeData_SkipsEmpty() { var usageExamples = Array.Empty <string>(); var symbolInfo = new Dictionary <string, IReadOnlyDictionary <string, string> >() { { "param1", new Dictionary <string, string>() { { "isHidden", "false" }, { "longName", "longParam1" }, { "shortName", "shortParam1" } } }, { "param2", new Dictionary <string, string>() { { "isHidden", "false" }, { "longName", "" }, { "shortName", "shortParam2" } } }, { "param3", new Dictionary <string, string>() { { "isHidden", "true" } } } }; var data = new HostSpecificTemplateData(symbolInfo, usageExamples, isHidden: false); var serialized = JObject.FromObject(data); Assert.NotNull(serialized); Assert.Equal(1, serialized.Children().Count()); Assert.Single <JProperty>(serialized.Properties(), p => p.Name == "SymbolInfo"); var symbolInfoArray = serialized.Properties().Single().Value as JObject; Assert.NotNull(symbolInfoArray); //empty values should stay when deserializing symbol info Assert.Equal(3, ((JObject)symbolInfoArray !["param1"] !).Properties().Count());
public void CanSerializeData() { var usageExamples = new[] { "example1" }; var symbolInfo = new Dictionary <string, IReadOnlyDictionary <string, string> >() { { "param1", new Dictionary <string, string>() { { "isHidden", "false" }, { "longName", "longParam1" }, { "shortName", "shortParam1" } } }, { "param2", new Dictionary <string, string>() { { "isHidden", "false" }, { "longName", "" }, { "shortName", "shortParam2" } } }, { "param3", new Dictionary <string, string>() { { "isHidden", "true" } } } }; var data = new HostSpecificTemplateData(symbolInfo, usageExamples, isHidden: true); var serialized = JObject.FromObject(data); Assert.NotNull(serialized); Assert.Equal(3, serialized.Children().Count()); Assert.Single <JProperty>(serialized.Properties(), p => p.Name == "UsageExamples"); Assert.Single <JProperty>(serialized.Properties(), p => p.Name == "SymbolInfo"); Assert.Single <JProperty>(serialized.Properties(), p => p.Name == "IsHidden"); }
public void ReparseForTemplate(ITemplateInfo templateInfo, HostSpecificTemplateData hostSpecificTemplateData) { Dictionary <string, string> templateParamValues = new Dictionary <string, string>(); Dictionary <string, IList <string> > remainingParams = new Dictionary <string, IList <string> >(); Dictionary <string, string> overrideToCanonicalMap = hostSpecificTemplateData.LongNameOverrides.ToDictionary(o => o.Value, o => o.Key); foreach (KeyValuePair <string, string> shortNameOverride in hostSpecificTemplateData.ShortNameOverrides) { overrideToCanonicalMap[shortNameOverride.Value] = shortNameOverride.Key; } foreach (KeyValuePair <string, string> inputParam in _templateOptions) { ITemplateParameter matchedParam = default(ITemplateParameter); if (templateInfo.Parameters != null) { matchedParam = templateInfo.Parameters?.FirstOrDefault(x => string.Equals(x.Name, inputParam.Key)); } if (matchedParam != default(ITemplateParameter)) { templateParamValues.Add(inputParam.Key, inputParam.Value); } else if (overrideToCanonicalMap.TryGetValue(inputParam.Key, out string canonical)) { templateParamValues.Add(canonical, inputParam.Value); } else { remainingParams.Add(inputParam.Key, new List <string>()); } } InputTemplateParams = templateParamValues; RemainingParameters = remainingParams; RemainingArguments = remainingParams.Keys.ToList(); _allParametersForTemplate = templateInfo.Parameters.Select(x => x.Name).ToList(); }
private static bool IsTemplateHiddenByHostFile(ITemplateInfo templateInfo, IHostSpecificDataLoader hostDataLoader) { HostSpecificTemplateData hostData = hostDataLoader.ReadHostSpecificTemplateData(templateInfo); return hostData.IsHidden; }
public static void ParseTemplateArgs(ITemplateInfo templateInfo, IHostSpecificDataLoader hostDataLoader, INewCommandInput commandInput) { HostSpecificTemplateData hostData = hostDataLoader.ReadHostSpecificTemplateData(templateInfo); commandInput.ReparseForTemplate(templateInfo, hostData); }
private static TemplateGroupParameterDetails DetermineParameterDispositionsForTemplateGroup(IReadOnlyList <ITemplateInfo> templateGroup, IEngineEnvironmentSettings environmentSettings, INewCommandInput commandInput, IHostSpecificDataLoader hostDataLoader, TemplateCreator templateCreator) { HashSet <string> groupUserParamsWithInvalidValues = new HashSet <string>(StringComparer.Ordinal); bool groupHasPostActionScriptRunner = false; List <IParameterSet> parameterSetsForAllTemplatesInGroup = new List <IParameterSet>(); IDictionary <string, InvalidParameterInfo> invalidParametersForGroup = new Dictionary <string, InvalidParameterInfo>(StringComparer.Ordinal); bool firstInList = true; Dictionary <string, IReadOnlyList <string> > defaultVariantsForCanonicals = new Dictionary <string, IReadOnlyList <string> >(StringComparer.Ordinal); Dictionary <string, IReadOnlyList <string> > groupVariantsForCanonicals = new Dictionary <string, IReadOnlyList <string> >(StringComparer.Ordinal); HashSet <string> groupUserParamsWithDefaultValues = new HashSet <string>(StringComparer.Ordinal); Dictionary <string, bool> parameterHidingDisposition = new Dictionary <string, bool>(StringComparer.OrdinalIgnoreCase); HashSet <string> parametersToAlwaysShow = new HashSet <string>(StringComparer.Ordinal); foreach (ITemplateInfo templateInfo in templateGroup.OrderByDescending(x => x.Precedence)) { TemplateUsageInformation usageInformation = TemplateUsageHelp.GetTemplateUsageInformation(templateInfo, environmentSettings, commandInput, hostDataLoader, templateCreator); HostSpecificTemplateData hostSpecificTemplateData = hostDataLoader.ReadHostSpecificTemplateData(templateInfo); HashSet <string> parametersToExplicitlyHide = hostSpecificTemplateData?.HiddenParameterNames ?? new HashSet <string>(StringComparer.Ordinal); foreach (ITemplateParameter parameter in usageInformation.AllParameters.ParameterDefinitions) { //If the parameter has previously been encountered... if (parameterHidingDisposition.TryGetValue(parameter.Name, out bool isCurrentlyHidden)) { //...and it was hidden, but it's not hidden in this template in the group, // remove its hiding, otherwise leave it as is if (isCurrentlyHidden && !parametersToExplicitlyHide.Contains(parameter.Name)) { parameterHidingDisposition[parameter.Name] = false; } } else { //...otherwise, since this is the first time the parameter has been seen, // its hiding state should be used as the current disposition parameterHidingDisposition[parameter.Name] = parametersToExplicitlyHide.Contains(parameter.Name); } } if (firstInList) { invalidParametersForGroup = usageInformation.InvalidParameters.ToDictionary(x => x.Canonical, x => x); firstInList = false; } else { invalidParametersForGroup = InvalidParameterInfo.IntersectWithExisting(invalidParametersForGroup, usageInformation.InvalidParameters); } groupUserParamsWithInvalidValues.IntersectWith(usageInformation.UserParametersWithInvalidValues); // intersect because if the value is valid for any version, it's valid. groupHasPostActionScriptRunner |= usageInformation.HasPostActionScriptRunner; parameterSetsForAllTemplatesInGroup.Add(usageInformation.AllParameters); // If this template has name overrides (either long or short), it's opinionated. // If it's the first opinionated template about the param, use its variants. // Else this template is not opinionated, note its values if there aren't defaults for the param already. // At the end, anything in the default list that isn't in the opinionated list will get merged in. // TODO: write tests for this code (and the rest of this method while we're at it) foreach (KeyValuePair <string, IReadOnlyList <string> > canonicalAndVariants in usageInformation.VariantsForCanonicals) { if (hostSpecificTemplateData.LongNameOverrides.ContainsKey(canonicalAndVariants.Key) || hostSpecificTemplateData.ShortNameOverrides.ContainsKey(canonicalAndVariants.Key)) { // this template is opinionated about this parameter. If no previous template is opinionated about this param, use this template's variants. if (!groupVariantsForCanonicals.ContainsKey(canonicalAndVariants.Key)) { groupVariantsForCanonicals[canonicalAndVariants.Key] = canonicalAndVariants.Value; } } else { // this template is not opinionated about this parameter. If no previous template had defaults for this param, use this template's defaults. if (!defaultVariantsForCanonicals.ContainsKey(canonicalAndVariants.Key)) { defaultVariantsForCanonicals[canonicalAndVariants.Key] = canonicalAndVariants.Value; } } } // If any template says the user input value is the default, include it here. groupUserParamsWithDefaultValues.UnionWith(usageInformation.UserParametersWithDefaultValues); parametersToAlwaysShow.UnionWith(hostSpecificTemplateData.ParametersToAlwaysShow); } // aggregate the parameter variants foreach (KeyValuePair <string, IReadOnlyList <string> > defaultVariants in defaultVariantsForCanonicals) { if (!groupVariantsForCanonicals.ContainsKey(defaultVariants.Key)) { // there were no opinionated variants, take the preferred default. groupVariantsForCanonicals[defaultVariants.Key] = defaultVariants.Value; } } IParameterSet allGroupParameters = new TemplateGroupParameterSet(parameterSetsForAllTemplatesInGroup); string parameterErrors = InvalidParameterInfo.InvalidParameterListToString(invalidParametersForGroup.Values.ToList()); HashSet <string> parametersToHide = new HashSet <string>(parameterHidingDisposition.Where(x => x.Value).Select(x => x.Key), StringComparer.Ordinal); return(new TemplateGroupParameterDetails { AllParams = allGroupParameters, AdditionalInfo = parameterErrors, InvalidParams = groupUserParamsWithInvalidValues.ToList(), ExplicitlyHiddenParams = parametersToHide, GroupVariantsForCanonicals = groupVariantsForCanonicals, GroupUserParamsWithDefaultValues = groupUserParamsWithDefaultValues, HasPostActionScriptRunner = groupHasPostActionScriptRunner, ParametersToAlwaysShow = parametersToAlwaysShow, }); }
public CliTemplateNameSearchResult(ITemplateInfo template, PackInfo packInfo, HostSpecificTemplateData hostSpecificData) : base(template, packInfo) { HostSpecificTemplateData = hostSpecificData; }
internal bool TryGetHostDataForTemplateIdentity(string identity, out HostSpecificTemplateData hostData) { EnsureInitialized(); return(CliHostSpecificData.TryGetValue(identity, out hostData)); }