Monday, January 30, 2012

Did you know? – Editing nodes with JsTree

For one of the Confluence plugins that I developed (Tags Plugin), I needed a client side tree. After a little research I decided to use jsTree. This is a jQuery plugin that is feature reach and so far I have been able to customize for my needs.

One of the features that I wanted was to allow users to edit a node on the tree when they double click it. It turn out to be super simple, first here the tree declaration (simplified from tag-macro.view.js):

treeContainer.jstree({
"json_data": {
"data": [ that.getTreeNodes() ]
},
"crrm":{
"input_width_limit":2000
},
"plugins": ["json_data", "ui", "crrm" ]
})

The important bit is to include the “crrm” plugin, I customized it to increase the size of the input field when the node is in edit mode. Once this is setup, putting a node into edit mode is as simple as:

$('.jstree-leaf').live('dblclick', function(){
treeContainer.jstree("rename");
});

Yeah, yeah, “live” is deprecated, but as far as I know the jQuery version provided by Confluence is not the lastest. This will auto-magically set the node to edit mode and listen for the ENTER key to trigger the “rename.jstree” event. So, how do you handle this event? You bind to the event when you declare the tree:

treeContainer.jstree({
//...
})
.bind("rename.jstree", function(e, data){
var oldText = data.rslt.old_name;
var newText = data.rslt.new_name;
//do your thing
});

If you need a client side tree, you may want to give jsTree a shot.
tags-test

Did you know? – NuGetGallery Build (part 7)

Now I feel like we took a big step forward, at the end of the previous post we kicked off the build process after all the packages where restored using Nuget.exe. Now, the regular “Build” target will compile the website project. What’s next? Well, let’s go back to NuGetGallery.msbuild (simplified):

<Project DefaultTargets="Build">
<UsingTask AssemblyFile="../3rdParty\xunit\xunit.runner.msbuild.dll" TaskName="Xunit.Runner.MSBuild.xunit" />

<Target Name="Build" DependsOnTargets="Clean">
<MSBuild Projects="..\NuGetGallery.sln" Targets="Build" Properties="Configuration=$(Configuration);CodeAnalysis=true;" />
</Target>

<Target Name="RunFacts" DependsOnTargets="Build">
<xunit Assembly="..\Facts\bin\$(Configuration)\NuGetGallery.Facts.dll" Xml="NuGetGallery.Facts.results.xml" />
</Target>

<Target Name="FullBuild" DependsOnTargets="RunFacts;UpdateDatabase" />
</Project>

Let’s assume that the “Build” target just finished. I am going to skip the “CodeAnalysis=true” that you see as part of the properties for the build and move to the next target in the sequence: “RunFacts”. This is where the unit tests are run and its pretty simple.

Since the solution also contains a NuGetGallery.Facts project this is included in the output of the build. All that happens in the “RunFacts” target is a call to the XUnit task passing the location of the assembly that contains the unit tests (NuGetGallery.Facts.dll). Where does this task come from? You can see the “UsingTask” directive at the top of the file that points to the assembly in the “3rdParty” directory that contains the task.

Could this be the reason why XUnit itself was not included to be downloaded using nuget.exe like the other packages? Perhaps MSBuild needs to load the task at the moment when is parsing the project file so it already needs to be there? Mmh…

Thursday, January 19, 2012

Did you know? – NuGetGallery Build (part 6)

Last post left off where NuGet.exe was used to download all packages before compiling the project. Let’s talk a little bit about conditions. If you remember from NuGet.targets, here is the target that restores the packages:

<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(RestoreCommand)"
LogStandardErrorAsError="true"
Condition="Exists('$(PackagesConfig)')" />
</Target>

I skipped over its DependsOnTargets=”CheckPrerequisites” attribute, this target is defined on the companion NuGet.settings.targets (in retrospect, I don’t know why there are two .target files where it seems that one would have sufficed):

<Target Name="CheckPrerequisites">
<!-- Raise an error if we're unable to locate nuget.exe -->
<Error Condition="!Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" />
</Target>

