Skip to content

Commit

Permalink
Add remaining role connection endpoints and objects
Browse files Browse the repository at this point in the history
  • Loading branch information
MazeXP committed Dec 12, 2022
1 parent f1c112e commit 8ec2013
Show file tree
Hide file tree
Showing 13 changed files with 537 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ public interface IApplicationRoleConnectionMetadata
/// <summary>
/// Gets the dictionary key for the metadata field.
/// </summary>
/// <remarks>The key must only consist of a-z, 0-9 or _ and must have a length between 1 and 50 characters.</remarks>
/// <remarks>The key must only consist of a-z, 0-9 or _ and must have a length of max. 50 characters.</remarks>
string Key { get; }

/// <summary>
/// Gets the name of the metadata field.
/// </summary>
/// <remarks>The length of the name must be between 1 and 100 characters.</remarks>
/// <remarks>The length of the name must be max. 100 characters.</remarks>
string Name { get; }

/// <summary>
Expand All @@ -57,7 +57,7 @@ public interface IApplicationRoleConnectionMetadata
/// <summary>
/// Gets the description of the metadata field.
/// </summary>
/// <remarks>The length of the description must be between 1 and 200 characters.</remarks>
/// <remarks>The length of the description must be max. 200 characters.</remarks>
string Description { get; }

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// IApplicationRoleConnection.cs
//
// Author:
// Jarl Gullberg <jarl.gullberg@gmail.com>
//
// Copyright (c) Jarl Gullberg
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//

using System.Collections.Generic;
using JetBrains.Annotations;
using Remora.Rest.Core;

namespace Remora.Discord.API.Abstractions.Objects;

