Files
sqltoolsservice/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQueryModel.tt
Aasim Khan c26a2aea14 Adding schema based object folders in OE (#1849)
* Making node types automated

* Adding schema based OE

* added folder types in NodeTypes

* Fixing stuff

* Moving schema to parent and cleaning up some code

* Replacing strings with nameof

* Sorting nodetypes generated by tt

* Adding option to put folders after nodes

* Fixing folder and children order

* Fixing tests

* Formatting file

* Formatting tt files

* Fixing tt

* fixing types

* Update src/Microsoft.SqlTools.ServiceLayer/SqlContext/ObjectExplorerSettings.cs

Co-authored-by: Charles Gagnon <chgagnon@microsoft.com>

* Fixing stuff

* Updating schema definitions and adding more logs

* Fixing copyright

* Adding Integration and Unit Tests

* Fixing test

---------

Co-authored-by: Charles Gagnon <chgagnon@microsoft.com>
2023-02-14 13:40:51 -08:00

377 lines
12 KiB
Plaintext

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Xml.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Globalization" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
// This file was generated by a T4 Template. Do not modify directly, instead update the SmoQueryModelDefinition.xml file
// and re-run the T4 template. This can be done in Visual Studio by right-click in and choosing "Run Custom Tool",
// or from the command-line on any platform by running "build.cmd -Target=CodeGen" or "build.sh -Target=CodeGen".
using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Smo.Broker;
using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes;
using Microsoft.SqlTools.Utility;
using Index = Microsoft.SqlServer.Management.Smo.Index;
namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
{
<#
var directory = Path.GetDirectoryName(Host.TemplateFile);
string xmlFile = Path.Combine(directory, "SmoQueryModelDefinition.xml");
/////////
// Now generate all the Query methods
/////////
var allNodes = GetNodes(xmlFile);
var indent = " ";
foreach (var nodeName in allNodes)
{
XmlElement nodeElement = GetNodeElement(xmlFile, nodeName);
IList<string> parents = GetParents(nodeElement, xmlFile, nodeName);
string nodeType = GetNodeType(nodeElement, nodeName);
var validFor = nodeElement.GetAttribute("ValidFor");
string queryBaseClass = "SmoQuerier";
PushIndent(indent);
WriteLine("");
WriteLine("[Export(typeof({0}))]", queryBaseClass);
WriteLine("internal partial class {0}Querier: {1}", nodeName, queryBaseClass);
WriteLine("{");
PushIndent(indent);
// Supported Types
WriteLine("Type[] supportedTypes = new Type[] { typeof("+ nodeType + ") };");
if (!string.IsNullOrWhiteSpace(validFor))
{
WriteLine("");
WriteLine("public override ValidForFlag ValidFor {{ get {{ return {0}; }} }}", GetValidForFlags(validFor));
WriteLine("");
}
WriteLine("");
WriteLine("public override Type[] SupportedObjectTypes { get { return supportedTypes; } }");
WriteLine("");
// Query impl
WriteLine("public override IEnumerable<SqlSmoObject> Query(SmoQueryContext context, string filter, bool refresh, IEnumerable<string> extraProperties)");
WriteLine("{");
PushIndent(indent);
WriteLine("Logger.Verbose(\"Begin query {0}\");", nodeType);
// TODO Allow override of the navigation path
foreach(var parentType in parents)
{
string parentVar = string.Format("parent{0}", parentType);
WriteLine("{0} {1} = context.Parent as {0};", parentType, parentVar);
WriteLine("if ({0} != null)", parentVar);
WriteLine("{");
PushIndent(indent);
WriteLine("Logger.Verbose(\"Parent of type `{0}` found\");",parentType);
XmlElement navPathElement = GetNavPathElement(xmlFile, nodeName, parentType);
string navigationPath = GetNavigationPath(nodeElement, nodeName, navPathElement);
string subField = GetNavPathAttribute(navPathElement, "SubField");
string fieldType = GetNavPathAttribute(navPathElement, "FieldType");
if(navPathElement != null){
/**
Adding runtime filters to the querier based on the property values available in the context.
The code below will go through all the filters in the navPath element and add them to the filter string
recieved by the querier.
**/
XmlNodeList runtimeFilters = navPathElement.GetElementsByTagName("Filter");
if(runtimeFilters.Count > 0)
{
WriteLine("List<NodePropertyFilter> filters = new List<NodePropertyFilter>();");
foreach(XmlElement filter in runtimeFilters)
{
string filterName = filter.GetAttribute("Property");
string filterField = filter.GetAttribute("Field");
string filterType = filter.GetAttribute("Type");
string filterValidFor = filter.GetAttribute("ValidFor");
WriteLine("filters.Add(new NodePropertyFilter()");
WriteLine("{");
PushIndent(indent);
WriteLine("Property = nameof({0}),", filterName);
WriteLine("Type = typeof({0}),", filterType);
WriteLine("Values = new List<object> {{ {0}.{1} }},", parentVar, filterField);
if(!string.IsNullOrWhiteSpace(filterValidFor))
{
WriteLine("ValidFor = {0},", GetValidForFlags(filterValidFor));
}
else
{
WriteLine("ValidFor = ValidForFlag.All ");
}
PopIndent();
WriteLine("});");
}
WriteLine("filter = INodeFilter.AddPropertyFilterToFilterString(filter, filters, this.GetType(), context.ValidFor);");
}
}
WriteLine("var retValue = {0}.{1};", parentVar, navigationPath);
WriteLine("if (retValue != null)");
WriteLine("{");
PushIndent(indent);
if (IsCollection(nodeElement))
{
WriteLine("retValue.ClearAndInitialize(filter, extraProperties);");
if (string.IsNullOrEmpty(subField) )
{
WriteLine("var ret = new SmoCollectionWrapper<{0}>(retValue).Where(c => PassesFinalFilters({1}, c));", nodeType, parentVar);
WriteLine("Logger.Verbose(\"End query {0}\");", nodeType);
WriteLine("return ret;");
}
else
{
WriteLine("List<{0}> subFieldResult = new List<{0}>();", nodeType);
WriteLine("foreach({0} field in retValue)", fieldType);
WriteLine("{");
PushIndent(indent);
WriteLine("{0} subField = field.{1};", nodeType, subField);
WriteLine("if (subField != null)");
WriteLine("{");
PushIndent(indent);
WriteLine("subFieldResult.Add(subField);");
PopIndent();
WriteLine("}");
PopIndent();
WriteLine("}");
WriteLine("var ret = subFieldResult.Where(c => PassesFinalFilters({1}, c));", nodeType, parentVar);
WriteLine("Logger.Verbose(\"End query {0}\");", nodeType);
WriteLine("return ret;");
}
}
else
{
WriteLine("if (refresh)");
WriteLine("{");
PushIndent(indent);
WriteLine("{0}.{1}.Refresh();", parentVar, navigationPath);
PopIndent();
WriteLine("}");
WriteLine("return new SqlSmoObject[] { retValue };");
}
PopIndent();
WriteLine("}");
PopIndent();
WriteLine("}"); // close If
}
WriteLine("return Enumerable.Empty<SqlSmoObject>();");
PopIndent();
WriteLine("}"); // close Query method
PopIndent();
WriteLine("}"); // close Class
PopIndent();
}
#>
}
<#+
public static string[] GetNodes(string xmlFile)
{
List<string> typesList = new List<string>();
XmlDocument doc = new XmlDocument();
doc.Load(xmlFile);
XmlNodeList treeTypes = doc.SelectNodes("/SmoQueryModel/Node");
if (treeTypes != null)
{
foreach (var type in treeTypes)
{
XmlElement element = type as XmlElement;
if (element != null)
{
typesList.Add(element.GetAttribute("Name"));
}
}
}
return typesList.ToArray();
}
public static XmlElement GetNodeElement(string xmlFile, string nodeName)
{
XmlDocument doc = new XmlDocument();
doc.Load(xmlFile);
return (XmlElement)doc.SelectSingleNode(string.Format("/SmoQueryModel/Node[@Name='{0}']", nodeName));
}
public static XmlElement GetNavPathElement(string xmlFile, string nodeName, string parent)
{
XmlDocument doc = new XmlDocument();
doc.Load(xmlFile);
XmlElement navPathElement = (XmlElement)doc.SelectSingleNode(string.Format("/SmoQueryModel/Node[@Name='{0}']/NavigationPath[@Parent='{1}']", nodeName, parent));
return navPathElement;
}
public static string GetNavPathAttribute(XmlElement navPathElement, string attributeName)
{
return navPathElement == null ? null : navPathElement.GetAttribute(attributeName);
}
public static string GetNavigationPath(XmlElement nodeElement, string nodeName, XmlElement navPathElement)
{
string navPathField = GetNavPathAttribute(navPathElement, "Field");
if (!string.IsNullOrEmpty(navPathField))
{
return navPathField;
}
// else use pluralized type as this is the most common scenario
string nodeType = GetNodeType(nodeElement, nodeName);
string nodeTypeAccessor = IsCollection(nodeElement) ? string.Format("{0}s", nodeType) : nodeType;
return nodeTypeAccessor;
}
public static string GetNodeType(XmlElement nodeElement, string nodeName)
{
var type = nodeElement.GetAttribute("Type");
if (!string.IsNullOrEmpty(type))
{
return type;
}
// Otherwise assume the type is the node name without "Sql" at the start
var prefix = "Sql";
return nodeName.IndexOf(prefix) == 0 ? nodeName.Substring(prefix.Length) : nodeName;
}
public static bool IsCollection(XmlElement nodeElement)
{
var collection = nodeElement.GetAttribute("Collection");
bool result;
if (bool.TryParse(collection, out result))
{
return result;
}
// Default is true
return true;
}
public static IList<string> GetParents(XmlElement nodeElement, string xmlFile, string parentName)
{
var parentAttr = nodeElement.GetAttribute("Parent");
if (!string.IsNullOrEmpty(parentAttr))
{
return new string[] { parentAttr };
}
var parentNodes = GetChildren(xmlFile, parentName, "Parent");
if (parentNodes != null && parentNodes.Count > 0)
{
List<string> parents = new List<string>();
foreach(var node in parentNodes)
{
parents.Add(node.InnerText);
}
return parents;
}
// default to assuming a type is under Database
return new string[] { "Database" };
}
public static List<XmlElement> GetChildren(string xmlFile, string parentName, string childNode)
{
XmlElement nodeElement = GetNodeElement(xmlFile, parentName);
XmlDocument doc = new XmlDocument();
doc.Load(xmlFile);
List<XmlElement> retElements = new List<XmlElement>();
XmlNodeList nodeList = doc.SelectNodes(string.Format("/SmoQueryModel/Node[@Name='{0}']/{1}", parentName, childNode));
foreach (var item in nodeList)
{
XmlElement itemAsElement = item as XmlElement;
if (itemAsElement != null)
{
retElements.Add(itemAsElement);
}
}
return retElements;
}
public static string GetValidForFlags(string validForStr)
{
List<string> flags = new List<string>();
if (validForStr.Contains("Sql2005"))
{
flags.Add("ValidForFlag.Sql2005");
}
if (validForStr.Contains("Sql2008"))
{
flags.Add("ValidForFlag.Sql2008");
}
if (validForStr.Contains("Sql2012"))
{
flags.Add("ValidForFlag.Sql2012");
}
if (validForStr.Contains("Sql2014"))
{
flags.Add("ValidForFlag.Sql2014");
}
if (validForStr.Contains("Sql2016"))
{
flags.Add("ValidForFlag.Sql2016");
}
if (validForStr.Contains("Sql2017"))
{
flags.Add("ValidForFlag.Sql2017");
}
if (validForStr.Contains("AzureV12"))
{
flags.Add("ValidForFlag.AzureV12");
}
if (validForStr.Contains("AllOnPrem"))
{
flags.Add("ValidForFlag.AllOnPrem");
}
if (validForStr.Contains("AllAzure"))
{
flags.Add("ValidForFlag.AllAzure");
}
if (validForStr.Contains("NotSqlDw"))
{
flags.Add("ValidForFlag.NotSqlDw");
}
if (validForStr.Contains("SqlOnDemand"))
{
flags.Add("ValidForFlag.SqlOnDemand");
}
if (validForStr == "NotSqlDemand")
{
flags.Add("ValidForFlag.NotSqlDemand");
}
if (validForStr == "All")
{
flags.Add("ValidForFlag.All");
}
return string.Join("|", flags);
}
#>