Friday, June 16, 2017

Moodle API .net Wrapper

Background

Working on a Moodle project, where I wanted to add and update content using Moodle API.
I couldn't find any .net wrappers to be able to talk to Moodle with a strict contract.

The Solution

To solve the issue I downloaded the Moodle API documentation. Wrote a piece of code that generated the proxy classes to connect to Moodle's Rest API using HttpClient.


The Nitty Gritty

The project (find download link below), uses the namespace Moodle.API.Wrapper.
It basically consists of two folders major namespaces "Moodle.API.Wrapper.Controllers" and "Moodle.API.Wrapper.Models".

The controllers namespace is then divided into further namespaces belong to each area as organized by the API documentation. Inside these namespaces there are classes again, as organized in the API documentation. The structure is identified through the function name's naming convention. e.g. the API call: core_competency_list_competency_frameworks is organized into Moodle.API.Wrapper.Controllers.Core namespace inside a class called Competency. The class then contains the function called ListCompetencyFramework. Note that the function names are converted to C# naming conventions.

The signature of the functions looks like this:


public CompetencyFrameworksModel ListCompetencyFrameworks(CompetencyFrameworksInputModel competencyFrameworksInputModel)
{
return Post<CompetencyFrameworksModel,CompetencyFrameworksInputModel>("core_competency_list_competency_frameworks", competencyFrameworksInputModel);
}

All the required parameters are converted to Model classes clearly identified by the keyword "InputModel" if the class is to be used as input to the API.

Each controller class is inherited from BaseController which contains the implementations for the Post method. The post method does all the heavy lifting of converting classes into rest format and vice versa.

Naturally, all the converted Models are found inside the Model namespace. To be able to make a call to the API using this wrapper, all you need to do is add a reference to the wrapper and use code like this:


var competencyController = new Moodle.API.Wrapper.Controllers.Competency();

competencyController.SetupController(securityToken, url,WriteProgress);

var frameworks = competencyController.ListCompetencyFrameworks(new Moodle.API.Wrapper.Models.Core.CompetencyFrameworksInputModel {
    context = new Moodle.API.Wrapper.Models.Core.ContextInputModel {
        // assign values here
    }
});

       
The securityToken variable expects the Moodle API token that has been generated through your Moodle Site Administration. The url is obviously the base url of your API. and the WriteProgress variable can be null or if you want to report progress back provide an instance of:

public Func<string, Task> WriteProgress;

Download Link


Click here to download the zip file.
Git Hub Repo here

TeamCity - Update app.config after build step

Background

A new testing initiative required that UI tests run nightly against staging environment. This is in addition to the UI tests already configured to run on each push to the branches. This requirement posed a challenge where the UI tests now required to be pointed to staging URL instead of localhost. Complicating the problem was the fact that VSTest does not allow passing parameters through command-line. Even though there is a way available to supply test run parameters through run settings file, but this was not practical in this case.

Luckily the UI tests were relying on application settings (app.config) to retrieve the base URL. However, the challenge of updating this URL for a specific build configuration inside TeamCity still existed.


The Quick Solution

Include a build step in configuration immediately after the build step to run a PowerShell script to update config file at path \TargetPathIncludingReleaseConfiguration\ProjectAssembly.dll.config. 
The update should replace the existing application setting with the new value.


The Nitty Gritty

The TeamCity server's build steps below:

The AppSettingReplace.ps1 is a modified version of the original script found here: http://stackoverflow.com/questions/37201731/change-value-in-app-config-within-teamcity/37204969 provided by Evolve Software Ltd.

The modified source code is below, 

param (
    [ValidateNotNullOrEmpty()]
    [string] $ConfigurationFile = $(throw "-ConfigurationFile is mandatory, please provide a value."),
    [ValidateNotNullOrEmpty()]
    [string] $ApplicationSetting = $(throw "-ApplicationSetting is mandatory, please provide a value."),
    [ValidateNotNullOrEmpty()]
    [string] $ApplicationSettingValue = $(throw "-ApplicationSettingValue is mandatory, please provide a value."),
 [ValidateNotNullOrEmpty()]
    [string] $ProjectSettingsNameSpace = $(throw "-ProjectSettingsNameSpace is mandatory, please provide a value.")
)

function Main() 
{
#sample value for $ProjectSettingsNameSpace = [ProjectNamespace].Properties.Settings
    $CurrentScriptVersion = "1.0"

    Write-Host "================== Config Transform - Version"$CurrentScriptVersion": START =================="

    # Log input variables passed in
    Log-Variables
    Write-Host

    try {
        $xml = [xml](get-content($ConfigurationFile))
        $conf = $xml.configuration.applicationSettings[$ProjectSettingsNameSpace]
  
        $conf.setting | foreach {
   if ($_.name -eq $ApplicationSetting) { $_.value = $ApplicationSettingValue } }
        $xml.Save($ConfigurationFile)
    } 
    catch [System.Exception] {
        Write-Output $_
        Exit 1
    }

    Write-Host "================== Config Transform - Version"$CurrentScriptVersion": END =================="
}

function Log-Variables
{
    Write-Host "ConfigurationFile: " $ConfigurationFile
    Write-Host "ApplicationSetting: " $ApplicationSetting
    Write-Host "ApplicationSettingValue: " $ApplicationSettingValue
    Write-Host "Computername:" (gc env:computername)
}

Main