/// <summary>
/// Represents a role connection that an application has attached to an user.
/// </summary>
[PublicAPI]
public interface IApplicationRoleConnection
{
/// <summary>
/// Gets the vanity name of the platform a bot has connected.
/// </summary>
/// <remarks>The length of the platform username must be max. 50 characters.</remarks>
Optional<string> PlatformName { get; }

/// <summary>
/// Gets the username on the platform a bot has connected.
/// </summary>
/// <remarks>The length of the platform username must be max. 100 characters.</remarks>
Optional<string> PlatformUsername { get; }

/// <summary>
/// Gets the object mapping of <see cref="IApplicationRoleConnectionMetadata.Key"/> to their stringified value
/// for the user on the platform a bot has connected.
/// </summary>
/// <remarks>The length of the stringified value must max. 100 characters.</remarks>
Optional<IReadOnlyDictionary<string, string>> Metadata { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,52 @@ Task<Result<IChannel>> CreateDMAsync
/// <summary>
/// Gets a list of connection objects.
/// </summary>
/// <remarks>
/// Requires the "connections" OAuth" scope.
/// </remarks>
/// <param name="ct">The cancellation token for this operation.</param>
/// <returns>A retrieval result which may or may not have succeeded.</returns>
Task<Result<IReadOnlyList<IConnection>>> GetUserConnectionsAsync
(
CancellationToken ct = default
);

/// <summary>
/// Gets the application role connection for the user.
/// </summary>
/// <remarks>
/// Requires an OAuth2 access token with role_connections.write scope for the specified <paramref name="applicationID"/>.
/// </remarks>
/// <param name="applicationID">The ID of the application.</param>
/// <param name="ct">The cancellation token for this operation.</param>
/// <returns>A retrieval result which may or may not have succeeded.</returns>
Task<Result<IApplicationRoleConnection>> GetUserApplicationRoleConnectionAsync
(
Snowflake applicationID,
CancellationToken ct = default
);

/// <summary>
/// Updates and returns the application role connection for the user.
/// </summary>
/// <remarks>
/// Requires an OAuth2 access token with role_connections.write scope for the specified <paramref name="applicationID"/>.
/// </remarks>
/// <param name="applicationID">The ID of the application.</param>
/// <param name="platformName">The vanity name of the platform a bot has connected (max 50 characters).</param>
/// <param name="platformUsername">The username on the platform a bot has connected (max 100 characters).</param>
/// <param name="metadata">
/// The object mapping application role connection metadata keys to their stringified value (max 100 characters) for
/// the user on the platform a bot has connected.
/// </param>
/// <param name="ct">The cancellation token for this operation.</param>
/// <returns>A retrieval result which may or may not have succeeded.</returns>
Task<Result<IApplicationRoleConnection>> UpdateUserApplicationRoleConnectionAsync
(
Snowflake applicationID,
Optional<string> platformName = default,
Optional<string> platformUsername = default,
Optional<IReadOnlyDictionary<string, string>> metadata = default,
CancellationToken ct = default
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// ApplicationRoleConnection.cs
//
// Author:
// Jarl Gullberg <jarl.gullberg@gmail.com>
//
// Copyright (c) Jarl Gullberg
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//

using System.Collections.Generic;
using JetBrains.Annotations;
using Remora.Discord.API.Abstractions.Objects;
using Remora.Rest.Core;

namespace Remora.Discord.API.Objects;

/// <inheritdoc cref="IApplicationRoleConnection"/>
[PublicAPI]
public record ApplicationRoleConnection
(
Optional<string> PlatformName = default,
Optional<string> PlatformUsername = default,
Optional<IReadOnlyDictionary<string, string>> Metadata = default
) : IApplicationRoleConnection;
Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,7 @@ this JsonSerializerOptions options
)
{
options.AddDataObjectConverter<IApplicationRoleConnectionMetadata, ApplicationRoleConnectionMetadata>();
options.AddDataObjectConverter<IApplicationRoleConnection, ApplicationRoleConnection>();

return options;
}
Expand Down
60 changes: 60 additions & 0 deletions Backend/Remora.Discord.Caching/API/CachingDiscordRestUserAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,4 +244,64 @@ public async Task<Result<IGuildMember>> GetCurrentUserGuildMemberAsync

return result;
}

/// <inheritdoc />
public async Task<Result<IApplicationRoleConnection>> GetUserApplicationRoleConnectionAsync
(
Snowflake applicationID,
CancellationToken ct = default
)
{
var key = KeyHelpers.CreateCurrentUserApplicationRoleConnectionCacheKey(applicationID);
var cacheResult = await _cacheService.TryGetValueAsync<IApplicationRoleConnection>(key, ct);

if (cacheResult.IsSuccess)
{
return cacheResult;
}

var getUserApplicationRoleConnection = await _actual.GetUserApplicationRoleConnectionAsync
(
applicationID,
ct
);
if (!getUserApplicationRoleConnection.IsDefined(out var userApplicationRoleConnection))
{
return getUserApplicationRoleConnection;
}

await _cacheService.CacheAsync(key, userApplicationRoleConnection, ct);

return getUserApplicationRoleConnection;
}

/// <inheritdoc />
public async Task<Result<IApplicationRoleConnection>> UpdateUserApplicationRoleConnectionAsync
(
Snowflake applicationID,
Optional<string> platformName = default,
Optional<string> platformUsername = default,
Optional<IReadOnlyDictionary<string, string>> metadata = default,
CancellationToken ct = default
)
{
var result = await _actual.UpdateUserApplicationRoleConnectionAsync
(
applicationID,
platformName,
platformUsername,
metadata,
ct
);

if (!result.IsDefined(out var userApplicationRoleConnection))
{
return result;
}

var key = KeyHelpers.CreateCurrentUserApplicationRoleConnectionCacheKey(applicationID);
await _cacheService.CacheAsync(key, userApplicationRoleConnection, ct);

return result;
}
}
10 changes: 10 additions & 0 deletions Backend/Remora.Discord.Caching/KeyHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,16 @@ public static string CreateCurrentUserDMsCacheKey()
return $"{CreateCurrentUserCacheKey()}:Channels";
}

