A lightweight, resilient .NET client for the Open Source Initiative (OSI) License API, with built‑in Dependency Injection (DI) extensions.
This repository contains:
- OpenSourceInitiative.LicenseApi — the core typed client to query OSI licenses (includes DI registration extensions)
- OpenSourceInitiative.LicenseApi.Example — a small console app demonstrating direct and DI usage
- OpenSourceInitiative.LicenseApi.Tests — unit and integration tests
- Fetches the OSI license catalog and extracts human‑readable license text from the HTML page of each license
- Keeps an in‑memory, thread‑safe cache once data is loaded; subsequent queries operate on the snapshot
- Fail‑safe networking: on errors, you get the last available snapshot (possibly empty) instead of exceptions bubbling up
- Async API with synchronous counterparts for convenient use across environments
- Server‑side filtering endpoints supported (name, keyword, steward, SPDX wildcard pattern)
- Strongly‑typed keyword filtering via
OsiLicenseKeywordenum
Core client:
dotnet add package OpenSourceInitiative.LicenseApiDependency Injection: built‑in via ServiceCollection extensions — no extra package needed.
Targets: net10.0, netstandard2.0 (broad platform compatibility)
using OpenSourceInitiative.LicenseApi.Clients;
using var http = new HttpClient { BaseAddress = new Uri("https://opensource.org/api/") }; // optional; defaults to this
await using var client = new OsiLicensesClient(http);
// Load all
var licenses = await client.GetAllLicensesAsync();
// Search by name or id (cached, case-insensitive)
var apache = await client.SearchAsync("Apache");
// Lookup by SPDX (sync/async)
var mit = client.GetBySpdx("MIT");
var mitAsync = await client.GetBySpdxAsync("MIT");
// Server-side filters
var byName = await client.GetLicensesByNameAsync("mit");
var bySteward = await client.GetLicensesByStewardAsync("eclipse-foundation");
var bySpdxPattern = await client.GetLicensesBySpdxPatternAsync("gpl*");
// Strongly-typed keyword filter
var popular = await client.GetLicensesByKeywordAsync(OpenSourceInitiative.LicenseApi.Enums.OsiLicenseKeyword.PopularStrongCommunity);using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OpenSourceInitiative.LicenseApi.Extensions;
using OpenSourceInitiative.LicenseApi.Interfaces;
var services = new ServiceCollection();
services.AddLogging(b => b.AddConsole());
services.AddOsiLicensesClient(o =>
{
// o.BaseAddress = new Uri("https://opensource.org/api/"); // optional (this is the default)
});
await using var provider = services.BuildServiceProvider();
var client = provider.GetRequiredService<IOsiLicensesClient>();
var all = await client.GetAllLicensesAsync();
var mit = await client.GetBySpdxAsync("MIT");
var search = client.Search("Apache"); // sync variantSee a runnable demo in OpenSourceInitiative.LicenseApi.Example.
The main abstraction is IOsiLicensesClient with both async and sync methods:
- Initialization/cache
Task InitializeAsync(CancellationToken)/void Initialize()IReadOnlyList<OsiLicense> Licenses— current immutable snapshot
- Catalog and search
Task<IReadOnlyList<OsiLicense>> GetAllLicensesAsync(...)Task<IReadOnlyList<OsiLicense>> SearchAsync(string query, ...)andIReadOnlyList<OsiLicense> Search(string query)Task<OsiLicense?> GetBySpdxAsync(string spdxId, ...)andOsiLicense? GetBySpdx(string spdxId)
- Server‑side filters (per OSI API)
GetLicensesByNameAsync(string name, ...)GetLicensesByKeywordAsync(string keyword, ...)andGetLicensesByKeywordAsync(OsiLicenseKeyword keyword, ...)GetLicensesByStewardAsync(string steward, ...)GetLicensesBySpdxPatternAsync(string spdxPattern, ...)(supports*wildcards)
OsiLicense (selected properties):
string Id— OSI unique identifierstring Name— human‑readable license namestring? SpdxId— SPDX id, e.g.,MIT,Apache-2.0string? VersionDateTime? SubmissionDate,string? SubmissionUrl,string? SubmitterNamebool Approved,DateTime? ApprovalDateList<string> StewardsList<OsiLicenseKeyword> Keywords— mapped from OSI keyword tokensOsiLicenseLinks Links— links to self page, public HTML page, and collectionstring LicenseText— extracted plaintext of the license HTML page
Keyword enum (OsiLicenseKeyword) covers OSI classifications, including:
PopularStrongCommunity, International, SpecialPurpose, NonReusable, Superseded, VoluntarilyRetired,
RedundantWithMorePopular, OtherMiscellaneous, Uncategorized.
When using AddOsiLicensesClient(...) you can configure:
BaseAddress— defaults tohttps://opensource.org/api/PrimaryHandlerFactory— supply a customHttpMessageHandler(e.g., for tests)
OpenSourceInitiative.LicenseApi.Example/Program.csdemonstrates direct and DI usage, search, SPDX lookup, and keyword filtering.
- Build locally:
dotnet build -c Release - Run tests:
dotnet test -c Release - CI: GitHub Actions builds on Ubuntu, Windows, and macOS with .NET SDK 9 and 10, runs tests with line coverage threshold and uploads Cobertura coverage artifacts.
Some integration tests hit the live OSI API (see OpenSourceInitiative.LicenseApi.Tests/Integration). They are
decorated to run only when the API is reachable.
OpenSourceInitiative.LicenseApi/— core library (see its README)OpenSourceInitiative.LicenseApi.Example/— runnable exampleOpenSourceInitiative.LicenseApi.Tests/— unit and integration tests
The project follows semantic versioning. Public API changes will result in a major version bump.
Issues and pull requests are welcome. If you contribute, please:
- Add or update tests
- Keep code style consistent with the surrounding code
- Ensure
dotnet testpasses (CI enforces coverage ≥ 75% lines) - Add yourself to the list of contributors in README.md
- Conform branching style to GitFlow
See also: CONTRIBUTING.md and our Code of Conduct.
MIT — see LICENSE.
- Data is provided by the Open Source Initiative (OSI) License API.
- Contributing guide: CONTRIBUTING.md
- Code of Conduct: CODE_OF_CONDUCT.md
- Security policy: SECURITY.md
- Support: SUPPORT.md
- Changelog: CHANGELOG.md
- Oliver Schantz (frequency403)