/// <summary> /// Gets the chain of base types for a given type. /// The first item in the chain is the type that the given type directly inherits from. /// </summary> /// <param name="type">The type to evaluate</param> /// <returns>The chain of base types.</returns> public static IEnumerable <OdcmClass> GetBaseTypes(this OdcmType type) { if (type == null) { throw new ArgumentNullException(nameof(type)); } OdcmClass currentType = type.GetBaseType(); while (currentType != null) { yield return(currentType); currentType = currentType.GetBaseType(); } }
/// <summary> /// Create the processor for adding parameters to a cmdlet. /// </summary> /// <param name="cmdlet">The cmdlet</param> /// <param name="baseType">The type for which we should add parameters</param> /// <param name="addSwitchParametersForAbstractTypes">Whether or not to create a switch parameter for abstract types</param> /// <param name="sharedParameterSetName"> /// The name of the shared parameter set, or null if parameters shouldn't be added to the shared parameter set /// </param> public static void AddParametersForEntityProperties( this Cmdlet cmdlet, OdcmType baseType, Func <OdcmProperty, bool> isReadOnlyFunc, string sharedParameterSetName = null, bool addSwitchParameters = true, bool markAsPowerShellParameter = true, bool setBaseTypeParameterSetAsDefault = false, bool allowPipelineInputByName = true) { if (cmdlet == null) { throw new ArgumentNullException(nameof(cmdlet)); } if (baseType == null) { throw new ArgumentNullException(nameof(baseType)); } if (isReadOnlyFunc == null) { throw new ArgumentNullException(nameof(isReadOnlyFunc)); } // Don't try to add parameters for Edm types if (baseType.Namespace.Name.StartsWith("Edm")) { return; } // Track parameters as we visit each type // C# is case sensitive whereas PowerShell is not, so we need to use case-insensitive comparisons when deduping IDictionary <OdcmType, IEnumerable <string> > parameterNameLookup = new Dictionary <OdcmType, IEnumerable <string> >(); IDictionary <string, CmdletParameter> parameterLookup = new Dictionary <string, CmdletParameter>(StringComparer.OrdinalIgnoreCase); // Visit all derived types baseType.VisitAllDerivedTypes((OdcmClass @class) => { string parameterName = @class.Name; string parameterSetName = @class.FullName; // Determine if this is the only entity type for this cmdlet bool isTheOnlyType = (@class == baseType && [email protected]().Any()); // Create the parameter set for this type if it doesn't already exist CmdletParameterSet parameterSet = cmdlet.GetOrCreateParameterSet(parameterSetName); // Set this as the default parameter set if it's the only type if ((setBaseTypeParameterSetAsDefault && @class == baseType) || (isTheOnlyType && markAsPowerShellParameter)) { cmdlet.DefaultParameterSetName = parameterSet.Name; } // Add a switch parameter for this type if required if (addSwitchParameters && [email protected] && // don't add a switch for abstract types !isTheOnlyType) // if there is only 1 type, don't add a switch parameter for it { // Add the switch parameter parameterSet.Add(new CmdletParameter(parameterName, typeof(PS.SwitchParameter)) { Mandatory = true, ParameterSetSelectorName = parameterSetName, ValueFromPipelineByPropertyName = false, Documentation = new CmdletParameterDocumentation() { Descriptions = new string[] { $"A switch parameter for selecting the parameter set which corresponds to the \"{@class.FullName}\" type.", }, }, }); } // Evaluate the properties on this type IEnumerable <OdcmProperty> properties = @class.EvaluateProperties(@class == baseType) .Where(prop => prop.Name != RequestProperties.Id); // Add this type into the parmeter name lookup table parameterNameLookup.Add(@class, properties //.Where(prop => !prop.ReadOnly && !prop.IsCollection && !prop.IsLink) .Select(prop => prop.Name) .Distinct()); // Add the base types' properties as parameters to this parameter set // NOTE: Safe lookups are not necessary since all base types are guaranteed to have already been processed // by the VisitDerivedTypes() method OdcmType currentType = @class; while (currentType != baseType) { // Get the next type currentType = currentType.GetBaseType(); // Lookup the properties for this type IEnumerable <string> parameterNames = parameterNameLookup[currentType]; // Lookup the CmdletParameter objects for each parameter name IEnumerable <CmdletParameter> parameters = parameterNames.Select(paramName => parameterLookup[paramName]); // Add the parameters to the parameter set parameterSet.AddAll(parameters); } // Iterate over properties foreach (OdcmProperty property in properties) { // Get the PowerShell type for this property from the Edm type Type propertyType = property.Type.ToPowerShellType(property.IsCollection); // Create the parameter for this property if it doesn't already exist if (!parameterLookup.TryGetValue(property.Name, out CmdletParameter parameter)) { // Get the valid values if it is an enum IEnumerable <string> enumMembers = null; if (markAsPowerShellParameter && property.Type is OdcmEnum @enum) { enumMembers = @enum.Members.Select(enumMember => enumMember.Name); } parameter = CreateEntityParameter( property, propertyType, markAsPowerShellParameter, @class == baseType, @class.FullName, isReadOnly: isReadOnlyFunc(property), allowPipelineInputByName: allowPipelineInputByName, enumValues: enumMembers); parameterLookup.Add(property.Name, parameter); } else if (propertyType != parameter.Type) { // If all uses of this parameter don't use the same type, default to System.Object parameter = CreateEntityParameter( property, typeof(object), markAsPowerShellParameter, @class == baseType, @class.FullName, isReadOnly: isReadOnlyFunc(property), allowPipelineInputByName: allowPipelineInputByName); parameterLookup[property.Name] = parameter; } // Save the original OData type name parameter.ODataTypeFullName = property.Type.FullName; // Save the names of the subtypes of the original OData type parameter.ODataSubTypeFullNames = property.Type.GetAllDerivedTypes().Select(type => type.FullName); // Add this type's properties as parameters to this parameter set parameterSet.Add(parameter); } }); // Get/create the shared parameter set if required (e.g. the "Post" parameter set for the "Create" cmdlet) if (sharedParameterSetName != null) { // Create the parameter set if it doesn't already exist CmdletParameterSet sharedParameterSet = cmdlet.GetOrCreateParameterSet(sharedParameterSetName); // All properties should be part of the shared parameter set sharedParameterSet.AddAll(parameterLookup.Values); } }