Julmar.MSLearnDevOps 2.0.1-preview

This is a prerelease version of Julmar.MSLearnDevOps.
dotnet add package Julmar.MSLearnDevOps --version 2.0.1-preview
NuGet\Install-Package Julmar.MSLearnDevOps -Version 2.0.1-preview
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Julmar.MSLearnDevOps" Version="2.0.1-preview" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Julmar.MSLearnDevOps --version 2.0.1-preview
#r "nuget: Julmar.MSLearnDevOps, 2.0.1-preview"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Julmar.MSLearnDevOps as a Cake Addin
#addin nuget:?package=Julmar.MSLearnDevOps&version=2.0.1-preview&prerelease

// Install Julmar.MSLearnDevOps as a Cake Tool
#tool nuget:?package=Julmar.MSLearnDevOps&version=2.0.1-preview&prerelease

MSLearnDevOps library

MSLearn DevOps library

The MSLearnDevOps library extends the AzDOUtilities library and provides custom WorkItem wrappers for the primary WorkItems in the Microsoft Learn Azure DevOps project:

  • ModuleWorkItem
  • LearningPathWorkItem
  • AchievementWorkItem
  • BugWorkItem (extends the base one to add custom fields)
  • ContentPlanWorkItem
  • EpicWorkItem (extends the base one to add custom fields)
  • ModuleUpdateWorkItem
  • UserStoryWorkItem` (extends the base one to add custom fields)

There is an unlisted NuGet package you can use to add the library to any .NET Core application.

Install-Package Julmar.MSLearnDevOps -Version 2.0.1-preview

The library also provides some helper functions to quickly pull data out of Azure DevOps. Almost all of these are on the MSLearnHelpers class.

public static class MSLearnHelpers
{
	// Constant for the MS Learn DevOps board
    public const string Uri = "https://ceapex.visualstudio.com";
    // Constant for the MS Learn project
    public const string Project = "Microsoft Learn";

    // Retrieve a typed LINQ class for all three WorkItems
    public static IMSLearnDB Db(IAzureDevOpsService service = null);

    // Wrapper around the AzureDevOpsFactory.Create method which can provide an access token
    public static IAzureDevOpsService Create(string accessToken);
}

The two constants point to the Azure DevOps site and project. The two creation methods provide simpler alternatives to the AzureDevOpsFactory methods.

The Create method takes an access token, but you can pass null unlike the underlying factory. If you don't supply the access token, the system will look in your documents folder for a file named vsts-rw-key.txt. If that file is present, it will read the contents and use them as the access key. If the file doesn't exist, and no token was supplied, an exception is thrown.

The Db method takes an IAzureDevOpsService instance and retrieves a wrapper object around each of the custom WorkItem types:

public IAzureDevOpsService Service { get; }
public IOrderedQueryable<ModuleWorkItem> Modules { get; }
public IOrderedQueryable<LearningPathWorkItem> LearningPaths { get; }
public IOrderedQueryable<AchievementWorkItem> Achievements { get; }
public IOrderedQueryable<WorkItem> WorkItems { get; }

This can be used to easily query each type. For example:

var myModules = MSLearnHelpers.Db().Modules.Where(m => m.AssignedTo == "smmark@microsoft.com").ToList();

The WorkItems property will pull from all registered types - for example, in the below code fragment, the library will return a mixture of ModuleWorkItem and LearningPathWorkItem objects. You can use obj is ModuleWorkItem at runtime to see what got returned.

var closedModulesAndPaths = MSLearnHelpers.Db().WorkItems.Where(wi => 
				(wi.WorkItemType == ModuleWorkItem.Type || wi.WorkItemType == LearningPathWorkItem.Type)
				&& wi.State == WorkItemStatus.Closed).ToList();

Note: keep in mind these are IQueryable objects -- the query is built and executed when you enumerate the objects. This is important because if you enumerate it more than once, you will execute the query more than once. It's best to capture the results in a list or array unless you only plan to enumerate it a single time!

Notice a set of status constants are used above. These are also included in the library as simple string constants and define each of the allowed states for Modules, Paths, or Achievements. You can use strings as well, these just provide a nice Intellisense experience and avoid mistypes.

public static class WorkItemStatus
{
    public const string New = "New";
    public const string Proposed = "Proposed";
    public const string Approved = "Approved";
    public const string Design = "Design";
    public const string InProgress = "In Progress";
    public const string Blocked = "Blocked";
    public const string InReview = "In Review";
    public const string Test = "Test";
    public const string PrePublish = "Pre-publish";
    public const string Published = "Published";
    public const string Closed = "Closed";
    public const string Removed = "Removed";
    public const string Canceled = "Canceled";
    public const string Declined = "Declined";
}

LINQ helpers

The library also has a set of extension methods which can be applied to any query. These can be added as part of the fluent syntax to string together the query. The final AzDO query will be constructed with each of these constraints when it's executed.


// Ignore WorkItems that have the tag 'Needs Review' or 'Missing'
workItems.IgnoreMissing()

// Ignore modules or paths that have the Hidden flag set
workItems.IgnoreHidden()

// Ignore modules or paths in the learn-sandbox-pr or 'Other'
workItems.IgnoreTestRepos()

// Ignore the specified states
// If no states are supplied, it uses WorkItemStatus.Removed, WorkItemStatus.Canceled, WorkItemStatus.Declined
workItems.IgnoreStates("state1", "state2", ...)

// Return items ONLY in the given state(s)
workItems.OnlyStates("state1", "state2", ...)

Here's an example:

var db = MSLearnHelpers.Db();


var query = db.Modules.Where(m => m.AssignedTo.EndsWith("@microsoft.com"))
          .OnlyStates(WorkItemStatus.Closed, WorkItemStatus.PrePublish)
          .IgnoreMissing()
          .IgnoreTestRepos();

// Build the query and execute it.
var modules = query.ToList();

// This sends:
// SELECT [System.Id] FROM [WorkItems] WHERE [System.TeamProject = 'Microsoft Learn']
//     AND [System.AssignedTo] CONTAINS ('@microsoft.com')
//     AND [System.State] IN ('Closed', 'Pre-Publish')
//     AND NOT [System.Tags] CONTAINS ('Missing', 'Needs Review')
//     AND NOT [Custom.Repo] CONTAINS ('learn-sandbox-pr', 'Other')
//
// Then the collection is filtered with Where(m => m.AssignedTo.EndsWith("@microsoft.com"))
// to filter out bad work items because AzDO doesn't support 'EndsWith'.
Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.0.1-preview 140 3/28/2022

1.0 Initial release