v5 API docs

This commit is contained in:
Mark McDowall 2025-01-27 16:27:42 -08:00
parent 5a47f34ef9
commit 553c4aeae1
No known key found for this signature in database
9 changed files with 637 additions and 20 deletions

View file

@ -1,12 +1,12 @@
name: 'API Docs'
name: "API Docs"
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * 1'
- cron: "0 0 * * 1"
push:
branches:
- develop
- v5-develop
paths:
- ".github/workflows/api_docs.yml"
- "docs.sh"
@ -46,7 +46,7 @@ jobs:
then
git commit -am 'Automated API Docs update' -m "ignore-downstream"
git push -f --set-upstream origin api-docs
curl -X POST -H "Authorization: Bearer ${{ secrets.OPENAPI_PAT }}" -H "Accept: application/vnd.github+json" https://api.github.com/repos/sonarr/sonarr/pulls -d '{"head":"api-docs","base":"develop","title":"Update API docs"}'
curl -X POST -H "Authorization: Bearer ${{ secrets.OPENAPI_PAT }}" -H "Accept: application/vnd.github+json" https://api.github.com/repos/sonarr/sonarr/pulls -d '{"head":"api-docs","base":"v5-develop","title":"Update API docs"}'
else
echo "No changes since last run"
fi

View file