This is self explanatory, it will error if Nuget.exe was not found. Furthermore, the “RestorePackages” target will error if the packages.config file is not found.

PS: I did left off one piece of the puzzle: precompiling the razor files before building. This is defined in the RazorGenerator.targets which is also imported by Website.csproj.

Wednesday, January 18, 2012

Did you know? – NuGetGallery Build (part 5)

In the previous post we left off trying to figure out how NuGetGallery downloads its required packages during the build process. We arrived at the file that contains the targets to do this NuGet.targets (simplified):

<Project ToolsVersion="4.0">
<Import Project="NuGet.settings.targets"/>
<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(RestoreCommand)"
LogStandardErrorAsError="true"
Condition="Exists('$(PackagesConfig)')" />
</Target>
</Project>

Excellent, there’s the definition of a “RestorePackages” target that runs an Exec task which presumably will take care of downloading the packages. The “RestoreCommand” is defined in yet another targets file which is imported at the beginning, NuGet.settings.targets (simplified):

<Project ToolsVersion="4.0">
<PropertyGroup>
<NuGetToolsPath>$(SolutionDir).nuget</NuGetToolsPath>
<NuGetExePath>$(NuGetToolsPath)\nuget.exe</NuGetExePath>
<PackagesConfig>$(ProjectDir)packages.config</PackagesConfig>
<PackagesDir>$(SolutionDir)packages</PackagesDir>

<!-- Commands -->
<RestoreCommand>"$(NuGetExePath)" install "$(PackagesConfig)" -o "$(PackagesDir)"</RestoreCommand>

