Skip to content

Commit

Permalink
Merge pull request #512 from momentohq/add-token-id-to-subscriptions
Browse files Browse the repository at this point in the history
feat: add TokenId support to GenerateDisposableToken and topic subscriptions
  • Loading branch information
cprice404 authored Nov 7, 2023
2 parents 5ca7411 + b47d1e9 commit 304aa0d
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 12 deletions.
4 changes: 2 additions & 2 deletions src/Momento.Sdk/AuthClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public AuthClient(IAuthConfiguration config, ICredentialProvider authProvider)
scsTokenClient = new ScsTokenClient(config, authProvider.AuthToken, authProvider.TokenEndpoint);
}

public async Task<GenerateDisposableTokenResponse> GenerateDisposableTokenAsync(DisposableTokenScope scope, ExpiresIn expiresIn)
public async Task<GenerateDisposableTokenResponse> GenerateDisposableTokenAsync(DisposableTokenScope scope, ExpiresIn expiresIn, string? tokenId = null)

Check warning on line 22 in src/Momento.Sdk/AuthClient.cs

View workflow job for this annotation

GitHub Actions / build_csharp (windows-latest, net461)

Missing XML comment for publicly visible type or member 'AuthClient.GenerateDisposableTokenAsync(DisposableTokenScope, ExpiresIn, string?)'
{
try {
Utils.CheckValidDisposableTokenExpiry(expiresIn);
Expand All @@ -28,7 +28,7 @@ public async Task<GenerateDisposableTokenResponse> GenerateDisposableTokenAsync(
{
return new GenerateDisposableTokenResponse.Error(new InvalidArgumentException(e.Message));
}
return await scsTokenClient.GenerateDisposableToken(scope, expiresIn);
return await scsTokenClient.GenerateDisposableToken(scope, expiresIn, tokenId);
}

/// <inheritdoc />
Expand Down
2 changes: 1 addition & 1 deletion src/Momento.Sdk/Exceptions/UnknownException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ public class UnknownException : SdkException
/// <include file="../docs.xml" path='docs/class[@name="SdkException"]/constructor/*' />
public UnknownException(string message, MomentoErrorTransportDetails? transportDetails = null, Exception? e = null) : base(MomentoErrorCode.UNKNOWN_ERROR, message, transportDetails, e)
{
this.MessageWrapper = "Unknown error has occurred";
this.MessageWrapper = "Unknown error has occurred: " + InnerException;
}
}
2 changes: 1 addition & 1 deletion src/Momento.Sdk/IAuthClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ namespace Momento.Sdk;
public interface IAuthClient : IDisposable
{
public Task<GenerateDisposableTokenResponse> GenerateDisposableTokenAsync(DisposableTokenScope scope,
ExpiresIn expiresIn);
ExpiresIn expiresIn, string? tokenId = null);
}
5 changes: 3 additions & 2 deletions src/Momento.Sdk/Internal/ScsTokenClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ private DateTime CalculateDeadline()
private const string RequestTypeAuthGenerateDisposableToken = "GENERATE_DISPOSABLE_TOKEN";

