Skip to content

Commit

Permalink
.net 7 && update portainer api level
Browse files Browse the repository at this point in the history
  • Loading branch information
Binali Rustamov committed Sep 26, 2023
1 parent d62c537 commit 1fac271
Show file tree
Hide file tree
Showing 22 changed files with 571 additions and 69 deletions.
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Dockerfile
**/*/
!PortainerClient/bin/release/netcoreapp3.1/linux-x64/publish/PortainerClient
!PortainerClient/bin/Release/net7.0/linux-x64/publish/PortainerClient
7 changes: 5 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
FROM alpine:3.7
FROM ubuntu:20.04

WORKDIR /
COPY PortainerClient/bin/release/netcoreapp3.1/linux-x64/publish/PortainerClient /usr/bin/portainerctl
COPY "PortainerClient/bin/Release/net7.0/linux-x64/publish/PortainerClient" "/usr/bin/portainerctl"

RUN chmod +x /usr/bin/portainerctl
2 changes: 1 addition & 1 deletion PortainerClient/Api/Base/BaseApiHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public static void AddParameters(this RestRequest request,
value.ToString());
break;
case ParamType.BodyParam:
request.AddParameter(paramName, value, ParameterType.RequestBody);
request.AddParameter(paramName, value, ParameterType.GetOrPost);
break;
case ParamType.JsonBody:
request.RequestFormat = DataFormat.Json;
Expand Down
44 changes: 29 additions & 15 deletions PortainerClient/Api/Base/BaseApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ private RestClient ResolveClient()
var options = new RestClientOptions
{
BaseUrl = new Uri(config.Url ?? throw new InvalidOperationException("Invalid URL from config")),
Authenticator = new JwtAuthenticator(config.Token ?? throw new InvalidOperationException("Invalid Token from URL"))
Authenticator =
new JwtAuthenticator(config.Token ?? throw new InvalidOperationException("Invalid Token from URL"))
};
_client ??= new RestClient(options);
return _client;
Expand All @@ -33,42 +34,48 @@ private RestClient ResolveClient()
/// Do Get request and return parsed response
/// </summary>
/// <param name="resource">API resource</param>
/// <param name="debug"></param>
/// <param name="parameters">List of request parameters</param>
/// <typeparam name="T">Response type</typeparam>
/// <returns>Parsed instance of T</returns>
protected T Get<T>(string resource, params (string paramName, object paramValue)[] parameters)
protected T Get<T>(string resource, bool debug = false,
params (string paramName, object paramValue)[] parameters)
where T : new() =>
ExecuteRequest<T>(
resource,
Method.Get,
request => request.AddParameters(parameters));
request => request.AddParameters(parameters), debug);

/// <summary>
/// Do Post request and return parsed response
/// </summary>
/// <param name="resource">API resource</param>
/// <param name="debug">Print content of request and response</param>
/// <param name="parameters">List of request parameters</param>
/// <typeparam name="T">Parsed instance of T</typeparam>
/// <returns>Parsed instance of T</returns>
protected T Post<T>(string resource, params (string? paramName, object value, ParamType type)[] parameters)
protected T Post<T>(string resource, bool debug = false,
params (string? paramName, object value, ParamType type)[] parameters)
where T : new() => ExecuteRequest<T>(
resource,
Method.Get,
request => request.AddParameters(parameters));
Method.Post,
request => request.AddParameters(parameters), debug);

/// <summary>
/// Do Put request and return parsed response
/// </summary>
/// <param name="resource">API resource</param>
/// <param name="debug">Debug request / response to Portainer</param>
/// <param name="parameters">List of request parameters</param>
/// <typeparam name="T">Parsed instance of T</typeparam>
/// <returns>Parsed instance of T</returns>
protected T Put<T>(string resource,
bool debug,
params (string? paramName, object value, ParamType type)[] parameters)
where T : new() => ExecuteRequest<T>(
resource,
Method.Get,
request => request.AddParameters(parameters));
Method.Put,
request => request.AddParameters(parameters), debug);

/// <summary>
/// Do Delete request and return parsed response
Expand All @@ -80,34 +87,41 @@ protected void Delete(string resource, params (string paramName, object paramVal

private void ExecuteRequest(string resource, Method method, Action<RestRequest>? requestConfig = null)
{
var request = new RestRequest(resource, method);
var request = new TraceRequest(resource, method);
requestConfig?.Invoke(request);
var response = ResolveClient().Execute(request);
if (response.IsSuccessful) return;
Debug.Assert(response.Content != null, "response.Content != null");
throw ParseError(request.Resource, response.Content);
throw ParseError(request.Resource, response);
}

private T ExecuteRequest<T>(string resource, Method method, Action<RestRequest>? requestConfig = null)
private T ExecuteRequest<T>(string resource, Method method, Action<RestRequest>? requestConfig = null,
bool debug = false)
where T : new()
{
var request = new RestRequest(resource, method);
var request = new TraceRequest(resource, method, debug);
requestConfig?.Invoke(request);

var response = ResolveClient().Execute<T>(request);
if (!response.IsSuccessful)
{
Debug.Assert(response.Content != null, "response.Content != null");
throw ParseError(request.Resource, response.Content);
throw ParseError(request.Resource, response);
}

Debug.Assert(response.Data != null, "response.Data != null");
return response.Data;
}

private static InvalidOperationException ParseError(string resource, string? responseData)
private static InvalidOperationException ParseError(string resource, RestResponse responseData)
{
ApiError? errorInfo = null;
if (responseData != null) errorInfo = JsonSerializer.Deserialize<ApiError>(responseData);
if (string.IsNullOrWhiteSpace(responseData.Content))
{
return new InvalidOperationException($"Request {resource}: {responseData.StatusDescription}");
}

if (responseData != null) errorInfo = JsonSerializer.Deserialize<ApiError>(responseData.Content);

return new InvalidOperationException(
$"Request {resource}: {(errorInfo != null ? $"{errorInfo.message}, details: {errorInfo.details}" : "no information")}");
Expand Down
171 changes: 171 additions & 0 deletions PortainerClient/Api/Base/TraceRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using RestSharp;

namespace PortainerClient.Api.Base;

/// <inheritdoc />
public class TraceRequest : RestRequest
{
#region Properties

private readonly bool _debug;

#endregion

#region Constructor

/// <inheritdoc />
public TraceRequest(string pResource, bool debug = false)
: base(pResource)
{
_debug = debug;
InitializeLogs();
}

/// <inheritdoc />
public TraceRequest(string pResource, Method method, bool debug = false) : base(pResource, method)
{
_debug = debug;
InitializeLogs();
}

#endregion

#region Methods

private void InitializeLogs()
{
if (!_debug)
return;
OnBeforeRequest = OnBeforeRequestMethod;
OnAfterRequest = OnAfterRequestMethod;
}

private ValueTask OnBeforeRequestMethod(HttpRequestMessage pMessage)
{
var builder = new StringBuilder();

builder.AppendLine("------------------------------");
builder.AppendLine($"REQUEST [{pMessage.Method}] {pMessage.RequestUri}");

foreach (var header in pMessage.Headers)
{
builder.AppendLine($" {header.Key}: {string.Join(';', header.Value)}");
}

var stream = pMessage.Content?.ReadAsStream();

ReadStream(stream, builder);


builder.AppendLine("------------------------------");

var content = builder.ToString();

Console.WriteLine(content);

return ValueTask.CompletedTask;
}

private void ReadContent(HttpContent? pContent, StringBuilder pBuilder)
{
if (pContent == null)
{
return;
}

foreach (var header in pContent.Headers)
{
pBuilder.AppendLine($" {header.Key}: {string.Join(';', header.Value)}");
}

ReadContent(pContent as StreamContent, pBuilder);
ReadContent(pContent as StringContent, pBuilder);
ReadContent(pContent as MultipartFormDataContent, pBuilder);

Console.WriteLine();
}

private void ReadContent(MultipartFormDataContent? pContent, StringBuilder pBuilder)
{
if (pContent == null) return;
foreach (var content in pContent)
{
pBuilder.AppendLine();
ReadContent(content, pBuilder);
}
}

private static void ReadContent(StreamContent? pContent, StringBuilder pBuilder)
{
if (pContent == null) return;
var stream = pContent.ReadAsStream();
pBuilder.AppendLine($" contains {stream.Length} bytes");
}

private void ReadContent(StringContent? pContent, StringBuilder pBuilder)
{
if (pContent == null) return;
var stream = pContent.ReadAsStream();
pBuilder.Append(" ");
ReadStream(stream, pBuilder);
}

private static void ReadStream(Stream? pStream, StringBuilder pBuilder)
{
if (pStream == null)
{
return;
}

var index = 0L;
var length = pStream.Length;
var buffer = new byte[1024];

while (index < length - 1)
{
var read = pStream.Read(buffer, 0, 1024);
var result = Encoding.UTF8.GetString(buffer, 0, read);

pBuilder.Append(result);

index += read;
}

pBuilder.AppendLine();

pStream.Seek(0L, SeekOrigin.Begin);
}

private ValueTask OnAfterRequestMethod(HttpResponseMessage pMessage)
{
var builder = new StringBuilder();

builder.AppendLine("------------------------------");
builder.AppendLine(
$"RESPONSE {pMessage.RequestMessage.Method} [{pMessage.RequestMessage.RequestUri}] {pMessage.StatusCode}");

foreach (var header in pMessage.Headers)
{
builder.AppendLine($" {header.Key}: {string.Join(';', header.Value)}");
}

var stream = pMessage.Content.ReadAsStream();

ReadStream(stream, builder);

builder.AppendLine("------------------------------");

var content = builder.ToString();

Console.WriteLine(content);

return ValueTask.CompletedTask;
}

#endregion
}
22 changes: 22 additions & 0 deletions PortainerClient/Api/Model/Authentication.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace PortainerClient.Api.Model;

/// <summary>
/// Represents authentication information for Git.
/// </summary>
public class Authentication
{
/// <summary>
/// Gets or sets the Git credential ID.
/// </summary>
public int GitCredentialID { get; set; }

/// <summary>
/// Gets or sets the Git password.
/// </summary>
public string Password { get; set; }

/// <summary>
/// Gets or sets the Git username.
/// </summary>
public string Username { get; set; }
}
32 changes: 32 additions & 0 deletions PortainerClient/Api/Model/AutoUpdate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace PortainerClient.Api.Model;

/// <summary>
/// Represents auto-update information.
/// </summary>
public class AutoUpdate
{
/// <summary>
/// Gets or sets a value indicating whether to force pulling the image.
/// </summary>
public bool ForcePullImage { get; set; }

/// <summary>
/// Gets or sets a value indicating whether to force the update.
/// </summary>
public bool ForceUpdate { get; set; }

/// <summary>
/// Gets or sets the interval for updates in a human-readable format (e.g., "1m30s").
/// </summary>
public string Interval { get; set; }

/// <summary>
/// Gets or sets the job ID associated with the auto-update.
/// </summary>
public string JobID { get; set; }

/// <summary>
/// Gets or sets the webhook URL for auto-updates.
/// </summary>
public string Webhook { get; set; }
}
17 changes: 17 additions & 0 deletions PortainerClient/Api/Model/Env.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace PortainerClient.Api.Model;

/// <summary>
/// Represents an environment variable.
/// </summary>
public class Env
{
/// <summary>
/// Gets or sets the name of the environment variable.
/// </summary>
public string Name { get; set; }

/// <summary>
/// Gets or sets the value of the environment variable.
/// </summary>
public string Value { get; set; }
}
Loading

0 comments on commit 1fac271

Please sign in to comment.