Candoumbe.Forms 0.3.0

dotnet add package Candoumbe.Forms --version 0.3.0                
NuGet\Install-Package Candoumbe.Forms -Version 0.3.0                
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="Candoumbe.Forms" Version="0.3.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Candoumbe.Forms --version 0.3.0                
#r "nuget: Candoumbe.Forms, 0.3.0"                
#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 Candoumbe.Forms as a Cake Addin
#addin nuget:?package=Candoumbe.Forms&version=0.3.0

// Install Candoumbe.Forms as a Cake Tool
#tool nuget:?package=Candoumbe.Forms&version=0.3.0                

FORMS

GitHub Main branch Status GitHub Develop branch Status Candoumbe.Forms codecov

Introduction

This project aims at describing forms using HTML like syntax and objects.

The idea behind this project is to give opportunity to APIs written in .NET to describe their endpoints (responses and inputs) in a "HTML like way" so clients could dynamically generate inputs to push data to a given endpoints.

Why

I know what you're thinking : "Open API and swagger already solve this !". Well, yes and no. Yes, the Open API specification defines how to describe an API from a global perspective (shape of resources, supported HTTP verbs, ...).

But consider the following use case : you are building an API that handle bank accounts.

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "name": {
      "type": "string"
    },
    "balance": {
      "type": "number"
    },
    "currency": {
      "type": "string"
    }
  },
  "required": [
    "id",
    "balance",
    "currency",
    "name"
  ]
}

When an API client reads an account

GET /accounts/{id}

the API would return an account with balance that is either :

  • balance > 0 : you can withdraw money or make a deposit.
  • balance <= 0 : you can only make a deposit.

How would you advertise that behavior to API consumers using only swagger ? As far as I can tell, there's no way to do this as what can be done is very business/context/state specific.

With Candoumbe.Forms, you have a set of classes to help you display that behavior when sending resources back to API clients. Modeled after the ION spec, it provides a set of classes that can be used in API reponses to convey additional meanings

  • Form : models a form that a client must comply to in order to submit data to create a new account

Given the following AccountModel

public record NewAccountModel(Guid Id, string Name, double InitialBalance);

the backend can "describe" the form for to use using the FormBuilder class as follow

FormBuilder<NewAccountModel> createAccountFormBuilder = new(new Link { Href = "url/where/data/will/be/sent", Method = "POST", Relations = ["create-form"] });

createAccountFormBuilder.AddField(static x => x.Id);
createAccountFormBuilder.AddField(static x => x.Name);
createAccountFormBuilder.AddField(static x => x.InitialBalance);

Form form = createAccountFormBuilder.Build();

would result in the following form (JSON representation)

{
  "fields": [
    {
      "label": "Id",
      "name": "Id"
    },
    {
      "label": "Name",
      "name": "Name"
    },
    {
      "type": "Integer",
      "label": "InitialBalance",
      "name": "InitialBalance"
    }
  ],
  "meta": {
    "href": "url/where/data/must/be/sent",
    "relations": [
      "create-form"
    ],
    "method": "POST",
    "template": false
  }
}

Given that json representation, any client could then build the form that could be used to create new accounts

using HTML

<form action="url/where/data/must/be/sent">
    <label for="Id">Id</label>
    <input type="text" id="Id" name="Id"/>

    <label for="Name">Name</label>
    <input type="text" id="Name" name="Name"/>

    <label for="InitialBalance">Id</label>
    <input type="text" id="InitialBalance" name="InitialBalance"/>

    <button type="submit">Create account</button>
    <button type="button">Cancel</button>
</form>

Same could have been achieved with exploring an Open API ?!?!

You are definitely right ! That's exactly one of the purpose for which Open API was designed.

But Candoumbe.Forms is that you can add more metadatas when describing a form

Going H.A.T.E.O.S.

One of the fundamental concept of HATEOS is to provide API responses that are self descriptive so that clients can discover the API without any prior knowledge. For an account resource with positive balance

{
  "item": {
    "id": "3bc34535dfde",
    "balance": 70,
    "currency": "$"
  },
  "links": [
    {
      "rel" : "deposit",
      "method": "POST",
      "href": "/accounts/3bc34535dfde/deposits"
    },
    {
      "rel" : "withdrawal",
      "method": "POST",
      "href": "/accounts/3bc34535dfde/withdrawals"
    }
  ]
}

whereas the following representation would be sent when balance < 0

{
  "item": {
    "id": "3bc34535dfde",
    "balance": -20,
    "currency": "$"
  },
  "links": [
    {
      "rel" : "deposit",
      "verb": "POST",
      "link": "/accounts/3bc34535dfde/deposits"
    }
  ]
}

Providing consistent and dynamic navigation and/or action links allow any client to "react" and adapt to the state of the any resource.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  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 is compatible.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos 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
0.3.0 413 12/28/2024
0.3.0-beta0005 2,301 4/16/2023
0.3.0-beta0004 334 4/16/2023
0.3.0-beta0001 136 3/24/2023

### 🚀 New features
• Added net8.0 TFM support
• Added support of DateOnly properties
### ⚠️ Breaking changes
• Changed Link.Relation from string to string[] [BREAKING]
• Dropped netstandard1.0 TFM support
• Dropped net5.0 TFM support
• Removed Ultimately dependency
• Changed IonResourceto a record (netstandard2.1• and net5+)
• Changed Formto a record (netstandard2.1• and net5+)
• Changed FormFieldto a record (netstandard2.1• and net5+)
• Changed Linkto a record (netstandard2.1• and net5+)
### 🧹 Housekeeping
• Updated Candoumbe.Pipelines to 0.12.1
• Updated Candoumbe.MiscUtilities to 0.14.0
• Improve unit tests readability

Full changelog at https://github.com/candoumbe/Forms/blob/main/CHANGELOG.md