Skip to content

wikibus/Argolis

Repository files navigation

graph icon

Argolis Build status NuGet version codecov.io codefactor

Hydra hypermedia controls for .NET Web applications written in Nancy.

Introduction

Hydra

From Hydra homepage

Hydra simplifies the development of interoperable, hypermedia-driven Web APIs

Hydra does it by defining (still in production though) a wide variety of hypermedia controls, which allow the implementation of resilient API clients. This resilience is achieved by relying on runtime API documentation and not out-of-band information and human-readable API descriptions.

For more information please see it's specification page.

Argolis

Argolis is a Super-Duper-Happy-Path towards documenting .NET web applications with Hydra. It struggles to be as simple as possible and have the least impact on your actual application logic:

The name

The name Argolis comes from the ancient (and modern) Greek province sometimes called The Argolid. Argolis is where the mythological monster Hydra lived. The monster's full name is Lernaean Hydra after the lake of Lerna, where it had it's lair.

Getting started with Argolis

TL;DR; To install add the Nuget package.

PM> Install-Package Argolis.Nancy

It is a meta-package, which pulls all components from their respective NuGet packages.

Currently only Nancy is supported, but the core library doesn't have a dependency and Web API or ServiceStack are definitely a possibility.

Basic usage

There are three steps requried to start using Agolis(.Nancy)

Implement IHydraDocumentationSettings

... and wire it up with Nancy.

public class HydraDocumentationSettings : IHydraDocumentationSettings
{
    public string DocumentationPath
    {
        get { return "api"; }
    }

    public IriRef EntryPoint
    {
        get { return (IriRef)"http://localhost:61186/entrypoint"; }
    }
}

public class ArgolisRegistrations : Registrations
{
    public ArgolisRegistrations(ITypeCatalog tc) : base(tc)
    {
        Register<IHydraDocumentationSettings>(new HydraDocumentationSettings());
    }
}

This will set up the hydra:entryPoint link and the route used to serve API Documentation.

Implement IDocumentedTypeSelector

This interface is used to discover Types, which should be used to produce SupportedClasses exposed by the API. There is an abstract class AssemblyAnnotatedTypeSelector, which will look for types annotated with [SupportedClass] in given assemblies.

public class AssembliesToScanForSupportedTypes : AssemblyAnnotatedTypeSelector
{
    protected override IEnumerable<Assembly> Assemblies
    {
        get { yield return typeof (Issue).Assembly; }
    }
}

Define your models

[SupportedClass("http://example.api/o#Issue")]
[Description("An issue reported by our users")]
public class Issue
{
    public string Id { get; set; }
        
    [JsonProperty("titel")]
    public string Title { get; set; }
        
    public string Content { get; set; }
        
    [Description("The number of people who liked this issue")]
    public int LikesCount { get; private set; }

    public bool IsResolved { get; set; }

    public User Submitter { get; set; }

    [Range("http://example.api/o#project")]
    public string ProjectId { get; set; }
    
    private JObject Context => new AutoContext<Issue>();
}

Mandatory parts are the [SupportedClass] attribute and a @context. Here it is defined directly in the class but it can also be provided by implementing IContextProvider and registering it with the container.

Please see the documentation of JsonLd.Entities for more details of how the JSON-LD context can be created.

Serve your ApiDocumentation

Requesting the configured route will return the hydra:ApiDocumentation (excerpt).

curl http://localhost:61186/api -H Accept:application/ld+json
{
  "@context": {
	// ...
  },
  "@id": "http://localhost:61186/api",
  "@type": "ApiDocumentation",
  "entrypoint": "http://localhost:61186/entrypoint",
  "supportedClass": [
    {
      "@id": "http://example.api/o#Issue",
      "@type": "Class",
      "description": "An issue reported by our users",
      "supportedProperty": [
        {
          "@type": "SupportedProperty",
          "description": "The title property",
          "property": {
            "@id": "http://example.api/o#Issue/titel",
            "@type": "rdf:Property",
            "range": "xsd:string"
          },
          "readable": true,
          "required": false,
          "title": "title",
          "writeable": true
        },
        {
          "@type": "SupportedProperty",
          "description": "The number of people who liked this issue",
          "property": {
            "@id": "http://example.api/o#Issue/likesCount",
            "@type": "rdf:Property",
            "range": "xsd:int"
          },
          "readable": true,
          "required": false,
          "title": "likesCount",
          "writeable": false
        },
        {
          "@type": "SupportedProperty",
          "description": "The dateCreated property",
          "property": {
            "@id": "http://example.api/o#Issue/dateCreated",
            "@type": "rdf:Property",
            "range": "xsd:dateTime"
          },
          "readable": true,
          "required": false,
          "title": "dateCreated",
          "writeable": false
        }
      ],
      "title": "Issue"
    }    
  ]
}

And any requested resource will include a Link header to the Api Documentation

HTTP/1.1 200 OK
Link: <http://localhost:61186/api>; rel="http://www.w3.org/ns/hydra/core#apiDocumentation"

Building

On Windows

To manage packages, Argolis uses Paket. To restore them, either install the VS extension or run Cake from PowerShell command line:

.\build.ps1 -Target Restore

On Mac (And Linux, presumably)

Argolis develops perfectly fine on Mac using dotnet and JetBrains Rider. However, the Cake script runner is PowerShell, which may or may not work great.

To restore packages install Paket from homebrew and run the native binary:

brew install paket
paket restore

Releasing

Use versionize to tag the next release.

dotnet tool install --global Versionize
versionize
git push --follow-tags origin master