Add annotations to swagger + more ASF API breaking changes that @Aareksio is ready for

This commit is contained in:
JustArchi
2018-10-08 00:09:30 +02:00
parent b3c3db30fa
commit cd28845d7b
9 changed files with 77 additions and 18 deletions

View File

@@ -56,6 +56,7 @@
<PackageReference Include="NLog.Web.AspNetCore" Version="4.7.0" /> <PackageReference Include="NLog.Web.AspNetCore" Version="4.7.0" />
<PackageReference Include="protobuf-net" Version="3.0.0-alpha.3" /> <PackageReference Include="protobuf-net" Version="3.0.0-alpha.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="3.0.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="3.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="3.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'"> <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">

View File

@@ -27,16 +27,21 @@ using ArchiSteamFarm.IPC.Requests;
using ArchiSteamFarm.IPC.Responses; using ArchiSteamFarm.IPC.Responses;
using ArchiSteamFarm.Localization; using ArchiSteamFarm.Localization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
namespace ArchiSteamFarm.IPC.Controllers.Api { namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController] [ApiController]
[Produces("application/json")] [Produces("application/json")]
[Route("Api/ASF")] [Route("Api/ASF")]
[SwaggerResponse(400, "The request has failed, check " + nameof(GenericResponse.Message) + " from response body for actual reason. Most of the time this is ASF, understanding the request, but refusing to execute it due to provided reason.", typeof(GenericResponse))]
[SwaggerResponse(401, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set, but you've failed to authenticate. See " + "https://github.com/" + SharedInfo.GithubRepo + "/wiki/IPC#authentication.")]
[SwaggerResponse(403, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set and you've failed to authenticate too many times, try again in an hour. See " + "https://github.com/" + SharedInfo.GithubRepo + "/wiki/IPC#authentication.")]
public sealed class ASFController : ControllerBase { public sealed class ASFController : ControllerBase {
/// <summary> /// <summary>
/// Fetches common info related to ASF as a whole. /// Fetches common info related to ASF as a whole.
/// </summary> /// </summary>
[HttpGet] [HttpGet]
[SwaggerResponse(200, type: typeof(GenericResponse<ASFResponse>))]
public ActionResult<GenericResponse<ASFResponse>> ASFGet() { public ActionResult<GenericResponse<ASFResponse>> ASFGet() {
uint memoryUsage = (uint) GC.GetTotalMemory(false) / 1024; uint memoryUsage = (uint) GC.GetTotalMemory(false) / 1024;
@@ -53,7 +58,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// <summary> /// <summary>
/// Updates ASF's global config. /// Updates ASF's global config.
/// </summary> /// </summary>
[Consumes("application/json")]
[HttpPost] [HttpPost]
[SwaggerResponse(200, type: typeof(GenericResponse))]
public async Task<ActionResult<GenericResponse>> ASFPost([FromBody] ASFRequest request) { public async Task<ActionResult<GenericResponse>> ASFPost([FromBody] ASFRequest request) {
if (request == null) { if (request == null) {
ASF.ArchiLogger.LogNullError(nameof(request)); ASF.ArchiLogger.LogNullError(nameof(request));
@@ -81,6 +88,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Makes ASF shutdown itself. /// Makes ASF shutdown itself.
/// </summary> /// </summary>
[HttpPost("Exit")] [HttpPost("Exit")]
[SwaggerResponse(200, type: typeof(GenericResponse))]
public ActionResult<GenericResponse> ExitPost() { public ActionResult<GenericResponse> ExitPost() {
(bool success, string output) = Actions.Exit(); (bool success, string output) = Actions.Exit();
return Ok(new GenericResponse(success, output)); return Ok(new GenericResponse(success, output));
@@ -90,6 +98,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Makes ASF restart itself. /// Makes ASF restart itself.
/// </summary> /// </summary>
[HttpPost("Restart")] [HttpPost("Restart")]
[SwaggerResponse(200, type: typeof(GenericResponse))]
public ActionResult<GenericResponse> RestartPost() { public ActionResult<GenericResponse> RestartPost() {
(bool success, string output) = Actions.Restart(); (bool success, string output) = Actions.Restart();
return Ok(new GenericResponse(success, output)); return Ok(new GenericResponse(success, output));
@@ -99,6 +108,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Makes ASF update itself. /// Makes ASF update itself.
/// </summary> /// </summary>
[HttpPost("Update")] [HttpPost("Update")]
[SwaggerResponse(200, type: typeof(GenericResponse<Version>))]
public async Task<ActionResult<GenericResponse<Version>>> UpdatePost() { public async Task<ActionResult<GenericResponse<Version>>> UpdatePost() {
(bool success, Version version) = await Actions.Update().ConfigureAwait(false); (bool success, Version version) = await Actions.Update().ConfigureAwait(false);
return Ok(new GenericResponse<Version>(success, version)); return Ok(new GenericResponse<Version>(success, version));

View File

@@ -29,16 +29,21 @@ using ArchiSteamFarm.IPC.Requests;
using ArchiSteamFarm.IPC.Responses; using ArchiSteamFarm.IPC.Responses;
using ArchiSteamFarm.Localization; using ArchiSteamFarm.Localization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
namespace ArchiSteamFarm.IPC.Controllers.Api { namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController] [ApiController]
[Produces("application/json")] [Produces("application/json")]
[Route("Api/Bot")] [Route("Api/Bot")]
[SwaggerResponse(400, "The request has failed, check " + nameof(GenericResponse.Message) + " from response body for actual reason. Most of the time this is ASF, understanding the request, but refusing to execute it due to provided reason.", typeof(GenericResponse))]
[SwaggerResponse(401, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set, but you've failed to authenticate. See " + "https://github.com/" + SharedInfo.GithubRepo + "/wiki/IPC#authentication.")]
[SwaggerResponse(403, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set and you've failed to authenticate too many times, try again in an hour. See " + "https://github.com/" + SharedInfo.GithubRepo + "/wiki/IPC#authentication.")]
public sealed class BotController : ControllerBase { public sealed class BotController : ControllerBase {
/// <summary> /// <summary>
/// Deletes all files related to given bots. /// Deletes all files related to given bots.
/// </summary> /// </summary>
[HttpDelete("{botNames:required}")] [HttpDelete("{botNames:required}")]
[SwaggerResponse(200, type: typeof(GenericResponse))]
public async Task<ActionResult<GenericResponse>> BotDelete(string botNames) { public async Task<ActionResult<GenericResponse>> BotDelete(string botNames) {
if (string.IsNullOrEmpty(botNames)) { if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames)); ASF.ArchiLogger.LogNullError(nameof(botNames));
@@ -58,24 +63,27 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Fetches common info related to given bots. /// Fetches common info related to given bots.
/// </summary> /// </summary>
[HttpGet("{botNames:required}")] [HttpGet("{botNames:required}")]
public ActionResult<GenericResponse<HashSet<Bot>>> BotGet(string botNames) { [SwaggerResponse(200, type: typeof(GenericResponse<IDictionary<string, Bot>>))]
public ActionResult<GenericResponse<IDictionary<string, Bot>>> BotGet(string botNames) {
if (string.IsNullOrEmpty(botNames)) { if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames)); ASF.ArchiLogger.LogNullError(nameof(botNames));
return BadRequest(new GenericResponse<HashSet<Bot>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames)))); return BadRequest(new GenericResponse<IDictionary<string, Bot>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames))));
} }
HashSet<Bot> bots = Bot.GetBots(botNames); HashSet<Bot> bots = Bot.GetBots(botNames);
if (bots == null) { if (bots == null) {
return BadRequest(new GenericResponse<HashSet<Bot>>(false, string.Format(Strings.ErrorIsInvalid, nameof(bots)))); return BadRequest(new GenericResponse<IDictionary<string, Bot>>(false, string.Format(Strings.ErrorIsInvalid, nameof(bots))));
} }
return Ok(new GenericResponse<HashSet<Bot>>(bots)); return Ok(new GenericResponse<IDictionary<string, Bot>>(bots.ToDictionary(bot => bot.BotName, bot => bot)));
} }
/// <summary> /// <summary>
/// Updates bot config of given bot. /// Updates bot config of given bot.
/// </summary> /// </summary>
[Consumes("application/json")]
[HttpPost("{botName:required}")] [HttpPost("{botName:required}")]
[SwaggerResponse(200, type: typeof(GenericResponse))]
public async Task<ActionResult<GenericResponse>> BotPost(string botName, [FromBody] BotRequest request) { public async Task<ActionResult<GenericResponse>> BotPost(string botName, [FromBody] BotRequest request) {
if (string.IsNullOrEmpty(botName) || (request == null)) { if (string.IsNullOrEmpty(botName) || (request == null)) {
ASF.ArchiLogger.LogNullError(nameof(botName) + " || " + nameof(request)); ASF.ArchiLogger.LogNullError(nameof(botName) + " || " + nameof(request));
@@ -113,6 +121,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Removes BGR output files of given bots. /// Removes BGR output files of given bots.
/// </summary> /// </summary>
[HttpDelete("{botNames:required}/GamesToRedeemInBackground")] [HttpDelete("{botNames:required}/GamesToRedeemInBackground")]
[SwaggerResponse(200, type: typeof(GenericResponse))]
public async Task<ActionResult<GenericResponse>> GamesToRedeemInBackgroundDelete(string botNames) { public async Task<ActionResult<GenericResponse>> GamesToRedeemInBackgroundDelete(string botNames) {
if (string.IsNullOrEmpty(botNames)) { if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames)); ASF.ArchiLogger.LogNullError(nameof(botNames));
@@ -132,15 +141,16 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Fetches BGR output files of given bots. /// Fetches BGR output files of given bots.
/// </summary> /// </summary>
[HttpGet("{botNames:required}/GamesToRedeemInBackground")] [HttpGet("{botNames:required}/GamesToRedeemInBackground")]
public async Task<ActionResult<GenericResponse<Dictionary<string, GamesToRedeemInBackgroundResponse>>>> GamesToRedeemInBackgroundGet(string botNames) { [SwaggerResponse(200, type: typeof(GenericResponse<IReadOnlyDictionary<string, GamesToRedeemInBackgroundResponse>>))]
public async Task<ActionResult<GenericResponse<IReadOnlyDictionary<string, GamesToRedeemInBackgroundResponse>>>> GamesToRedeemInBackgroundGet(string botNames) {
if (string.IsNullOrEmpty(botNames)) { if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames)); ASF.ArchiLogger.LogNullError(nameof(botNames));
return BadRequest(new GenericResponse<Dictionary<string, GamesToRedeemInBackgroundResponse>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames)))); return BadRequest(new GenericResponse<IReadOnlyDictionary<string, GamesToRedeemInBackgroundResponse>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames))));
} }
HashSet<Bot> bots = Bot.GetBots(botNames); HashSet<Bot> bots = Bot.GetBots(botNames);
if ((bots == null) || (bots.Count == 0)) { if ((bots == null) || (bots.Count == 0)) {
return BadRequest(new GenericResponse<Dictionary<string, GamesToRedeemInBackgroundResponse>>(false, string.Format(Strings.BotNotFound, botNames))); return BadRequest(new GenericResponse<IReadOnlyDictionary<string, GamesToRedeemInBackgroundResponse>>(false, string.Format(Strings.BotNotFound, botNames)));
} }
IList<(Dictionary<string, string> UnusedKeys, Dictionary<string, string> UsedKeys)> results = await Utilities.InParallel(bots.Select(bot => bot.GetUsedAndUnusedKeys())).ConfigureAwait(false); IList<(Dictionary<string, string> UnusedKeys, Dictionary<string, string> UsedKeys)> results = await Utilities.InParallel(bots.Select(bot => bot.GetUsedAndUnusedKeys())).ConfigureAwait(false);
@@ -152,13 +162,15 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
result[bot.BotName] = new GamesToRedeemInBackgroundResponse(unusedKeys, usedKeys); result[bot.BotName] = new GamesToRedeemInBackgroundResponse(unusedKeys, usedKeys);
} }
return Ok(new GenericResponse<Dictionary<string, GamesToRedeemInBackgroundResponse>>(result)); return Ok(new GenericResponse<IReadOnlyDictionary<string, GamesToRedeemInBackgroundResponse>>(result));
} }
/// <summary> /// <summary>
/// Adds keys to redeem using BGR to given bot. /// Adds keys to redeem using BGR to given bot.
/// </summary> /// </summary>
[Consumes("application/json")]
[HttpPost("{botName:required}/GamesToRedeemInBackground")] [HttpPost("{botName:required}/GamesToRedeemInBackground")]
[SwaggerResponse(200, type: typeof(GenericResponse<OrderedDictionary>))]
public async Task<ActionResult<GenericResponse<OrderedDictionary>>> GamesToRedeemInBackgroundPost(string botName, [FromBody] GamesToRedeemInBackgroundRequest request) { public async Task<ActionResult<GenericResponse<OrderedDictionary>>> GamesToRedeemInBackgroundPost(string botName, [FromBody] GamesToRedeemInBackgroundRequest request) {
if (string.IsNullOrEmpty(botName) || (request == null)) { if (string.IsNullOrEmpty(botName) || (request == null)) {
ASF.ArchiLogger.LogNullError(nameof(botName) + " || " + nameof(request)); ASF.ArchiLogger.LogNullError(nameof(botName) + " || " + nameof(request));
@@ -180,7 +192,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// <summary> /// <summary>
/// Pauses given bots. /// Pauses given bots.
/// </summary> /// </summary>
[Consumes("application/json")]
[HttpPost("{botNames:required}/Pause")] [HttpPost("{botNames:required}/Pause")]
[SwaggerResponse(200, type: typeof(GenericResponse))]
public async Task<ActionResult<GenericResponse>> PausePost(string botNames, [FromBody] BotPauseRequest request) { public async Task<ActionResult<GenericResponse>> PausePost(string botNames, [FromBody] BotPauseRequest request) {
if (string.IsNullOrEmpty(botNames) || (request == null)) { if (string.IsNullOrEmpty(botNames) || (request == null)) {
ASF.ArchiLogger.LogNullError(nameof(botNames) + " || " + nameof(request)); ASF.ArchiLogger.LogNullError(nameof(botNames) + " || " + nameof(request));
@@ -200,6 +214,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Resumes given bots. /// Resumes given bots.
/// </summary> /// </summary>
[HttpPost("{botNames:required}/Resume")] [HttpPost("{botNames:required}/Resume")]
[SwaggerResponse(200, type: typeof(GenericResponse))]
public async Task<ActionResult<GenericResponse>> ResumePost(string botNames) { public async Task<ActionResult<GenericResponse>> ResumePost(string botNames) {
if (string.IsNullOrEmpty(botNames)) { if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames)); ASF.ArchiLogger.LogNullError(nameof(botNames));
@@ -219,6 +234,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Starts given bots. /// Starts given bots.
/// </summary> /// </summary>
[HttpPost("{botNames:required}/Start")] [HttpPost("{botNames:required}/Start")]
[SwaggerResponse(200, type: typeof(GenericResponse))]
public async Task<ActionResult<GenericResponse>> StartPost(string botNames) { public async Task<ActionResult<GenericResponse>> StartPost(string botNames) {
if (string.IsNullOrEmpty(botNames)) { if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames)); ASF.ArchiLogger.LogNullError(nameof(botNames));
@@ -238,6 +254,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Stops given bots. /// Stops given bots.
/// </summary> /// </summary>
[HttpPost("{botNames:required}/Stop")] [HttpPost("{botNames:required}/Stop")]
[SwaggerResponse(200, type: typeof(GenericResponse))]
public async Task<ActionResult<GenericResponse>> StopPost(string botNames) { public async Task<ActionResult<GenericResponse>> StopPost(string botNames) {
if (string.IsNullOrEmpty(botNames)) { if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames)); ASF.ArchiLogger.LogNullError(nameof(botNames));

View File

@@ -25,11 +25,15 @@ using System.Threading.Tasks;
using ArchiSteamFarm.IPC.Responses; using ArchiSteamFarm.IPC.Responses;
using ArchiSteamFarm.Localization; using ArchiSteamFarm.Localization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
namespace ArchiSteamFarm.IPC.Controllers.Api { namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController] [ApiController]
[Produces("application/json")] [Produces("application/json")]
[Route("Api/Command")] [Route("Api/Command")]
[SwaggerResponse(400, "The request has failed, check " + nameof(GenericResponse.Message) + " from response body for actual reason. Most of the time this is ASF, understanding the request, but refusing to execute it due to provided reason.", typeof(GenericResponse))]
[SwaggerResponse(401, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set, but you've failed to authenticate. See " + "https://github.com/" + SharedInfo.GithubRepo + "/wiki/IPC#authentication.")]
[SwaggerResponse(403, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set and you've failed to authenticate too many times, try again in an hour. See " + "https://github.com/" + SharedInfo.GithubRepo + "/wiki/IPC#authentication.")]
public sealed class CommandController : ControllerBase { public sealed class CommandController : ControllerBase {
/// <summary> /// <summary>
/// Executes a command. /// Executes a command.
@@ -39,6 +43,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// You should use "given bot" commands when executing this endpoint, omitting targets of the command will cause the command to be executed on first defined bot /// You should use "given bot" commands when executing this endpoint, omitting targets of the command will cause the command to be executed on first defined bot
/// </remarks> /// </remarks>
[HttpPost("{command:required}")] [HttpPost("{command:required}")]
[SwaggerResponse(200, type: typeof(GenericResponse<string>))]
public async Task<ActionResult<GenericResponse<string>>> CommandPost(string command) { public async Task<ActionResult<GenericResponse<string>>> CommandPost(string command) {
if (string.IsNullOrEmpty(command)) { if (string.IsNullOrEmpty(command)) {
ASF.ArchiLogger.LogNullError(nameof(command)); ASF.ArchiLogger.LogNullError(nameof(command));

View File

@@ -20,6 +20,7 @@
// limitations under the License. // limitations under the License.
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Text; using System.Text;
@@ -30,11 +31,15 @@ using ArchiSteamFarm.Localization;
using ArchiSteamFarm.NLog; using ArchiSteamFarm.NLog;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json; using Newtonsoft.Json;
using Swashbuckle.AspNetCore.Annotations;
namespace ArchiSteamFarm.IPC.Controllers.Api { namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController] [ApiController]
[Produces("application/json")] [Produces("application/json")]
[Route("Api/NLog")] [Route("Api/NLog")]
[SwaggerResponse(400, "The request has failed, check " + nameof(GenericResponse.Message) + " from response body for actual reason. Most of the time this is ASF, understanding the request, but refusing to execute it due to provided reason.", typeof(GenericResponse))]
[SwaggerResponse(401, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set, but you've failed to authenticate. See " + "https://github.com/" + SharedInfo.GithubRepo + "/wiki/IPC#authentication.")]
[SwaggerResponse(403, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set and you've failed to authenticate too many times, try again in an hour. See " + "https://github.com/" + SharedInfo.GithubRepo + "/wiki/IPC#authentication.")]
public sealed class NLogController : ControllerBase { public sealed class NLogController : ControllerBase {
private static readonly ConcurrentDictionary<WebSocket, SemaphoreSlim> ActiveLogWebSockets = new ConcurrentDictionary<WebSocket, SemaphoreSlim>(); private static readonly ConcurrentDictionary<WebSocket, SemaphoreSlim> ActiveLogWebSockets = new ConcurrentDictionary<WebSocket, SemaphoreSlim>();
@@ -45,6 +50,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// This API endpoint requires a websocket connection. /// This API endpoint requires a websocket connection.
/// </remarks> /// </remarks>
[HttpGet] [HttpGet]
[SwaggerResponse(200, type: typeof(IEnumerable<GenericResponse<string>>))]
public async Task<ActionResult> NLogGet() { public async Task<ActionResult> NLogGet() {
if (!HttpContext.WebSockets.IsWebSocketRequest) { if (!HttpContext.WebSockets.IsWebSocketRequest) {
return BadRequest(new GenericResponse(false, string.Format(Strings.WarningFailedWithError, nameof(HttpContext.WebSockets.IsWebSocketRequest) + ": " + HttpContext.WebSockets.IsWebSocketRequest))); return BadRequest(new GenericResponse(false, string.Format(Strings.WarningFailedWithError, nameof(HttpContext.WebSockets.IsWebSocketRequest) + ": " + HttpContext.WebSockets.IsWebSocketRequest)));

View File

@@ -23,11 +23,15 @@ using System;
using ArchiSteamFarm.IPC.Responses; using ArchiSteamFarm.IPC.Responses;
using ArchiSteamFarm.Localization; using ArchiSteamFarm.Localization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
namespace ArchiSteamFarm.IPC.Controllers.Api { namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController] [ApiController]
[Produces("application/json")] [Produces("application/json")]
[Route("Api/Structure")] [Route("Api/Structure")]
[SwaggerResponse(400, "The request has failed, check " + nameof(GenericResponse.Message) + " from response body for actual reason. Most of the time this is ASF, understanding the request, but refusing to execute it due to provided reason.", typeof(GenericResponse))]
[SwaggerResponse(401, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set, but you've failed to authenticate. See " + "https://github.com/" + SharedInfo.GithubRepo + "/wiki/IPC#authentication.")]
[SwaggerResponse(403, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set and you've failed to authenticate too many times, try again in an hour. See " + "https://github.com/" + SharedInfo.GithubRepo + "/wiki/IPC#authentication.")]
public sealed class StructureController : ControllerBase { public sealed class StructureController : ControllerBase {
/// <summary> /// <summary>
/// Fetches structure of given type. /// Fetches structure of given type.
@@ -36,6 +40,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Structure is defined as a representation of given object in its default state. /// Structure is defined as a representation of given object in its default state.
/// </remarks> /// </remarks>
[HttpGet("{structure:required}")] [HttpGet("{structure:required}")]
[SwaggerResponse(200, type: typeof(GenericResponse<object>))]
public ActionResult<GenericResponse<object>> StructureGet(string structure) { public ActionResult<GenericResponse<object>> StructureGet(string structure) {
if (string.IsNullOrEmpty(structure)) { if (string.IsNullOrEmpty(structure)) {
ASF.ArchiLogger.LogNullError(nameof(structure)); ASF.ArchiLogger.LogNullError(nameof(structure));

View File

@@ -27,11 +27,15 @@ using ArchiSteamFarm.IPC.Responses;
using ArchiSteamFarm.Localization; using ArchiSteamFarm.Localization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json; using Newtonsoft.Json;
using Swashbuckle.AspNetCore.Annotations;
namespace ArchiSteamFarm.IPC.Controllers.Api { namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController] [ApiController]
[Produces("application/json")] [Produces("application/json")]
[Route("Api/Type")] [Route("Api/Type")]
[SwaggerResponse(400, "The request has failed, check " + nameof(GenericResponse.Message) + " from response body for actual reason. Most of the time this is ASF, understanding the request, but refusing to execute it due to provided reason.", typeof(GenericResponse))]
[SwaggerResponse(401, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set, but you've failed to authenticate. See " + "https://github.com/" + SharedInfo.GithubRepo + "/wiki/IPC#authentication.")]
[SwaggerResponse(403, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set and you've failed to authenticate too many times, try again in an hour. See " + "https://github.com/" + SharedInfo.GithubRepo + "/wiki/IPC#authentication.")]
public sealed class TypeController : ControllerBase { public sealed class TypeController : ControllerBase {
/// <summary> /// <summary>
/// Fetches type info of given type. /// Fetches type info of given type.
@@ -40,6 +44,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Type info is defined as a representation of given object with its fields and properties being assigned to a string value that defines their type. /// Type info is defined as a representation of given object with its fields and properties being assigned to a string value that defines their type.
/// </remarks> /// </remarks>
[HttpGet("{type:required}")] [HttpGet("{type:required}")]
[SwaggerResponse(200, type: typeof(GenericResponse<TypeResponse>))]
public ActionResult<GenericResponse<TypeResponse>> TypeGet(string type) { public ActionResult<GenericResponse<TypeResponse>> TypeGet(string type) {
if (string.IsNullOrEmpty(type)) { if (string.IsNullOrEmpty(type)) {
ASF.ArchiLogger.LogNullError(nameof(type)); ASF.ArchiLogger.LogNullError(nameof(type));

View File

@@ -28,11 +28,15 @@ using ArchiSteamFarm.IPC.Requests;
using ArchiSteamFarm.IPC.Responses; using ArchiSteamFarm.IPC.Responses;
using ArchiSteamFarm.Localization; using ArchiSteamFarm.Localization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
namespace ArchiSteamFarm.IPC.Controllers.Api { namespace ArchiSteamFarm.IPC.Controllers.Api {
[ApiController] [ApiController]
[Produces("application/json")] [Produces("application/json")]
[Route("Api/WWW")] [Route("Api/WWW")]
[SwaggerResponse(400, "The request has failed, check " + nameof(GenericResponse.Message) + " from response body for actual reason. Most of the time this is ASF, understanding the request, but refusing to execute it due to provided reason.", typeof(GenericResponse))]
[SwaggerResponse(401, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set, but you've failed to authenticate. See " + "https://github.com/" + SharedInfo.GithubRepo + "/wiki/IPC#authentication.")]
[SwaggerResponse(403, "ASF has " + nameof(GlobalConfig.IPCPassword) + " set and you've failed to authenticate too many times, try again in an hour. See " + "https://github.com/" + SharedInfo.GithubRepo + "/wiki/IPC#authentication.")]
public sealed class WWWController : ControllerBase { public sealed class WWWController : ControllerBase {
/// <summary> /// <summary>
/// Fetches files in given directory relative to WWW root. /// Fetches files in given directory relative to WWW root.
@@ -41,15 +45,16 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW as they can disappear and change anytime. /// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW as they can disappear and change anytime.
/// </remarks> /// </remarks>
[HttpGet("Directory/{directory:required}")] [HttpGet("Directory/{directory:required}")]
public ActionResult<GenericResponse<HashSet<string>>> DirectoryGet(string directory) { [SwaggerResponse(200, type: typeof(GenericResponse<IReadOnlyCollection<string>>))]
public ActionResult<GenericResponse<IReadOnlyCollection<string>>> DirectoryGet(string directory) {
if (string.IsNullOrEmpty(directory)) { if (string.IsNullOrEmpty(directory)) {
ASF.ArchiLogger.LogNullError(nameof(directory)); ASF.ArchiLogger.LogNullError(nameof(directory));
return BadRequest(new GenericResponse<HashSet<string>>(false, string.Format(Strings.ErrorIsEmpty, nameof(directory)))); return BadRequest(new GenericResponse<IReadOnlyCollection<string>>(false, string.Format(Strings.ErrorIsEmpty, nameof(directory))));
} }
string directoryPath = Path.Combine(ArchiKestrel.WebsiteDirectory, directory); string directoryPath = Path.Combine(ArchiKestrel.WebsiteDirectory, directory);
if (!Directory.Exists(directoryPath)) { if (!Directory.Exists(directoryPath)) {
return BadRequest(new GenericResponse<HashSet<string>>(false, string.Format(Strings.ErrorIsInvalid, directory))); return BadRequest(new GenericResponse<IReadOnlyCollection<string>>(false, string.Format(Strings.ErrorIsInvalid, directory)));
} }
string[] files; string[] files;
@@ -57,11 +62,11 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
try { try {
files = Directory.GetFiles(directoryPath); files = Directory.GetFiles(directoryPath);
} catch (Exception e) { } catch (Exception e) {
return BadRequest(new GenericResponse<HashSet<string>>(false, string.Format(Strings.ErrorParsingObject, nameof(files)) + Environment.NewLine + e)); return BadRequest(new GenericResponse<IReadOnlyCollection<string>>(false, string.Format(Strings.ErrorParsingObject, nameof(files)) + Environment.NewLine + e));
} }
HashSet<string> result = files.Select(Path.GetFileName).ToHashSet(); HashSet<string> result = files.Select(Path.GetFileName).ToHashSet();
return Ok(new GenericResponse<HashSet<string>>(result)); return Ok(new GenericResponse<IReadOnlyCollection<string>>(result));
} }
/// <summary> /// <summary>
@@ -71,18 +76,19 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW as they can disappear and change anytime. /// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW as they can disappear and change anytime.
/// </remarks> /// </remarks>
[HttpGet("GitHub/Releases")] [HttpGet("GitHub/Releases")]
public async Task<ActionResult<GenericResponse<IEnumerable<GitHubReleaseResponse>>>> GitHubReleasesGet([FromQuery] byte count = 10) { [SwaggerResponse(200, type: typeof(GenericResponse<IReadOnlyCollection<GitHubReleaseResponse>>))]
public async Task<ActionResult<GenericResponse<IReadOnlyCollection<GitHubReleaseResponse>>>> GitHubReleasesGet([FromQuery] byte count = 10) {
if (count == 0) { if (count == 0) {
return BadRequest(new GenericResponse<IEnumerable<GitHubReleaseResponse>>(false, string.Format(Strings.ErrorIsEmpty, nameof(count)))); return BadRequest(new GenericResponse<IReadOnlyCollection<GitHubReleaseResponse>>(false, string.Format(Strings.ErrorIsEmpty, nameof(count))));
} }
List<GitHub.ReleaseResponse> response = await GitHub.GetReleases(count).ConfigureAwait(false); List<GitHub.ReleaseResponse> response = await GitHub.GetReleases(count).ConfigureAwait(false);
if ((response == null) || (response.Count == 0)) { if ((response == null) || (response.Count == 0)) {
return BadRequest(new GenericResponse<IEnumerable<GitHub.ReleaseResponse>>(false, string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries))); return BadRequest(new GenericResponse<IReadOnlyCollection<GitHub.ReleaseResponse>>(false, string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)));
} }
IEnumerable<GitHubReleaseResponse> result = response.Select(singleResponse => new GitHubReleaseResponse(singleResponse)); List<GitHubReleaseResponse> result = response.Select(singleResponse => new GitHubReleaseResponse(singleResponse)).ToList();
return Ok(new GenericResponse<IEnumerable<GitHubReleaseResponse>>(result)); return Ok(new GenericResponse<IReadOnlyCollection<GitHubReleaseResponse>>(result));
} }
/// <summary> /// <summary>
@@ -92,6 +98,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW as they can disappear and change anytime. /// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW as they can disappear and change anytime.
/// </remarks> /// </remarks>
[HttpGet("GitHub/Releases/{version:required}")] [HttpGet("GitHub/Releases/{version:required}")]
[SwaggerResponse(200, type: typeof(GenericResponse<GitHubReleaseResponse>))]
public async Task<ActionResult<GenericResponse<GitHubReleaseResponse>>> GitHubReleasesGet(string version) { public async Task<ActionResult<GenericResponse<GitHubReleaseResponse>>> GitHubReleasesGet(string version) {
if (string.IsNullOrEmpty(version)) { if (string.IsNullOrEmpty(version)) {
return BadRequest(new GenericResponse<GitHubReleaseResponse>(false, string.Format(Strings.ErrorIsEmpty, nameof(version)))); return BadRequest(new GenericResponse<GitHubReleaseResponse>(false, string.Format(Strings.ErrorIsEmpty, nameof(version))));
@@ -111,7 +118,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// <remarks> /// <remarks>
/// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW as they can disappear and change anytime. /// This is internal API being utilizied by our ASF-ui IPC frontend. You should not depend on existence of any /Api/WWW as they can disappear and change anytime.
/// </remarks> /// </remarks>
[Consumes("application/json")]
[HttpPost("Send")] [HttpPost("Send")]
[SwaggerResponse(200, type: typeof(GenericResponse<string>))]
public async Task<ActionResult<GenericResponse<string>>> SendPost([FromBody] WWWSendRequest request) { public async Task<ActionResult<GenericResponse<string>>> SendPost([FromBody] WWWSendRequest request) {
if (request == null) { if (request == null) {
ASF.ArchiLogger.LogNullError(nameof(request)); ASF.ArchiLogger.LogNullError(nameof(request));

View File

@@ -95,6 +95,7 @@ namespace ArchiSteamFarm.IPC {
services.AddSwaggerGen( services.AddSwaggerGen(
c => { c => {
c.DescribeAllEnumsAsStrings(); c.DescribeAllEnumsAsStrings();
c.EnableAnnotations();
c.SwaggerDoc("ASF", new Info { Title = "ASF API" }); c.SwaggerDoc("ASF", new Info { Title = "ASF API" });
string xmlDocumentationFile = Path.Combine(AppContext.BaseDirectory, SharedInfo.AssemblyDocumentation); string xmlDocumentationFile = Path.Combine(AppContext.BaseDirectory, SharedInfo.AssemblyDocumentation);