PowerShell Navigation Provider for Coral8

A couple of months ago I put together a quick PowerShell based stream reader for Coral8.  

While it was fun to write, beyond demos, it didn't see much use - lack of easy discoverability about which streams were available meant that it was easier to use the Coral8 studio.

So today I have a fix for that, a Coral8 navigation provider. Although it's not nearly fully featured, it's enough to get started.

 

This implementation takes advantage of the previous PowerShell script to do much of its work.  Although it's interesting to see how easy it is to "shell out" to augment the capabilities of c#, I don't see myself doing much more of that style of coding in the future, as debugging the scripts from within C# is too difficult.

 

Here's how the list of workspaces are obtained. Most of the work is around the raw SOAP calls.

string script = string.Format("([xml]$input).Envelope.Body.{0}Response.return.'#text'",
elementNameNoNS);
return POSHelp.Invoke<string>(script, resp).Single();
public string GetManagerStatus()
{
return GetSOAPResponse(
"GetManagerStatus", "");
}
public IEnumerable<string> GetWorkspaces()
{
string ms = GetManagerStatus();
return POSHelp.Invoke<string>(
"([xml]$input).Status.CclWorkspaceInfo.Object | % { $_.Name}", ms);
}
IEnumerable<string> workspaces = C8core.GetWorkspaces();
workspaces.ForEach(wss => WriteItemObject(new WorkSpace() { Name = wss }, path, true));

Getting the list of streams within a project took a little bit more effort, requiring an examination of the workspace status stream

private IEnumerable<string> GetStreamNames(IEnumerable<PSObject> res,string workspace,string program)
{
    int markerCount =
0;
    string match =
"/Stream/" + workspace +"/" + program +"/";
    Dictionary<string, bool> found = new Dictionary<string, bool>();

    var psoTransform = from pso in res select new {
            MessageGroup  = (string) pso.Properties[
"MessageGroup"].Value,
            MessageName =  (string)pso.Properties[
"MessageName"].Value,
            ObjectId = (string)pso.Properties[
"ObjectId"].Value,
            ObjectId2 = (string)pso.Properties[
"ObjectID2"].Value,
    };

    foreach (var psoT in psoTransform)
    {
        if (psoT.MessageGroup ==
"CclApplicationInfo" && psoT.MessageName =="RunningTime")
        {
            if (markerCount++ >
1)
               yield break;
        }
        if (psoT.MessageGroup ==
"CclStreamPairInfo") { string[] objecIDPair = { psoT.ObjectId, psoT.ObjectId2 };
            foreach (string objid in objecIDPair)
            {
                var uri = new Uri(objid);
                if (uri.PathAndQuery.StartsWith(match) && !found.ContainsKey(uri.PathAndQuery))
                {
                    found[uri.PathAndQuery] = true;
                    yield return uri.PathAndQuery.Substring(match.Length);
                }
            }
        }

    }
}

var urib = new UriBuilder(path.Replace(@"\", "/"));
urib.Path = string.Format(
"Status/Workspace/{0}", workspace);
IEnumerable<PSObject> res = C8core.OpenStream(urib.ToString());
GetStreamNames(res, workspace, program).ForEach(streamname =>
WriteItemObject(new C8Stream() { Name = streamname }, Path.Combine(path, streamname), true));

 

And reading the actual stream - in this case, C# is just a pass-through

 

static string C8StreamReaderScript = @"
($csv,$tblC) = $input | % { $_ }
$vals = $csv.Split(',')
$tblcc = $tblC.Count
if (!($vals.Count -eq $tblcc))
{
throw ""col count error $($vals.Count) -neq $tblcc ""
}
$o = new-object PSObject
for ($ii=0;$ii -lt $vals.Count;$ii++)
{
$t = $tblC[$ii].DataType
if ($t -eq [datetime])
{
$v = ([datetime]'1/1/1970 GMT').AddTicks( ([long]$vals[$ii]) * 10 )
}
else
{
$v = $vals[$ii] -as $t
}
$o | add-member -membertype noteproperty -name $tblC[$ii].ColumnName -value $v
}

$o
"
;

public IEnumerable<PSObject> OpenStream(string streamUrl)
{
using (WebClient wc = new WebClient())
{
var rurl = ResolveUrl(streamUrl);
DataTable tbl = GetTD(streamUrl);
var req = WebRequest.Create(rurl);
req.Headers.Add(
"X-C8-StreamFormat", "CSV");
req.Headers.Add(
"X-C8-StreamFormatOptions", "TitleRow=false");
req.Method =
"GET";
using (var resp = req.GetResponse())
using (var rdr = new System.IO.StreamReader(resp.GetResponseStream()))
do
{
var psres = POSHelp.Invoke(C8StreamReaderScript, rdr.ReadLine(), tbl.Columns);
foreach (PSObject pso in psres)
{
yield return pso;
}
} while (true);
}
}
IEnumerable<PSObject> resStream = C8core.OpenStream(streamurl);
resStream.ForEach(pso => WriteItemObject(pso, path, false));

Update:

one advantage the PowerShell provider has over Coral8 studio  is the ability to explore a remote server's stream.  With Coral8 studio if you connect to a remote server you can look at status information, but stream and window information is hidden.

Here's how to use the provider with a remote server:

>new-psdrive c8Remote Coral8 ccl://UATserver:6789

Name       Provider      Root
----       --------      ----
c8Remote   Coral8        ccl://UATserver:6789

> get-psdrive -psprovider Coral8

Name       Provider      Root
----       --------      ----
C8Local    Coral8        ccl://localhost:6789
c8Remote   Coral8        ccl://UATserver:6789

> ls c8Remote:\Default\Project1

Name
----
outAlert

...

> ls c8Remote:\Default\Project1\outAlert

Timestamp            : 8/13/2008 9:10:40 AM
EventName            : Desk.Execution.Buy
Id                   : 1218471879101908Desk37

...

No Comments