/// <summary>
/// Creates a cache key for an <see cref="IApplicationRoleConnection" /> instance of the current <see cref="IUser"/> instance.
/// </summary>
/// <param name="applicationID">The ID of the application.</param>
/// <returns>The cache key.</returns>
public static string CreateCurrentUserApplicationRoleConnectionCacheKey(in Snowflake applicationID)
{
return $"{CreateCurrentUserCacheKey()}:Application:{applicationID}:RoleConnection";
}

/// <summary>
/// Creates a cache key for an <see cref="IConnection"/> instance.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -589,30 +589,30 @@ public virtual async Task<Result<IReadOnlyList<IApplicationRoleConnectionMetadat
CancellationToken ct = default
)
{
if (records.Any(r => r.Key.Length is < 1 or > 50))
if (records.Any(r => r.Key.Length > 50))
{
return new ArgumentOutOfRangeError
(
nameof(records),
"Role connection metadata keys must be between 1 and 50 characters."
"Role connection metadata keys must be max. 50 characters."
);
}

if (records.Any(r => r.Name.Length is < 1 or > 100))
if (records.Any(r => r.Name.Length > 100))
{
return new ArgumentOutOfRangeError
(
nameof(records),
"Role connection metadata names must be between 1 and 100 characters."
"Role connection metadata names must be max. 100 characters."
);
}

if (records.Any(r => r.Description.Length is < 1 or > 200))
if (records.Any(r => r.Description.Length > 200))
{
return new ArgumentOutOfRangeError
(
nameof(records),
"Role connection metadata descriptions must be between 1 and 200 characters."
"Role connection metadata descriptions must be max. 200 characters."
);
}

Expand Down
70 changes: 70 additions & 0 deletions Backend/Remora.Discord.Rest/API/Users/DiscordRestUserAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -230,4 +231,73 @@ public virtual Task<Result<IReadOnlyList<IConnection>>> GetUserConnectionsAsync
ct: ct
);
}

/// <inheritdoc />
public virtual Task<Result<IApplicationRoleConnection>> GetUserApplicationRoleConnectionAsync
(
Snowflake applicationID,
CancellationToken ct = default
)
{
return this.RestHttpClient.GetAsync<IApplicationRoleConnection>
(
$"users/@me/applications/{applicationID}/role-connection",
b => b.WithRateLimitContext(this.RateLimitCache),
ct: ct
);
}

/// <inheritdoc />
public virtual async Task<Result<IApplicationRoleConnection>> UpdateUserApplicationRoleConnectionAsync
(
Snowflake applicationID,
Optional<string> platformName = default,
Optional<string> platformUsername = default,
Optional<IReadOnlyDictionary<string, string>> metadata = default,
CancellationToken ct = default
)
{
if (platformName.HasValue && platformName.Value.Length > 50)
{
return new ArgumentOutOfRangeError
(
nameof(platformName),
"The platform name must be max. 50 characters."
);
}

if (platformUsername.HasValue && platformUsername.Value.Length > 100)
{
return new ArgumentOutOfRangeError
(
nameof(platformUsername),
"The platform username must be max. 100 characters."
);
}

if (metadata.HasValue && metadata.Value.Values.Any(m => m.Length > 100))
{
return new ArgumentOutOfRangeError
(
nameof(metadata),
"The metadata values must be max. 100 characters."
);
}

return await this.RestHttpClient.PutAsync<IApplicationRoleConnection>
(
$"users/@me/applications/{applicationID}/role-connection",
b => b.WithJson
(
json =>
{
json.Write("platform_name", platformName, this.JsonOptions);
json.Write("platform_username", platformUsername, this.JsonOptions);
json.Write("metadata", metadata, this.JsonOptions);
}
)
.WithRateLimitContext(this.RateLimitCache),
ct: ct
);
}
}
Loading

0 comments on commit 8ec2013

Please sign in to comment.