<!-- Make the build depend on restore packages -->
<BuildDependsOn Condition="$(RestorePackages) == 'true'">
RestorePackages;
$(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>
</Project>

A lot of things happening here:
- There is a property definition to the location of NuGet.exe (which is included in the source)
- There is a property definition to a packages.config, which contains the list of packages that it needs to download.
- There is a property definition to a directory where the packages will be downloaded into.
- Then there is a property definition for the actual restore command, which is a call to Nuget.exe with the “install” parameter.

The next line is probably the most important one, it’s where the target defined previously is glued into the build process. “BuildDependsOn” is a special MSBuild property used to override the sequence of steps during build. In this case it adds the “RestorePackages” target at the beginning.

Don’t get tripped up by the condition included in the “BuildDependsOn” which uses a property with the same name as the target “RestorePackages” (this confused me for a while). Basically we are only going to download the references if the “RestorePackages” property is set to true, which if you remember it was set at the beginning of Website.csproj.

Monday, January 16, 2012

Did you know? – NuGetGallery Build (part 4)

In the last post we left at the point where all previous build output from all projects was deleted. Let’s continue with the build from NuGetGallery.msbuild:

<Target Name="Build" DependsOnTargets="Clean">
<MSBuild Projects="..\NuGetGallery.sln" Targets="Build" Properties="Configuration=$(Configuration);CodeAnalysis=true;" />
</Target>

The msbuild task is invoked again, to run the Build target on the NuGetGallery.sln. This file references the “Website\Website.csproj”, MSBuild runs the Build target on this project.

Now, before it can compile anything it needs to download all the dependencies. Notice that NuGetGallery includes very few assemblies as part of the source, take a look at the “3rd Party” folder to see an unimpressive list of references. The trick is that it uses NuGet.exe to download all the extra references before the build. So how does it accomplish this magic? Let’s take a look at WebSite.csproj (simplified):

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build">
<PropertyGroup>
<RestorePackages>true</RestorePackages>
</PropertyGroup>

<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
</Project>

Mh, there’s a property called “RestorePackages” that sounds like it’s used to, I don’t know, restoring nuget packages? Remember this one. And at the bottom there is an import of NuGet.targets. That’s where the answers must be!

Saturday, January 14, 2012

Did you know? – NuGetGallery Build (part 3)

In the previous posts we saw how the NuGetGallery build got kicked off by Build-Solution.ps1, which internally went and grabbed the connection string from the website’s web.config using Get-ConnectionString.ps1. At the end of the file it finally calls msbuild.exe passing the NuGetGallery.msbuild file and the connection string (pay special attention to the /t:FullBuild at the end which defines which target to run):

$projFile = join-path $scriptPath Scripts\NuGetGallery.msbuild
& "$(get-content env:windir)\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" $projFile /p:DbConnection=$connectionString /t:FullBuild

Let’s peel another piece of the onion and look at this msbuild file (simplified):

<Project DefaultTargets="Build">
<Target Name="Clean">
<MSBuild Projects="..\NuGetGallery.sln" Targets="Clean" Properties="Configuration=$(Configuration)"/>
</Target>

<Target Name="Build" DependsOnTargets="Clean">
<MSBuild Projects="..\NuGetGallery.sln" Targets="Build" Properties="Configuration=$(Configuration);CodeAnalysis=true;" />
</Target>

<Target Name="RunFacts" DependsOnTargets="Build"> ... </Target>
<Target Name="FullBuild" DependsOnTargets="RunFacts;UpdateDatabase" />
</Project>


This is where it starts to wire up a list of targets to run during the build. As you can see, the “FullBuild” target is empty, but depends on the “RunFacts” and “UpdateDatabase” targets. Working our way back, the “RunFacts” depends on “Build”, which in turn depends on “Clean”.

Phew, maybe we can now do some work? All right, at this point it calls msbuild to run the “Clean” target on the NuGetGallery.sln file. I am going to skip how this part works, the important thing is that all the projects included in the solution will delete all their intermediate and final build outputs. Note: the “Clean” target is defined in “Microsoft.Common.targets” file which lives in your framework directory.

Friday, January 13, 2012

Did you know? – NuGetGallery RequestModels

A while back, while working on ASP.NET MVC, I learned to not pass the business models directly into my Views. Especially if they were Entity Framework entities. So I got in the habit of creating specialized ViewModels for this purpose.

But last week, while reading NuGetGallery’s source, I saw how they took this concept to the next step: RequestModels. You see, for the MVC applications that I wrote I was accustomed to re-using the ViewModel to capture data from the requests, my thinking was “well, I used the ViewModel to generate the response, it’s fitting that it should also be used to receive requests from the page that it created”.

RequestModels
I like the idea of further separating the concerns between the object that is used to create response and the one used to bind to the request. You’ll notice how the RequestModels have the validations from DataAnnotations that are to be used when binding to the request and only have the fields necessary to process each request.

For some reason, the application doesn’t use this extra separation all the time and some times receives the ViewModel (for example in the PackagesController.cs). An oversight?

I am going to play around with this way of organization in my own projects and see how it feels.

Friday, January 6, 2012

Did you know? – XUnit Theories

Another discovery while reading through the NuGet Gallery source. As I was taking a look at their tests I bumped into RequireRemoteHttpsAttributeFacts.cs and saw this attribute:

[Theory]
[InlineData(new object[] { "POST" })]
[InlineData(new object[] { "DELETE" })]
[InlineData(new object[] { "PUT" })]
[InlineData(new object[] { "head" })]
[InlineData(new object[] { "trace" })]
public void RequireHttpsAttributeReturns403IfNonGetRequest(string method)
{
...
}

Now, I realise this may be old news since XUnit Extensions has been around since at least 2008, but it was new to me. This works very similar to NUnit Testcase attribute, each of the InlineData will be passed to the test as an argument.

There are different data providers including in the xunit.extensions assembly:
1. ExcelData
2. InlineData
3. OleDbData
4. PropertyData
5. SqlServerData

An introduction blog post about xunit extensions can be found here (by Ben Hall).

Thursday, January 5, 2012

Did you know? – NuGetGallery Build (part 2)

To continue my previous post, let’s take a peek at Get-ConnectionString.ps1, because there is something in there that I had no idea you could do:
function Get-ConnectionString($configPath, $connectionStringName) 
{
  $config = [xml](get-content $configPath)
  
  $connectionString = ($config.configuration.connectionStrings.add | where { $_.name -eq $connectionStringName }).connectionString
  
  $connectionString = $connectionString.Replace("=", "%3D")
  $connectionString = $connectionString.Replace(";", "%3B")

  return $connectionString
}

Look at line #3, what’s up with that “[xml]” statement? Is it trying to cast the output of get-content as xml? That’s ok, I guess. But then the next line hits:
($config.configuration.connectionStrings.add | where { $_.name -eq $connectionStringName }).connectionString


It can traverse XML nodes and attributes like property objects?!? Kudos to you Mr. PowerShell. I have no idea how that [xml] works or what others are supported but I did a small experiment in the PowerShell REPL:
$foo = [xml] "<span></span>"
$foo.getType()

And it says that it is of type System.Xml.XmlDocument. The fact that you can traverse it and the way the author uses the where command-let to filter is awesome (was that you Drew? Kudos to you too man).

So now you know, maybe it will come handy some day.

Did you know? – NuGetGallery Build

Today I read over the NuGetGallery source and naturally, started with the first entry point according to the documentation: their build script.
Build-Solution.ps1
There is a lot of PowerShell stuff that I didn’t know about in this short file, so let's look at it line by line:
1. The param() function is used to process named parameters, so the connection string can be passed when calling into this script.
param($connectionString = "")

2. Next it gets the path to the directory that contains this script file and uses it to invoke another .ps1 file. Note that the “.” is used to set PowerShell to parse the line in command mode. We’ll look at what Get-ConnectionString.ps1 does some other time, for now just be aware that it is going to load a new function to be used.
$scriptPath = Split-Path $MyInvocation.MyCommand.Path
. (join-path $scriptPath Scripts\Get-ConnectionString.ps1)

3. This is where the function is used, note that the web.config file is passed to it, so apparently that script is capable of parsing the xml to retrieve the connection string.
if ($connectionString.Trim() -eq "") 
{
  $connectionString = Get-ConnectionString -configPath (join-path $scriptPath Website\web.config) -connectionStringName NuGetGallery
}

4. Finally msbuild is invoked. Three things to note:
-  it is not building the .sln file directly but instead a custom .msbuild file is used (take a look at that for some nice usage of targets).
-  it uses the get-content command let to retrieve the value of an environment variable for the windows directory
- note the “&” at the begnning of the line? This turns PowerShell into expression mode so that the string is evaluated as an expression. First time I ever saw that.
$projFile = join-path $scriptPath Scripts\NuGetGallery.msbuild
 
& "$(get-content env:windir)\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" $projFile /p:DbConnection=$connectionString /t:FullBuild

Pretty cool stuff, source file here.

Tuesday, January 3, 2012

Did you know? - WCF and ClientMessageInspector

Last month I got a feature request to add time logging to the Atlassian .NET SDK. I thought it would be simple until I ran into a peculiar JIRA bug: it turns out that the SOAP response to any call involving time tracking blows up miserably. Curse you JIRA!

After searching the inter webs, I found what the error is on the response and how to fix it. Now what I needed was a way to intercept the response from the server and fix it up before it got passed to the internals of WCF

ClientMessageInspector to the rescue!

First create one of these guys that can update the content of the message
class RemoteWorklogMessageInspector : IClientMessageInspector
{
    public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
    {
        // update the contents of the response and return a new one with the changes
    }

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        return null;
    }
}
Then you have to register it, which wasn’t totally intuitive. Next you need to create an EndpointBehaviour that adds the inspector to the ClientRuntime.
class RemoteWorklogPatchBehavior : IEndpointBehavior
{    
    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(new RemoteWorklogMessageInspector());
    }
    
    // other methods from the interface
}
Finally, add the behaviour to the endpoint of the service:
var jiraSoapServiceClient = new JiraSoapServiceClient(binding, endpoint);
jiraSoapServiceClient.Endpoint.Behaviors.Add(new RemoteWorklogPatchBehavior());

Now, whenever a SOAP request goes to JIRA, the response will be inspected and updated if necessary to fix that nasty bug.

Look here for the full source.

“Tags” plugin now available in plugins.atlassian.com

For those interested, the entry is available here.

Now I need to convince the admins to install it in our internal instance so that I can start using it for my test sessions.

- Federico