SS12000.Client
0.3.1
dotnet add package SS12000.Client --version 0.3.1
NuGet\Install-Package SS12000.Client -Version 0.3.1
<PackageReference Include="SS12000.Client" Version="0.3.1" />
<PackageVersion Include="SS12000.Client" Version="0.3.1" />
<PackageReference Include="SS12000.Client" />
paket add SS12000.Client --version 0.3.1
#r "nuget: SS12000.Client, 0.3.1"
#:package SS12000.Client@0.3.1
#addin nuget:?package=SS12000.Client&version=0.3.1
#tool nuget:?package=SS12000.Client&version=0.3.1
SS12000 C# Client Library
This is a C# client library designed to simplify interaction with the SS12000 API, a standard for information exchange between school administration processes based on OpenAPI 3.x. The library utilizes HttpClient for efficient HTTP communication and System.Text.Json for JSON serialization/deserialization, providing a structured and asynchronous approach to interact with all the API's defined endpoints.
You can download your own personal copy of the SS12000 standard for free from here: sis.se.
Important
The SS12000 does not require the server to support all of the endpoints. You need to actually look at the server documentation to see which endpoints that are actually available with each service. Adding some sort of discovery service is beyond the scope of this small library in my humble opinion.
All dates are in the RFC 3339 format, we're not cavemen here.
Table of Contents
Installation
- Add the nuget package:
dotnet add package SS12000.Client
or start from scratch:
- Create a .NET Project: If you don't have one, create a new .NET project (e.g., Console App, Web API).
dotnet new console -n MySS12000App
cd MySS12000App
- Add the Client File: Place the SS12000Client.cs file directly into your project directory (e.g., MySS12000App/SS12000Client.cs).
- Add Necessary Usings: Ensure your project file (.csproj) includes the necessary framework references. The client uses System.Web for HttpUtility, which might require adding a package reference if you're not targeting a full .NET Framework. For .NET Core / .NET 5+, System.Web.HttpUtility is available via the System.Web.HttpUtility NuGet package.
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="6.0.0" />
<PackageReference Include="System.Web.HttpUtility" Version="1.0.0" />
</ItemGroup>
The package has only two real dependencies: Microsoft.AspNetCore.WebUtilities and System.Text.Json. Add them manually if they don't resolve automatically.
Usage
Initializing the Client
To start using the client, create an instance of SS12000Client. It's recommended to use using statement for proper disposal of the underlying HttpClient.
using SS12000.Client;
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
public class Program
{
public static async Task Main(string[] args)
{
const string baseUrl = "https://some.server.se/v2.0"; // Replace with your test server URL
const string authToken = "YOUR_JWT_TOKEN_HERE"; // Replace with your actual JWT token
// It's recommended to use HttpClientFactory in ASP.NET Core for managing HttpClient instances.
// For a simple console app, instantiating directly is fine, but ensure proper disposal.
using (var client = new SS12000Client(baseUrl, authToken))
{
await GetOrganizationData(client);
await GetPersonData(client);
await ManageSubscriptions(client);
}
}
// ... (methods below)
}
Fetching Organizations
+You can retrieve a list of organizations or a specific organization by its ID. Parameters are passed as typed method parameters (e.g. limit: int?, expand: List<string>, date filters as DateTime? — date-only params use "yyyy-MM-dd", date-time use RFC3339/"o"). See the API Reference and SS12000Client.cs XML docs for exact parameter names and formats.
public static async Task GetOrganizationData(SS12000Client client)
{
try
{
Console.WriteLine("\nFetching organizations...");
var organizations = await client.GetOrganisationsAsync(limit: 2);
Console.WriteLine("Fetched organizations:\n" + JsonSerializer.Serialize(organizations, new JsonSerializerOptions { WriteIndented = true }));
if (organizations.TryGetProperty("data", out var orgsArray) && orgsArray.ValueKind == JsonValueKind.Array && orgsArray.GetArrayLength() > 0)
{
var firstOrgId = orgsArray[0].GetProperty("id").GetString();
Console.WriteLine($"\nFetching organization with ID: {firstOrgId}...");
var orgById = await client.GetOrganisationByIdAsync(firstOrgId, true); // expandReferenceNames = true
Console.WriteLine("Fetched organization by ID:\n" + JsonSerializer.Serialize(orgById, new JsonSerializerOptions { WriteIndented = true }));
}
}
catch (HttpRequestException e)
{
Console.WriteLine($"An HTTP request error occurred fetching organization data: {e.Message}");
}
catch (Exception e)
{
Console.WriteLine($"An unexpected error occurred fetching organization data: {e.Message}");
}
}
Fetching Persons
Similarly, you can fetch persons and expand related data such as duties.
public static async Task GetPersonData(SS12000Client client)
{
try
{
Console.WriteLine("\nFetching persons...");
var persons = await client.GetPersonsAsync(limit: 2, expand: new List<string> { "duties" });
Console.WriteLine("Fetched persons:\n" + JsonSerializer.Serialize(persons, new JsonSerializerOptions { WriteIndented = true }));
if (persons.TryGetProperty("data", out var personsArray) && personsArray.ValueKind == JsonValueKind.Array && personsArray.GetArrayLength() > 0)
{
var firstPersonId = personsArray[0].GetProperty("id").GetString();
Console.WriteLine($"\nFetching person with ID: {firstPersonId}...");
var personById = await client.GetPersonByIdAsync(firstPersonId, new List<string> { "duties", "responsibleFor" }, true);
Console.WriteLine("Fetched person by ID:\n" + JsonSerializer.Serialize(personById, new JsonSerializerOptions { WriteIndented = true }));
}
}
catch (HttpRequestException e)
{
Console.WriteLine($"An HTTP request error occurred fetching person data: {e.Message}");
}
catch (Exception e)
{
Console.WriteLine($"An unexpected error occurred fetching person data: {e.Message}");
}
}
Fetch ...
Check the API reference below to see all available nodes.
Webhooks (Subscriptions)
The client provides asynchronous methods to manage subscriptions (webhooks).
public static async Task ManageSubscriptions(SS12000Client client)
{
try
{
Console.WriteLine("\nFetching subscriptions...");
var subscriptions = await client.GetSubscriptionsAsync();
Console.WriteLine("Fetched subscriptions:\n" + JsonSerializer.Serialize(subscriptions, new JsonSerializerOptions { WriteIndented = true }));
// Example: Create a subscription (requires a publicly accessible webhook URL)
// Console.WriteLine("\nCreating a subscription...");
// var newSubscription = await client.CreateSubscriptionAsync(new
// {
// name = "My Test Subscription",
// target = "http://your-public-webhook-url.com/ss12000-webhook", // Replace with your public URL
// resourceTypes = new[] { new { resource = "Person" }, new { resource = "Activity" } }
// });
// Console.WriteLine("Created subscription:\n" + JsonSerializer.Serialize(newSubscription, new JsonSerializerOptions { WriteIndented = true }));
// Example: Delete a subscription
// if (subscriptions.TryGetProperty("data", out var subsArray) && subsArray.ValueKind == JsonValueKind.Array && subsArray.GetArrayLength() > 0)
// {
// var subToDeleteId = subsArray[0].GetProperty("id").GetString();
// Console.WriteLine($"\nDeleting subscription with ID: {subToDeleteId}...");
// await client.DeleteSubscriptionAsync(subToDeleteId);
// Console.WriteLine("Subscription deleted successfully.");
// }
}
catch (HttpRequestException e)
{
Console.WriteLine($"An HTTP request error occurred managing subscriptions: {e.Message}");
}
catch (Exception e)
{
Console.WriteLine($"An unexpected error occurred managing subscriptions: {e.Message}");
}
}
API Reference
The SS12000Client class is designed to expose asynchronous methods for all SS12000 API endpoints. All methods return Task<JsonElement> for data retrieval or Task for operations without content (e.g., DELETE). Here is a list of the primary resource paths defined in the OpenAPI specification, along with their corresponding client methods:
- /organisations
- GetOrganisationsAsync
- LookupOrganisationsAsync
- GetOrganisationByIdAsync
- /persons
- GetPersonsAsync
- LookupPersonsAsync
- GetPersonByIdAsync
- /placements
- GetPlacementsAsync
- LookupPlacementsAsync
- GetPlacementByIdAsync
- /duties
- GetDutiesAsync
- LookupDutiesAsync
- GetDutyByIdAsync
- /groups
- GetGroupsAsync
- LookupGroupsAsync
- GetGroupByIdAsync
- /programmes
- GetProgrammesAsync
- LookupProgrammesAsync
- GetProgrammeByIdAsync
- /studyplans
- GetStudyPlansAsync
- LookupStudyPlansAsync
- GetStudyPlanByIdAsync
- /syllabuses
- GetSyllabusesAsync
- LookupSyllabusesAsync
- GetSyllabusByIdAsync
- /schoolUnitOfferings
- GetSchoolUnitOfferingsAsync
- LookupSchoolUnitOfferingsAsync
- GetSchoolUnitOfferingByIdAsync
- /activities
- GetActivitiesAsync
- LookupActivitiesAsync
- GetActivityByIdAsync
- /calendarEvents
- GetCalendarEventsAsync
- LookupCalendarEventsAsync
- GetCalendarEventByIdAsync
- /attendances
- GetAttendancesAsync
- CreateAttendanceAsync
- LookupAttendancesAsync
- GetAttendanceByIdAsync
- DeleteAttendanceAsync
- /attendanceEvents
- GetAttendanceEventsAsync
- CreateAttendanceEventAsync
- LookupAttendanceEventsAsync
- GetAttendanceEventByIdAsync
- DeleteAttendanceEventByIdAsync
- /attendanceSchedules
- GetAttendanceSchedulesAsync
- CreateAttendanceScheduleAsync
- LookupAttendanceSchedulesAsync
- GetAttendanceScheduleByIdAsync
- DeleteAttendanceScheduleByIdAsync
- /grades
- GetGradesAsync
- LookupGradesAsync
- GetGradeByIdAsync
- /absences
- GetAbsencesAsync
- CreateAbsenceAsync
- LookupAbsencesAsync
- GetAbsenceByIdAsync
- /aggregatedAttendance
- GetAggregatedAttendancesAsync
- /resources
- GetResourcesAsync
- LookupResourcesAsync
- GetResourceByIdAsync
- /rooms
- GetRoomsAsync
- LookupRoomsAsync
- GetRoomByIdAsync
- /subscriptions
- GetSubscriptionsAsync
- CreateSubscriptionAsync
- DeleteSubscriptionAsync
- GetSubscriptionByIdAsync
- UpdateSubscriptionAsync
- /deletedEntities
- GetDeletedEntitiesAsync
- /log
- CreateLogEntryAsync
- /statistics
- CreateStatisticsAsync
Detailed information on available parameters can be found in the XML documentation comments within SS12000Client.cs.
The .yaml file can be downloaded from the SS12000 site over at sis.se.
Webhook Receiver (ASP.NET Core Example)
To receive webhooks in a C# application, you would typically set up an ASP.NET Core Web API endpoint. This example demonstrates a basic controller for receiving SS12000 notifications.
This is just an example and is not part of the client library. It just shows how you could implement a receiver server for the webhooks. The code below is not production ready code, it's just a thought experiment that will point you in a direction toward a simple solution.
// Save this in a separate file, e.g., `Controllers/WebhookController.cs`
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
using System.Threading.Tasks;
namespace MySS12000App.Controllers
{
[ApiController]
[Route("[controller]")]
public class WebhookController : ControllerBase
{
// You might inject SS12000Client here if you need to make follow-up API calls
// private readonly SS12000Client _ss12000Client;
// public WebhookController(SS12000Client ss12000Client)
// {
// _ss12000Client = ss12000Client;
// }
/// <summary>
/// Webhook endpoint for SS12000 notifications.
/// </summary>
[HttpPost("ss12000-webhook")]
public async Task<IActionResult> ReceiveSS12000Webhook()
{
Console.WriteLine("Received a webhook from SS12000!");
foreach (var header in Request.Headers)
{
Console.WriteLine($"Header: {header.Key} = {string.Join(", ", header.Value)}");
}
try
{
using (var reader = new System.IO.StreamReader(Request.Body))
{
var jsonBody = await reader.ReadToEndAsync();
Console.WriteLine("Body:\n" + JsonSerializer.Serialize(JsonDocument.Parse(jsonBody).RootElement, new JsonSerializerOptions { WriteIndented = true }));
// Here you can implement your logic to handle the webhook message.
// E.g., save the information to a database, trigger an update, etc.
using (JsonDocument doc = JsonDocument.Parse(jsonBody))
{
if (doc.RootElement.TryGetProperty("modifiedEntites", out JsonElement modifiedEntitiesElement) && modifiedEntitiesElement.ValueKind == JsonValueKind.Array)
{
foreach (var resourceType in modifiedEntitiesElement.EnumerateArray())
{
Console.WriteLine($"Changes for resource type: {resourceType.GetString()}");
// You can call the SS12000Client here to fetch updated information
// depending on the resource type.
// Example: if (resourceType.GetString() == "Person") { await _ss12000Client.GetPersonsAsync(...); }
}
}
if (doc.RootElement.TryGetProperty("deletedEntities", out JsonElement deletedEntitiesElement) && deletedEntitiesElement.ValueKind == JsonValueKind.Array)
{
Console.WriteLine("There are deleted entities to fetch from /deletedEntities.");
// Call client.GetDeletedEntitiesAsync(...) to fetch the deleted IDs.
}
}
return Ok("Webhook received successfully!");
}
}
catch (JsonException ex)
{
Console.WriteLine($"Error parsing JSON webhook body: {ex.Message}");
return BadRequest("Invalid JSON body.");
}
catch (Exception ex)
{
Console.WriteLine($"Error processing webhook: {ex.Message}");
return StatusCode(500, $"Internal server error: {ex.Message}");
}
}
}
}
To enable this webhook endpoint in your ASP.NET Core application, ensure you have:
- Program.cs (or Startup.cs in older versions):
// In Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Optional: Register HttpClient and SS12000Client for dependency injection
// builder.Services.AddHttpClient();
// builder.Services.AddScoped<SS12000Client>(sp =>
// {
// var httpClient = sp.GetRequiredService<HttpClient>();
// var baseUrl = builder.Configuration["SS12000:BaseUrl"]; // Get from appsettings.json
// var authToken = builder.Configuration["SS12000:AuthToken"]; // Get from appsettings.json
// return new SS12000Client(baseUrl, authToken, httpClient);
// });
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseRouting();
app.UseAuthorization(); // If you have authentication/authorization
app.MapControllers(); // Maps controller endpoints
app.Run();
- appsettings.json (Optional, for configuration):
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "\*",
"SS12000": {
"BaseUrl": "https://some.server.se/v2.0",
"AuthToken": "YOUR_JWT_TOKEN_HERE"
}
}
- Run the application: dotnet run. Your webhook endpoint will typically be accessible at http://localhost:<port>/webhook/ss12000-webhook. Remember that for the SS12000 API to reach your webhook, it must be publicly accessible (e.g., through a reverse proxy or tunneling service like ngrok).
Contribute
Contributions are welcome! If you want to add, improve, optimize or just change things just send in a pull request and I will have a look. Found a bug and don't know how to fix it? Create an issue!
License
This project is licensed under the MIT License.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- Microsoft.AspNetCore.WebUtilities (>= 9.0.7)
- System.Text.Json (>= 6.0.10)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Initial release of the SS12000 C# Client Library.