@ -1,32 +1,35 @@
# How to Contribute #
# How to Contribute
We're always looking for people to help make Sonarr even better, there are a number of ways to contribute.
## Documentation ##
## Documentation
Setup guides, [FAQ](https://wiki.servarr.com/sonarr/faq), the more information we have on the [wiki](https://wiki.servarr.com/sonarr) the better.
## Development ##
## Development
### Tools required ###
- Visual Studio 2019 or higher (https://www.visualstudio.com/vs/). The community version is free and works (https://www.visualstudio.com/downloads/).
### Tools required
- Visual Studio 2019 or higher (https://www.visualstudio.com/vs/). The community version is free and works (https://www.visualstudio.com/downloads/).
- HTML/Javascript editor of choice (VS Code/Sublime Text/Webstorm/Atom/etc)
- [Git](https://git-scm.com/downloads)
- [NodeJS](https://nodejs.org/en/download/) (Node 10.X.X or higher)
- [Yarn](https://yarnpkg.com/)
### Getting started ###
### Getting started
1. Fork Sonarr
2. Clone the repository into your development machine. [*info*](https://docs.github.com/en/get-started/quickstart/fork-a-repo)
2. Clone the repository into your development machine. [_info_](https://docs.github.com/en/get-started/quickstart/fork-a-repo)
3. Install the required Node Packages `yarn install`
4. Start webpack to monitor your dev environment for any frontend changes that need post processing using `yarn start` command.
5. Build the project in Visual Studio, Setting startup project to `Sonarr.Console` and framework to `x86`
6. Debug the project in Visual Studio
7. Open http://localhost:8989
### Contributing Code ###
### Contributing Code
- If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/Sonarr/Sonarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first)
- Rebase from Sonarr's `develop` branch, don't merge
- Rebase from Sonarr's `v5-develop` branch, don't merge
- Make meaningful commits, or squash them
- Feel free to make a pull request before work is complete, this will let us see where its at and make comments/suggest improvements
- Reach out to us on our [forums](https://forums.sonarr.tv/), [subreddit](https://www.reddit.com/r/sonarr/), [discord](https://discord.gg/Ex7FmFK), or [IRC](https://web.libera.chat/?channels=#sonarr) if you have any questions
@ -35,8 +38,9 @@ Setup guides, [FAQ](https://wiki.servarr.com/sonarr/faq), the more information w
- One feature/bug fix per pull request to keep things clean and easy to understand
- Use 4 spaces instead of tabs, this should be the default for VS 2019 and WebStorm
### Pull Requesting ###
- Only make pull requests to develop (currently `develop`), never `main`, if you make a PR to master we'll comment on it and close it
### Pull Requesting
- Only make pull requests to the default branch (currently `v5-develop`), never `main`, if you make a PR to main we'll comment on it and close it
- You're probably going to get some comments or questions from us, they will be to ensure consistency and maintainability
- We'll try to respond to pull requests as soon as possible, if its been a day or two, please reach out to us, we may have missed it
- Each PR should come from its own [feature branch](http://martinfowler.com/bliki/FeatureBranch.html) not develop in your fork, it should have a meaningful branch name (what is being added/fixed)

11
docs.sh
View file

@ -3,13 +3,14 @@ set -e
FRAMEWORK="net8.0"
PLATFORM=$1
ARCHITECTURE="${2:-'default value'}"
if [ "$PLATFORM" = "Windows" ]; then
RUNTIME="win-x64"
RUNTIME="win-$ARCHITECTURE"
elif [ "$PLATFORM" = "Linux" ]; then
RUNTIME="linux-x64"
RUNTIME="linux-$ARCHITECTURE"
elif [ "$PLATFORM" = "Mac" ]; then
RUNTIME="osx-x64"
RUNTIME="osx-$ARCHITECTURE"
else
echo "Platform must be provided as first argument: Windows, Linux or Mac"
exit 1
@ -36,10 +37,10 @@ dotnet clean $slnFile -c Release
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
dotnet new tool-manifest
# dotnet new tool-manifests
dotnet tool install --version 6.6.2 Swashbuckle.AspNetCore.Cli
dotnet tool run swagger tofile --output ./src/Sonarr.Api.V3/openapi.json "$outputFolder/$FRAMEWORK/$RUNTIME/$application" v3 &
dotnet tool run swagger tofile --output ./src/Sonarr.Api.V5/openapi.json "$outputFolder/$FRAMEWORK/$RUNTIME/$application" v5 &
sleep 45

View file

@ -1,12 +1,14 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using DryIoc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@ -178,6 +180,37 @@ namespace NzbDrone.Host
});
c.DescribeAllParametersInCamelCase();
// Generate docs based on the controller's API version
c.DocInclusionPredicate((docName, apiDesc) =>
{
Type type = null;
if (apiDesc.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
{
type = controllerActionDescriptor.ControllerTypeInfo;
}
if (type == null)
{
return false;
}
var versions = new List<int>();
versions.AddRange(type
.GetCustomAttributes(true)
.OfType<VersionedApiControllerAttribute>()
.Select(attr => attr.Version));
versions.AddRange(type
.GetCustomAttributes(true)
.OfType<VersionedFeedControllerAttribute>()
.Select(attr => attr.Version));
// Return anything with no version or a matching version
return !versions.Any() || versions.Any(v => $"v{v}" == docName);
});
});
services

View file

@ -9,6 +9,7 @@ namespace Sonarr.Api.V3.Qualities
{
public Quality Quality { get; set; }
public string Title { get; set; }
public int Weight { get; set; }
public double? MinSize { get; set; }
public double? MaxSize { get; set; }
public double? PreferredSize { get; set; }
@ -28,6 +29,7 @@ namespace Sonarr.Api.V3.Qualities
Id = model.Id,
Quality = model.Quality,
Title = model.Title,
Weight = model.Weight,
MinSize = model.MinSize,
MaxSize = model.MaxSize,
PreferredSize = model.PreferredSize
@ -46,6 +48,7 @@ namespace Sonarr.Api.V3.Qualities
Id = resource.Id,
Quality = resource.Quality,
Title = resource.Title,
Weight = resource.Weight,
MinSize = resource.MinSize,
MaxSize = resource.MaxSize,
PreferredSize = resource.PreferredSize

View file

@ -10778,6 +10778,21 @@
},
"allowed": {
"type": "boolean"
},
"minSize": {
"type": "number",
"format": "double",
"nullable": true
},
"maxSize": {
"type": "number",
"format": "double",
"nullable": true
},
"preferredSize": {
"type": "number",
"format": "double",
"nullable": true
}
},
"additionalProperties": false

View file

@ -0,0 +1,551 @@
{
"openapi": "3.0.1",
"info": {
"title": "Sonarr",
"description": "Sonarr API docs - The v5 API docs apply to Sonarr v5 only.",
"license": {
"name": "GPL-3.0",
"url": "https://github.com/Sonarr/Sonarr/blob/develop/LICENSE"
},
"version": "5.0.0"
},
"servers": [
{
"url": "{protocol}://{hostpath}",
"variables": {
"protocol": {
"default": "http",
"enum": [
"http",
"https"
]
},
"hostpath": {
"default": "localhost:8989"
}
}
}
],
"paths": {
"/api/v5/series/lookup": {
"get": {
"tags": [
"SeriesLookup"
],
"parameters": [
{
"name": "term",
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"text/plain": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SeriesResource"
}
}
},
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SeriesResource"
}
}
},
"text/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SeriesResource"
}
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"AddSeriesOptions": {
"type": "object",
"properties": {
"ignoreEpisodesWithFiles": {
"type": "boolean"
},
"ignoreEpisodesWithoutFiles": {
"type": "boolean"
},
"monitor": {
"$ref": "#/components/schemas/MonitorTypes"
},
"searchForMissingEpisodes": {
"type": "boolean"
},
"searchForCutoffUnmetEpisodes": {
"type": "boolean"
}
},
"additionalProperties": false
},
"AlternateTitleResource": {
"type": "object",
"properties": {
"title": {
"type": "string",
"nullable": true
},
"seasonNumber": {
"type": "integer",
"format": "int32",
"nullable": true
},
"sceneSeasonNumber": {
"type": "integer",
"format": "int32",
"nullable": true
},
"sceneOrigin": {
"type": "string",
"nullable": true
},
"comment": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
},
"Language": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"name": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
},
"MediaCover": {
"type": "object",
"properties": {
"coverType": {
"$ref": "#/components/schemas/MediaCoverTypes"
},
"url": {
"type": "string",
"nullable": true
},
"remoteUrl": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
},
"MediaCoverTypes": {
"enum": [
"unknown",
"poster",
"banner",
"fanart",
"screenshot",
"headshot",
"clearlogo"
],
"type": "string"
},
"MonitorTypes": {
"enum": [
"unknown",
"all",
"future",
"missing",
"existing",
"firstSeason",
"lastSeason",
"latestSeason",
"pilot",
"recent",
"monitorSpecials",
"unmonitorSpecials",
"none",
"skip"
],
"type": "string"
},
"NewItemMonitorTypes": {
"enum": [
"all",
"none"
],
"type": "string"
},
"Ratings": {
"type": "object",
"properties": {
"votes": {
"type": "integer",
"format": "int32"
},
"value": {
"type": "number",
"format": "double"
}
},
"additionalProperties": false
},
"SeasonResource": {
"type": "object",
"properties": {
"seasonNumber": {
"type": "integer",
"format": "int32"
},
"monitored": {
"type": "boolean"
},
"statistics": {
"$ref": "#/components/schemas/SeasonStatisticsResource"
},
"images": {
"type": "array",
"items": {
"$ref": "#/components/schemas/MediaCover"
},
"nullable": true
}
},
"additionalProperties": false
},
"SeasonStatisticsResource": {
"type": "object",
"properties": {
"nextAiring": {
"type": "string",
"format": "date-time",
"nullable": true
},
"previousAiring": {
"type": "string",
"format": "date-time",
"nullable": true
},
"episodeFileCount": {
"type": "integer",
"format": "int32"
},
"episodeCount": {
"type": "integer",
"format": "int32"
},
"totalEpisodeCount": {
"type": "integer",
"format": "int32"
},
"sizeOnDisk": {
"type": "integer",
"format": "int64"
},
"releaseGroups": {
"type": "array",
"items": {
"type": "string"
},
"nullable": true
},
"percentOfEpisodes": {
"type": "number",
"format": "double",
"readOnly": true
}
},
"additionalProperties": false
},
"SeriesResource": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"title": {
"type": "string",
"nullable": true
},
"alternateTitles": {
"type": "array",
"items": {
"$ref": "#/components/schemas/AlternateTitleResource"
},
"nullable": true
},
"sortTitle": {
"type": "string",
"nullable": true
},
"status": {
"$ref": "#/components/schemas/SeriesStatusType"
},
"ended": {
"type": "boolean",
"readOnly": true
},
"profileName": {
"type": "string",
"nullable": true
},
"overview": {
"type": "string",
"nullable": true
},
"nextAiring": {
"type": "string",
"format": "date-time",
"nullable": true
},
"previousAiring": {
"type": "string",
"format": "date-time",
"nullable": true
},
"network": {
"type": "string",
"nullable": true
},
"airTime": {
"type": "string",
"nullable": true
},
"images": {
"type": "array",
"items": {
"$ref": "#/components/schemas/MediaCover"
},
"nullable": true
},
"originalLanguage": {
"$ref": "#/components/schemas/Language"
},
"remotePoster": {
"type": "string",
"nullable": true
},
"seasons": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SeasonResource"
},
"nullable": true
},
"year": {
"type": "integer",
"format": "int32"
},
"path": {
"type": "string",
"nullable": true
},
"qualityProfileId": {
"type": "integer",
"format": "int32"
},
"seasonFolder": {
"type": "boolean"
},
"monitored": {
"type": "boolean"
},
"monitorNewItems": {
"$ref": "#/components/schemas/NewItemMonitorTypes"
},
"useSceneNumbering": {
"type": "boolean"
},
"runtime": {
"type": "integer",
"format": "int32"
},
"tvdbId": {
"type": "integer",
"format": "int32"
},
"tvRageId": {
"type": "integer",
"format": "int32"
},
"tvMazeId": {
"type": "integer",
"format": "int32"
},
"tmdbId": {
"type": "integer",
"format": "int32"
},
"firstAired": {
"type": "string",
"format": "date-time",
"nullable": true
},
"lastAired": {
"type": "string",
"format": "date-time",
"nullable": true
},
"seriesType": {
"$ref": "#/components/schemas/SeriesTypes"
},
"cleanTitle": {
"type": "string",
"nullable": true
},
"imdbId": {
"type": "string",
"nullable": true
},
"titleSlug": {
"type": "string",
"nullable": true
},
"rootFolderPath": {
"type": "string",
"nullable": true
},
"folder": {
"type": "string",
"nullable": true
},
"certification": {
"type": "string",
"nullable": true
},
"genres": {
"type": "array",
"items": {
"type": "string"
},
"nullable": true
},
"tags": {
"uniqueItems": true,
"type": "array",
"items": {
"type": "integer",
"format": "int32"
},
"nullable": true
},
"added": {
"type": "string",
"format": "date-time"
},
"addOptions": {
"$ref": "#/components/schemas/AddSeriesOptions"
},
"ratings": {
"$ref": "#/components/schemas/Ratings"
},
"statistics": {
"$ref": "#/components/schemas/SeriesStatisticsResource"
},
"episodesChanged": {
"type": "boolean",
"nullable": true
}
},
"additionalProperties": false
},
"SeriesStatisticsResource": {
"type": "object",
"properties": {
"seasonCount": {
"type": "integer",
"format": "int32"
},
"episodeFileCount": {
"type": "integer",
"format": "int32"
},
"episodeCount": {
"type": "integer",
"format": "int32"
},
"totalEpisodeCount": {
"type": "integer",
"format": "int32"
},
"sizeOnDisk": {
"type": "integer",
"format": "int64"
},
"releaseGroups": {
"type": "array",
"items": {
"type": "string"
},
"nullable": true
},
"percentOfEpisodes": {
"type": "number",
"format": "double",
"readOnly": true
}
},
"additionalProperties": false
},
"SeriesStatusType": {
"enum": [
"continuing",
"ended",
"upcoming",
"deleted"
],
"type": "string"
},
"SeriesTypes": {
"enum": [
"standard",
"daily",
"anime"
],
"type": "string"
}
},
"securitySchemes": {
"X-Api-Key": {
"type": "apiKey",
"description": "Apikey passed as header",
"name": "X-Api-Key",
"in": "header"
},
"apikey": {
"type": "apiKey",
"description": "Apikey passed as query parameter",
"name": "apikey",
"in": "query"
}
}
},
"security": [
{
"X-Api-Key": [ ]
},
{
"apikey": [ ]
}
]
}

View file

@ -15,6 +15,7 @@ namespace Sonarr.Http
Resource = resource;
Template = $"api/v{version}/{resource}";
PolicyName = API_CORS_POLICY;
Version = version;
}
public string Resource { get; }
@ -22,6 +23,7 @@ namespace Sonarr.Http
public int? Order => 2;
public string Name { get; set; }
public string PolicyName { get; set; }
public int Version { get; set; }
}
public class V3ApiControllerAttribute : VersionedApiControllerAttribute

View file

@ -24,4 +24,12 @@ namespace Sonarr.Http
{
}
}
public class V5FeedControllerAttribute : VersionedFeedControllerAttribute
{
public V5FeedControllerAttribute(string resource = "[controller]")
: base(5, resource)
{
}
}
}