Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements #368

Merged
merged 11 commits into from
Jan 9, 2025
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "8.0.303",
"version": "9.0.101",
"allowPrerelease": false,
"rollForward": "latestMajor"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace Redmine.Net.Api.Async
{
/// <summary>
/// </summary>
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use RedmineManger async methods instead")]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use RedmineManger async methods instead.")]
public static class RedmineManagerAsyncExtensions
{
/// <summary>
Expand All @@ -40,7 +40,7 @@ public static class RedmineManagerAsyncExtensions
/// <param name="impersonateUserName"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use the extension with RequestOptions parameter instead.")]
public static async Task<User> GetCurrentUserAsync(this RedmineManager redmineManager, NameValueCollection parameters = null, string impersonateUserName = null, CancellationToken cancellationToken = default)
{
var requestOptions = RedmineManagerExtensions.CreateRequestOptions();
Expand All @@ -55,7 +55,7 @@ public static async Task<User> GetCurrentUserAsync(this RedmineManager redmineMa
/// <param name="pageName">Name of the page.</param>
/// <param name="wikiPage">The wiki page.</param>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use the extension with RequestOptions parameter instead.")]
public static async Task<WikiPage> CreateWikiPageAsync(this RedmineManager redmineManager, string projectId, string pageName, WikiPage wikiPage)
{
var requestOptions = RedmineManagerExtensions.CreateRequestOptions();
Expand All @@ -71,7 +71,7 @@ public static async Task<WikiPage> CreateWikiPageAsync(this RedmineManager redmi
/// <param name="pageName">Name of the page.</param>
/// <param name="wikiPage">The wiki page.</param>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use the extension with RequestOptions parameter instead.")]
public static async Task UpdateWikiPageAsync(this RedmineManager redmineManager, string projectId, string pageName, WikiPage wikiPage)
{
var requestOptions = RedmineManagerExtensions.CreateRequestOptions();
Expand All @@ -85,7 +85,7 @@ public static async Task UpdateWikiPageAsync(this RedmineManager redmineManager,
/// <param name="projectId">The project identifier.</param>
/// <param name="pageName">Name of the page.</param>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use the extension with RequestOptions parameter instead.")]
public static async Task DeleteWikiPageAsync(this RedmineManager redmineManager, string projectId, string pageName)
{
var requestOptions = RedmineManagerExtensions.CreateRequestOptions();
Expand All @@ -101,11 +101,11 @@ public static async Task DeleteWikiPageAsync(this RedmineManager redmineManager,
/// <returns>
/// .
/// </returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use the extension with RequestOptions parameter instead.")]
public static async Task<Upload> UploadFileAsync(this RedmineManager redmineManager, byte[] data)
{
var requestOptions = RedmineManagerExtensions.CreateRequestOptions();
return await redmineManager.UploadFileAsync(data, requestOptions).ConfigureAwait(false);
return await redmineManager.UploadFileAsync(data, null, requestOptions).ConfigureAwait(false);
}

/// <summary>
Expand All @@ -114,7 +114,7 @@ public static async Task<Upload> UploadFileAsync(this RedmineManager redmineMana
/// <param name="redmineManager">The redmine manager.</param>
/// <param name="address">The address.</param>
/// <returns></returns>
[Obsolete("Use DownloadFileAsync instead")]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use the extension with RequestOptions parameter instead.")]
public static async Task<byte[]> DownloadFileAsync(this RedmineManager redmineManager, string address)
{
var requestOptions = RedmineManagerExtensions.CreateRequestOptions();
Expand All @@ -131,7 +131,7 @@ public static async Task<byte[]> DownloadFileAsync(this RedmineManager redmineMa
/// <param name="pageName">Name of the page.</param>
/// <param name="version">The version.</param>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use the extension with RequestOptions parameter instead.")]
public static async Task<WikiPage> GetWikiPageAsync(this RedmineManager redmineManager, string projectId, NameValueCollection parameters, string pageName, uint version = 0)
{
var requestOptions = RedmineManagerExtensions.CreateRequestOptions(parameters);
Expand All @@ -145,7 +145,7 @@ public static async Task<WikiPage> GetWikiPageAsync(this RedmineManager redmineM
/// <param name="parameters">The parameters.</param>
/// <param name="projectId">The project identifier.</param>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use the extension with RequestOptions parameter instead.")]
public static async Task<List<WikiPage>> GetAllWikiPagesAsync(this RedmineManager redmineManager, NameValueCollection parameters, string projectId)
{
var requestOptions = RedmineManagerExtensions.CreateRequestOptions(parameters);
Expand All @@ -161,7 +161,7 @@ public static async Task<List<WikiPage>> GetAllWikiPagesAsync(this RedmineManage
/// <returns>
/// Returns the Guid associated with the async request.
/// </returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use the extension with RequestOptions parameter instead.")]
public static async Task AddUserToGroupAsync(this RedmineManager redmineManager, int groupId, int userId)
{
var requestOptions = RedmineManagerExtensions.CreateRequestOptions();
Expand All @@ -175,7 +175,7 @@ public static async Task AddUserToGroupAsync(this RedmineManager redmineManager,
/// <param name="groupId">The group id.</param>
/// <param name="userId">The user id.</param>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use the extension with RequestOptions parameter instead.")]
public static async Task RemoveUserFromGroupAsync(this RedmineManager redmineManager, int groupId, int userId)
{
var requestOptions = RedmineManagerExtensions.CreateRequestOptions();
Expand All @@ -189,7 +189,7 @@ public static async Task RemoveUserFromGroupAsync(this RedmineManager redmineMan
/// <param name="issueId">The issue identifier.</param>
/// <param name="userId">The user identifier.</param>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use the extension with RequestOptions parameter instead.")]
public static async Task AddWatcherToIssueAsync(this RedmineManager redmineManager, int issueId, int userId)
{
var requestOptions = RedmineManagerExtensions.CreateRequestOptions();
Expand All @@ -203,7 +203,7 @@ public static async Task AddWatcherToIssueAsync(this RedmineManager redmineManag
/// <param name="issueId">The issue identifier.</param>
/// <param name="userId">The user identifier.</param>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use the extension with RequestOptions parameter instead.")]
public static async Task RemoveWatcherFromIssueAsync(this RedmineManager redmineManager, int issueId, int userId)
{
var requestOptions = RedmineManagerExtensions.CreateRequestOptions();
Expand All @@ -217,7 +217,7 @@ public static async Task RemoveWatcherFromIssueAsync(this RedmineManager redmine
/// <param name="include"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use the extension with RequestOptions parameter instead.")]
public static async Task<int> CountAsync<T>(this RedmineManager redmineManager, params string[] include) where T : class, new()
{
return await redmineManager.CountAsync<T>(null, CancellationToken.None).ConfigureAwait(false);
Expand All @@ -230,7 +230,7 @@ public static async Task RemoveWatcherFromIssueAsync(this RedmineManager redmine
/// <param name="parameters"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use CountAsync method instead.")]
public static async Task<int> CountAsync<T>(this RedmineManager redmineManager, NameValueCollection parameters) where T : class, new()
{
var requestOptions = RedmineManagerExtensions.CreateRequestOptions(parameters);
Expand All @@ -245,7 +245,7 @@ public static async Task RemoveWatcherFromIssueAsync(this RedmineManager redmine
/// <param name="redmineManager">The redmine manager.</param>
/// <param name="parameters">The parameters.</param>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use GetPagedAsync method instead.")]
public static async Task<PagedResults<T>> GetPaginatedObjectsAsync<T>(this RedmineManager redmineManager, NameValueCollection parameters)
where T : class, new()
{
Expand All @@ -260,7 +260,7 @@ public static async Task<PagedResults<T>> GetPaginatedObjectsAsync<T>(this Redmi
/// <param name="redmineManager">The redmine manager.</param>
/// <param name="parameters">The parameters.</param>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use GetAsync method instead.")]
public static async Task<List<T>> GetObjectsAsync<T>(this RedmineManager redmineManager, NameValueCollection parameters)
where T : class, new()
{
Expand All @@ -276,7 +276,7 @@ public static async Task<List<T>> GetObjectsAsync<T>(this RedmineManager redmine
/// <param name="id">The id of the object.</param>
/// <param name="parameters">Optional filters and/or optional fetched data.</param>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use GetAsync method instead.")]
public static async Task<T> GetObjectAsync<T>(this RedmineManager redmineManager, string id, NameValueCollection parameters)
where T : class, new()
{
Expand All @@ -291,7 +291,7 @@ public static async Task<T> GetObjectAsync<T>(this RedmineManager redmineManager
/// <param name="redmineManager">The redmine manager.</param>
/// <param name="entity">The object to create.</param>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use CreateAsync method instead.")]
public static async Task<T> CreateObjectAsync<T>(this RedmineManager redmineManager, T entity)
where T : class, new()
{
Expand All @@ -307,7 +307,7 @@ public static async Task<T> CreateObjectAsync<T>(this RedmineManager redmineMana
/// <param name="entity">The object to create.</param>
/// <param name="ownerId">The owner identifier.</param>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use CreateAsync method instead.")]
public static async Task<T> CreateObjectAsync<T>(this RedmineManager redmineManager, T entity, string ownerId)
where T : class, new()
{
Expand All @@ -323,7 +323,7 @@ public static async Task<T> CreateObjectAsync<T>(this RedmineManager redmineMana
/// <param name="id">The identifier.</param>
/// <param name="entity">The object.</param>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use UpdateAsync method instead.")]
public static async Task UpdateObjectAsync<T>(this RedmineManager redmineManager, string id, T entity)
where T : class, new()
{
Expand All @@ -338,29 +338,13 @@ public static async Task UpdateObjectAsync<T>(this RedmineManager redmineManager
/// <param name="redmineManager">The redmine manager.</param>
/// <param name="id">The id of the object to delete</param>
/// <returns></returns>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
[Obsolete(RedmineConstants.OBSOLETE_TEXT + " Use DeleteAsync method instead.")]
public static async Task DeleteObjectAsync<T>(this RedmineManager redmineManager, string id)
where T : class, new()
{
var requestOptions = RedmineManagerExtensions.CreateRequestOptions();
await redmineManager.DeleteAsync<T>(id, requestOptions).ConfigureAwait(false);
}

/// <summary>
///
/// </summary>
/// <param name="redmineManager"></param>
/// <param name="q"></param>
/// <param name="limit"></param>
/// <param name="offset"></param>
/// <param name="searchFilter"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
[Obsolete(RedmineConstants.OBSOLETE_TEXT)]
public static async Task<PagedResults<Search>> SearchAsync(this RedmineManager redmineManager, string q, int limit = RedmineManager.DEFAULT_PAGE_SIZE_VALUE, int offset = 0, SearchFilterBuilder searchFilter = null)
{
return await RedmineManagerExtensions.SearchAsync(redmineManager, q, limit, offset, searchFilter).ConfigureAwait(false);
}
}
}
#endif
10 changes: 5 additions & 5 deletions src/redmine-net-api/Extensions/RedmineManagerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public static PagedResults<News> GetProjectNews(this RedmineManager redmineManag

var escapedUri = Uri.EscapeDataString(uri);

var response = redmineManager.GetPaginatedObjects<News>(escapedUri, requestOptions);
var response = redmineManager.GetPaginatedInternal<News>(escapedUri, requestOptions);

return response;
}
Expand Down Expand Up @@ -96,7 +96,7 @@ public static PagedResults<ProjectMembership> GetProjectMemberships(this Redmine
{
var uri = redmineManager.RedmineApiUrls.ProjectMemberships(projectIdentifier);

var response = redmineManager.GetPaginatedObjects<ProjectMembership>(uri, requestOptions);
var response = redmineManager.GetPaginatedInternal<ProjectMembership>(uri, requestOptions);

return response;
}
Expand All @@ -113,7 +113,7 @@ public static PagedResults<File> GetProjectFiles(this RedmineManager redmineMana
{
var uri = redmineManager.RedmineApiUrls.ProjectFilesFragment(projectIdentifier);

var response = redmineManager.GetPaginatedObjects<File>(uri, requestOptions);
var response = redmineManager.GetPaginatedInternal<File>(uri, requestOptions);

return response;
}
Expand Down Expand Up @@ -291,7 +291,7 @@ public static List<WikiPage> GetAllWikiPages(this RedmineManager redmineManager,
{
var uri = redmineManager.RedmineApiUrls.ProjectWikiIndex(projectId);

var response = redmineManager.GetObjects<WikiPage>(uri, requestOptions);
var response = redmineManager.GetInternal<WikiPage>(uri, requestOptions);

return response;
}
Expand Down Expand Up @@ -530,7 +530,7 @@ public static async Task<WikiPage> CreateWikiPageAsync(this RedmineManager redmi
}

/// <summary>
/// Creates the or update wiki page asynchronous.
/// Creates or update wiki page asynchronous.
/// </summary>
/// <param name="redmineManager">The redmine manager.</param>
/// <param name="projectId">The project identifier.</param>
Expand Down
5 changes: 3 additions & 2 deletions src/redmine-net-api/IRedmineManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,18 @@ void Update<T>(string id, T entity, string projectId = null, RequestOptions requ
/// <typeparam name="T"></typeparam>
void Delete<T>(string id, RequestOptions requestOptions = null)
where T : class, new();

/// <summary>
/// Support for adding attachments through the REST API is added in Redmine 1.4.0.
/// Upload a file to server.
/// </summary>
/// <param name="data">The content of the file that will be uploaded on server.</param>
/// <param name="fileName"></param>
/// <returns>
/// Returns the token for uploaded file.
/// </returns>
/// <exception cref="RedmineException"></exception>
Upload UploadFile(byte[] data);
Upload UploadFile(byte[] data, string fileName = null);

/// <summary>
/// Downloads a file from the specified address.
Expand Down
3 changes: 2 additions & 1 deletion src/redmine-net-api/IRedmineManagerAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,13 @@ Task DeleteAsync<T>(string id, RequestOptions requestOptions = null, Cancellatio
/// Upload a file to server. This method does not block the calling thread.
/// </summary>
/// <param name="data">The content of the file that will be uploaded on server.</param>
/// <param name="fileName"></param>
/// <param name="requestOptions"></param>
/// <param name="cancellationToken"></param>
/// <returns>
/// .
/// </returns>
Task<Upload> UploadFileAsync(byte[] data, RequestOptions requestOptions = null, CancellationToken cancellationToken = default);
Task<Upload> UploadFileAsync(byte[] data, string fileName = null, RequestOptions requestOptions = null, CancellationToken cancellationToken = default);

/// <summary>
/// Downloads the file asynchronous.
Expand Down
32 changes: 32 additions & 0 deletions src/redmine-net-api/Internals/ArgumentNullThrowHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

namespace Redmine.Net.Api.Internals;

internal static class ArgumentNullThrowHelper
{
public static void ThrowIfNull(
#if INTERNAL_NULLABLE_ATTRIBUTES || NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER
[NotNull]
#endif
object? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null)

Check warning on line 13 in src/redmine-net-api/Internals/ArgumentNullThrowHelper.cs

View workflow job for this annotation

GitHub Actions / Build ubuntu-latest - dotnet

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 13 in src/redmine-net-api/Internals/ArgumentNullThrowHelper.cs

View workflow job for this annotation

GitHub Actions / Build ubuntu-latest - dotnet

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 13 in src/redmine-net-api/Internals/ArgumentNullThrowHelper.cs

View workflow job for this annotation

GitHub Actions / Build windows-latest - dotnet

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 13 in src/redmine-net-api/Internals/ArgumentNullThrowHelper.cs

View workflow job for this annotation

GitHub Actions / Build windows-latest - dotnet

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
#if !NET7_0_OR_GREATER || NETSTANDARD || NETFRAMEWORK
if (argument is null)
{
Throw(paramName);
}
#else
ArgumentNullException.ThrowIfNull(argument, paramName);
#endif
}

#if !NET7_0_OR_GREATER || NETSTANDARD || NETFRAMEWORK
#if INTERNAL_NULLABLE_ATTRIBUTES || NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER
[DoesNotReturn]
#endif
internal static void Throw(string? paramName) =>

Check warning on line 29 in src/redmine-net-api/Internals/ArgumentNullThrowHelper.cs

View workflow job for this annotation

GitHub Actions / Build windows-latest - dotnet

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
throw new ArgumentNullException(paramName);
#endif
}
6 changes: 4 additions & 2 deletions src/redmine-net-api/Net/RedmineApiUrls.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,11 @@ public string GetListFragment(Type type, string ownerId = null)
return $"{TypeFragment(TypeUrlFragments, type)}.{Format}";
}

public string UploadFragment()
public string UploadFragment(string fileName = null)
{
return $"{RedmineKeys.UPLOADS}.{Format}";
return !fileName.IsNullOrWhiteSpace()
? $"{RedmineKeys.UPLOADS}.{Format}?filename={Uri.EscapeDataString(fileName)}"
: $"{RedmineKeys.UPLOADS}.{Format}";
}
}
}
Loading
Loading