public async Task<GenerateDisposableTokenResponse> GenerateDisposableToken(
DisposableTokenScope scope, ExpiresIn expiresIn
DisposableTokenScope scope, ExpiresIn expiresIn, string? tokenId = null
) {
Permissions permissions;
try
Expand All @@ -59,7 +59,8 @@ public async Task<GenerateDisposableTokenResponse> GenerateDisposableToken(
{
Expires = new _GenerateDisposableTokenRequest.Types.Expires() { ValidForSeconds = (uint)expiresIn.Seconds() },

Check warning on line 60 in src/Momento.Sdk/Internal/ScsTokenClient.cs

View workflow job for this annotation

GitHub Actions / build_csharp (ubuntu-latest, net6.0)

Nullable value type may be null.

Check warning on line 60 in src/Momento.Sdk/Internal/ScsTokenClient.cs

View workflow job for this annotation

GitHub Actions / build_csharp (windows-latest, net461)

Nullable value type may be null.
AuthToken = this.authToken,
Permissions = permissions
Permissions = permissions,
TokenId = tokenId ?? ""
};
_logger.LogTraceExecutingGenericRequest(RequestTypeAuthGenerateDisposableToken);
var response = await grpcManager.Client.generateDisposableToken(
Expand Down
4 changes: 2 additions & 2 deletions src/Momento.Sdk/Internal/ScsTopicClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,10 +242,10 @@ public async Task Subscribe()
{
case _TopicValue.KindOneofCase.Text:
_logger.LogTraceTopicMessageReceived("text", _cacheName, _topicName);
return new TopicMessage.Text(message.Item.Value);
return new TopicMessage.Text(message.Item.Value, message.Item.PublisherId == "" ? null : message.Item.PublisherId);
case _TopicValue.KindOneofCase.Binary:
_logger.LogTraceTopicMessageReceived("binary", _cacheName, _topicName);
return new TopicMessage.Binary(message.Item.Value);
return new TopicMessage.Binary(message.Item.Value, message.Item.PublisherId == "" ? null : message.Item.PublisherId);
case _TopicValue.KindOneofCase.None:
default:
_logger.LogTraceTopicMessageReceived("unknown", _cacheName, _topicName);
Expand Down
2 changes: 1 addition & 1 deletion src/Momento.Sdk/Momento.Sdk.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
<ItemGroup>
<PackageReference Include="Grpc.Net.Client" Version="2.49.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="7.0.0" />
<PackageReference Include="Momento.Protos" Version="0.88.0" />
<PackageReference Include="Momento.Protos" Version="0.91.1" />
<PackageReference Include="JWT" Version="9.0.3" />
<PackageReference Include="System.Threading.Channels" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
Expand Down
18 changes: 16 additions & 2 deletions src/Momento.Sdk/Responses/TopicMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,22 @@ public class Text : TopicMessage
/// <summary>
/// A topic message containing a text value.
/// </summary>
public Text(_TopicValue topicValue)
public Text(_TopicValue topicValue, string? tokenId = null)
{
Value = topicValue.Text;
TokenId = tokenId;
}

/// <summary>
/// The text value of this message.
/// </summary>
public string Value { get; }

/// <summary>
/// The TokenId that was used to publish the message, or null if the token did not have an id.
/// This can be used to securely identify the sender of a message.
/// </summary>
public string? TokenId { get; }
}

/// <summary>
Expand All @@ -62,15 +69,22 @@ public class Binary : TopicMessage
/// <summary>
/// A topic message containing a binary value.
/// </summary>
public Binary(_TopicValue topicValue)
public Binary(_TopicValue topicValue, string? tokenId = null)
{
Value = topicValue.Binary.ToByteArray();
TokenId = tokenId;
}

/// <summary>
/// The binary value of this message.
/// </summary>
public byte[] Value { get; }

/// <summary>
/// The TokenId that was used to publish the message, or null if the token did not have an id.
/// This can be used to securely identify the sender of a message.
/// </summary>
public string? TokenId { get; }
}

/// <include file="../docs.xml" path='docs/class[@name="Error"]/description/*' />
Expand Down
40 changes: 39 additions & 1 deletion tests/Integration/Momento.Sdk.Tests/AuthClientTopicTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,13 @@ public AuthClientTopicTest(

private async Task<ITopicClient> GetClientForTokenScope(DisposableTokenScope scope)
{
var response = await authClient.GenerateDisposableTokenAsync(scope, ExpiresIn.Minutes(2));
return await GetClientForTokenScope(scope, null);
}


private async Task<ITopicClient> GetClientForTokenScope(DisposableTokenScope scope, string? tokenId)
{
var response = await authClient.GenerateDisposableTokenAsync(scope, ExpiresIn.Minutes(2), tokenId);
Assert.True(response is GenerateDisposableTokenResponse.Success, $"Unexpected response: {response}");
string authToken = "";
if (response is GenerateDisposableTokenResponse.Success token)
Expand All @@ -43,6 +49,7 @@ private async Task<ITopicClient> GetClientForTokenScope(DisposableTokenScope sco
var authProvider = new StringMomentoTokenProvider(authToken);
return new TopicClient(TopicConfigurations.Laptop.latest(), authProvider);
}


private async Task PublishToTopic(string cache, string topic, string value, ITopicClient? client = null)
{
Expand All @@ -51,9 +58,17 @@ private async Task PublishToTopic(string cache, string topic, string value, ITop
Assert.True(response is TopicPublishResponse.Success, $"Unexpected response: {response}");
}


private async Task ExpectTextFromSubscription(
TopicSubscribeResponse.Subscription subscription, string expectedText
)
{
await ExpectTextFromSubscription(subscription, expectedText, null);
}

private async Task ExpectTextFromSubscription(
TopicSubscribeResponse.Subscription subscription, string expectedText, string? tokenId
)
{
var cts = new CancellationTokenSource();
cts.CancelAfter(5000);
Expand All @@ -64,6 +79,7 @@ private async Task ExpectTextFromSubscription(
Assert.True(message is TopicMessage.Text, $"Unexpected response: {message}");
if (message is TopicMessage.Text textMsg) {
Assert.Equal(expectedText, textMsg.Value);
Assert.Equal(tokenId, textMsg.TokenId);
gotText = true;
}
cts.Cancel();
Expand Down Expand Up @@ -338,6 +354,28 @@ public async Task GenerateDisposableTopicAuthToken_ReadWrite_HappyPath()
);
await GenerateDisposableTopicAuthToken_ReadWrite_Common(readwriteTopicClient, messageValue);
}


[Fact]
public async Task GenerateDisposableTopicAuthToken_ReadWrite_WithTokenId_HappyPath()
{
const string messageValue = "hello";
const string tokenId = "tacoToken";
var readwriteTopicClient = await GetClientForTokenScope(
DisposableTokenScopes.TopicPublishSubscribe(cacheName, topicName),
tokenId
);
var subscribeResponse = await readwriteTopicClient.SubscribeAsync(cacheName, topicName);
Assert.True(subscribeResponse is TopicSubscribeResponse.Subscription, $"Unexpected response: {subscribeResponse}");
var publishResponse = await readwriteTopicClient.PublishAsync(cacheName, topicName, messageValue);
Assert.True(publishResponse is TopicPublishResponse.Success, $"Unexpected response: {publishResponse}");
if (subscribeResponse is TopicSubscribeResponse.Subscription subscription)
{
await PublishToTopic(cacheName, topicName, messageValue, readwriteTopicClient);
await ExpectTextFromSubscription(subscription, messageValue, tokenId);
}
}


[Fact]
public async Task GenerateDisposableTopicAuthToken_ReadWrite_NamePrefix_HappyPath()
Expand Down

0 comments on commit 304aa0d

Please sign in to comment.