I'm creating a Powershell module that acts as a wrapper for interfacing with a RESTful API.
In order to simplify things for the scripter, I provide this Cmdlet to "establish a connection" to the API (essentially create the Worker class object and specify the authentication token):
public class GetAPIConnection : Cmdlet
{
[Parameter(Position=0, Mandatory = true, ValueFromPipeline = true)]
[Alias("T","Password","Pwd","APIKey")]
public string Key { get; set; }
//This is the object I intend to send to the pipeline
private APIWorker API;
protected override void BeginProcessing()
{
base.BeginProcessing();
BuildOutputObject();
}
protected override void ProcessRecord()
{
base.ProcessRecord();
WriteObject(API);
}
private BuildOutputObject()
{
API = new APIWorker(APIKey);
}
The problem I'm having is that I want other cmdlets to be able to accept the object that is generated by Get-APIConnection from the pipeline. Here's an example of how I tried to implement that in another Cmdlet:
[Cmdlet(VerbsCommon.Get, @"SecurityGroups")]
[OutputType(typeof(string))]
public class GetSecurityGroups : Cmdlet
{
private APIWorker connection;
[Parameter(Position = 0,
Mandatory = true,
ValueFromPipeline = true)]
public APIWorker Connection
{
get { return connection; }
set
{
//Confirmation that set is actually being called
Console.WriteLine(@"Got here!");
connection = value;
}
}
[Parameter(Mandatory = true)]
public string Identity { get; set; }
private IEnumerable<string> Result;
protected override void BeginProcessing()
{
base.BeginProcessing();
BuildOutputObject();
}
protected override void ProcessRecord()
{
base.ProcessRecord();
WriteObject(Result);
}
private BuildOutputObject()
{
Result = Connection.GetUserGroups(Identity);
}
So, after I import the module, I am able to create the PoshAPI object just fine using the Get-APIConnection cmdlet:
>$API = Get-APIConnection -Key '[My Token]'
...And Get-SecurityGroups works if I pass $Connection as a named parameter:
>Get-SecurityGroups -Connection $API -Identity 'abcdefg'
Got here!
PowershellUsers
DotNetCOE
CompanyCarOwners
...
Notice that "Got here!" is sent to the screen indicating that set is indeed being called on the "Connection" parameter
...But, even though I specified the ValueFromPipeline attribute for the "Connection" parameter in "Get-SecurityGroups", I can't pass $API from the pipeline for some reason:
$API | Get-SecurityGroups -Identity 'abcdefg'
Get-SecurityGroups : Object reference not set to an instance of an object.
At line:1 char:7
+ $API | Get-SecurityGroups -Identity 'abcdefg'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-SecurityGroups], NullReferenceException
+ FullyQualifiedErrorId : System.NullReferenceException,APIHelper.GetSecurityGroups
The exception details indicate that the null reference exception is being thrown in BuildOutputObject():
> $error[0] | select *
PSMessageDetails :
Exception : System.NullReferenceException: Object reference not set to an instance of an object.
at APIHelper.GetSecurityGroups.BuildOutputObject()
at System.Management.Automation.Cmdlet.DoBeginProcessing()
at System.Management.Automation.CommandProcessorBase.DoBegin()
TargetObject :
CategoryInfo : NotSpecified: (:) [Get-SecurityGroups], NullReferenceException
FullyQualifiedErrorId : System.NullReferenceException,APIHelper.GetSecurityGroups
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at <ScriptBlock>, <No file>: line 1
PipelineIterationInfo : {}
It’s interesting to me that I was able to reach BuildOutputObject() at all given that Connection is a mandatory parameter, and set wasn’t called on it (hence no “Got here!”). Wouldn’t proper behavior have been to prompt me to define that parameter??
Any help would be greatly appreciated!