diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index a331fde6..1cd858b8 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -69,6 +69,12 @@ jobs:
working-directory: ./examples/MomentoApplication
run: dotnet run
+ - name: Run MomentoWeb example
+ id: validation-momento-web
+ continue-on-error: true
+ working-directory: ./examples/MomentoWeb
+ run: dotnet run
+
- name: Run doc API snippets
id: validation-docexampleapis
continue-on-error: true
@@ -78,7 +84,7 @@ jobs:
- name: Test example failure
id: test-example-failure
run: |
- if [[ "${{ steps.validation-momentoapplication.outcome }}" == "failure" || "${{ steps.validation-docexampleapis.outcome }}" == "failure" ]]
+ if [[ "${{ steps.validation-momentoapplication.outcome }}" == "failure" || "${{ steps.validation-momento-web.outcome }}" == "failure" || "${{ steps.validation-docexampleapis.outcome }}" == "failure" ]]
then
echo "failure=true" >> $GITHUB_OUTPUT
else
diff --git a/examples/DictionaryExample/DictionaryExample.csproj b/examples/DictionaryExample/DictionaryExample.csproj
index 3c7b1ac0..4855fcbb 100644
--- a/examples/DictionaryExample/DictionaryExample.csproj
+++ b/examples/DictionaryExample/DictionaryExample.csproj
@@ -9,6 +9,6 @@
-
+
diff --git a/examples/DisposableTokens/DisposableTokens.csproj b/examples/DisposableTokens/DisposableTokens.csproj
index b932b97d..1790e7e4 100644
--- a/examples/DisposableTokens/DisposableTokens.csproj
+++ b/examples/DisposableTokens/DisposableTokens.csproj
@@ -16,6 +16,6 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
diff --git a/examples/DocExampleApis/DocExampleApis.csproj b/examples/DocExampleApis/DocExampleApis.csproj
index c6ace0d3..52e8cc67 100644
--- a/examples/DocExampleApis/DocExampleApis.csproj
+++ b/examples/DocExampleApis/DocExampleApis.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/examples/MomentoApplication/MomentoApplication.csproj b/examples/MomentoApplication/MomentoApplication.csproj
index 9a586eb2..3939e673 100644
--- a/examples/MomentoApplication/MomentoApplication.csproj
+++ b/examples/MomentoApplication/MomentoApplication.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/examples/MomentoApplication/Program.cs b/examples/MomentoApplication/Program.cs
index 52adf158..06f020d4 100644
--- a/examples/MomentoApplication/Program.cs
+++ b/examples/MomentoApplication/Program.cs
@@ -1,7 +1,4 @@
-using System;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-using Momento.Sdk;
+using Momento.Sdk;
using Momento.Sdk.Auth;
using Momento.Sdk.Config;
using Momento.Sdk.Exceptions;
@@ -17,7 +14,7 @@
await AdvancedExamples.ListCachesExample(client);
await AdvancedExamples.SetGetDeleteExample(client);
await AdvancedExamples.DeleteCacheExample(client);
- AdvancedExamples.EagerConnectionExample();
+ await AdvancedExamples.EagerConnectionExample();
Console.WriteLine("\nProgram has completed successfully.");
@@ -103,7 +100,7 @@ public static async Task SetGetDeleteExample(ICacheClient client) {
var deleteKeyResponse = await client.DeleteAsync(CACHE_NAME, KEY);
if (deleteKeyResponse is CacheDeleteResponse.Error deleteKeyError)
{
- // Also not considred fatal.
+ // Also not considered fatal.
Console.WriteLine($"Error deleting key: {deleteKeyError.Message}!");
}
}
@@ -115,7 +112,7 @@ public static async Task DeleteCacheExample(ICacheClient client) {
if (deleteCacheResponse is DeleteCacheResponse.Error deleteCacheError)
{
// Report fatal error and exit
- Console.WriteLine("Error deleting cache: {deleteCacheError.Message}. Exiting.");
+ Console.WriteLine($"Error deleting cache: {deleteCacheError.Message}. Exiting.");
Environment.Exit(1);
}
}
@@ -126,22 +123,17 @@ public static async Task DeleteCacheExample(ICacheClient client) {
/// when it is instantiated, and to specify a distinct timeout for the
/// connection to be established.
///
- public static void EagerConnectionExample()
+ public static async Task EagerConnectionExample()
{
- ICredentialProvider authProvider = new EnvMomentoTokenProvider("MOMENTO_API_KEY");
- TimeSpan defaultTtl = TimeSpan.FromSeconds(60);
+ var authProvider = new EnvMomentoTokenProvider("MOMENTO_API_KEY");
+ var defaultTtl = TimeSpan.FromSeconds(60);
var config = Configurations.Laptop.V1();
- var eagerConnectionConfig = config.WithTransportStrategy(config.TransportStrategy.WithEagerConnectionTimeout(TimeSpan.FromSeconds(10)));
+ var eagerConnectionTimeout = TimeSpan.FromSeconds(10);
+
Console.WriteLine("Creating a momento client with eager connection");
- using (CacheClient client = new CacheClient(eagerConnectionConfig, authProvider, defaultTtl)) {
+ using (var client = await CacheClient.CreateAsync(config, authProvider, defaultTtl, eagerConnectionTimeout))
+ {
Console.WriteLine("Successfully created a momento client with eager connection");
}
-
- var grpcConfig = config.TransportStrategy.GrpcConfig.WithMinNumGrpcChannels(4);
- var transportStrategy = config.TransportStrategy
- .WithEagerConnectionTimeout(TimeSpan.FromSeconds(20))
- .WithGrpcConfig(grpcConfig);
- config = config.WithTransportStrategy(transportStrategy);
-
}
}
diff --git a/examples/MomentoApplication/README.md b/examples/MomentoApplication/README.md
index 9116d6c3..1b479f5d 100644
--- a/examples/MomentoApplication/README.md
+++ b/examples/MomentoApplication/README.md
@@ -31,13 +31,13 @@ MOMENTO_API_KEY= dotnet run
## Error Handling
-Errors that occur in calls to SimpleCacheClient methods are surfaced to developers as part of the return values of
+Errors that occur in calls to CacheClient methods are surfaced to developers as part of the return values of
the calls, as opposed to by throwing exceptions. This makes them more visible, and allows your IDE to be more
helpful in ensuring that you've handled the ones you care about. (For more on our philosophy about this, see our
blog post on why [Exceptions are bugs](https://www.gomomento.com/blog/exceptions-are-bugs). And send us any
feedback you have!)
-The preferred way of interpreting the return values from SimpleCacheClient methods is using [Pattern matching](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching). Here's a quick example:
+The preferred way of interpreting the return values from CacheClient methods is using [Pattern matching](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching). Here's a quick example:
```csharp
CacheGetResponse getResponse = await client.GetAsync(CACHE_NAME, KEY);
@@ -68,5 +68,5 @@ if (getResponse is CacheGetResponse.Error errorResponse)
}
```
-Note that, outside of SimpleCacheClient responses, exceptions can occur and should be handled as usual. For example, trying to instantiate a SimpleCacheClient with an invalid authentication token will result in an
+Note that, outside of CacheClient responses, exceptions can occur and should be handled as usual. For example, trying to instantiate a CacheClient with an invalid authentication token will result in an
IllegalArgumentException being thrown.
diff --git a/examples/MomentoExamples.sln b/examples/MomentoExamples.sln
index 82597a3a..811b6eb5 100644
--- a/examples/MomentoExamples.sln
+++ b/examples/MomentoExamples.sln
@@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisposableTokens", "Disposa
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TopicExample", "TopicExample\TopicExample.csproj", "{24E1BEE9-8D9D-44B8-B9D5-EBBF08534934}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MomentoWeb", "MomentoWeb\MomentoWeb.csproj", "{DE3FDC58-9342-4B93-A4DB-C4039A235BA3}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -67,6 +69,10 @@ Global
{24E1BEE9-8D9D-44B8-B9D5-EBBF08534934}.Debug|Any CPU.Build.0 = Debug|Any CPU
{24E1BEE9-8D9D-44B8-B9D5-EBBF08534934}.Release|Any CPU.ActiveCfg = Release|Any CPU
{24E1BEE9-8D9D-44B8-B9D5-EBBF08534934}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DE3FDC58-9342-4B93-A4DB-C4039A235BA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DE3FDC58-9342-4B93-A4DB-C4039A235BA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DE3FDC58-9342-4B93-A4DB-C4039A235BA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DE3FDC58-9342-4B93-A4DB-C4039A235BA3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/examples/MomentoFSharpApplication/MomentoFSharpApplication.fsproj b/examples/MomentoFSharpApplication/MomentoFSharpApplication.fsproj
index 1e00d78c..5bde5cd3 100644
--- a/examples/MomentoFSharpApplication/MomentoFSharpApplication.fsproj
+++ b/examples/MomentoFSharpApplication/MomentoFSharpApplication.fsproj
@@ -10,6 +10,6 @@
-
+
diff --git a/examples/MomentoLoadGen/MomentoLoadGen.csproj b/examples/MomentoLoadGen/MomentoLoadGen.csproj
index df0f8aa0..02fe6203 100644
--- a/examples/MomentoLoadGen/MomentoLoadGen.csproj
+++ b/examples/MomentoLoadGen/MomentoLoadGen.csproj
@@ -28,6 +28,6 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
diff --git a/examples/MomentoUsage/MomentoUsage.csproj b/examples/MomentoUsage/MomentoUsage.csproj
index b932b97d..1790e7e4 100644
--- a/examples/MomentoUsage/MomentoUsage.csproj
+++ b/examples/MomentoUsage/MomentoUsage.csproj
@@ -16,6 +16,6 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
diff --git a/examples/MomentoUsage/README.md b/examples/MomentoUsage/README.md
index b1f64e6e..8c255940 100644
--- a/examples/MomentoUsage/README.md
+++ b/examples/MomentoUsage/README.md
@@ -27,13 +27,13 @@ MOMENTO_API_KEY= dotnet run
## Error Handling
-Errors that occur in calls to SimpleCacheClient methods are surfaced to developers as part of the return values of
+Errors that occur in calls to CacheClient methods are surfaced to developers as part of the return values of
the calls, as opposed to by throwing exceptions. This makes them more visible, and allows your IDE to be more
helpful in ensuring that you've handled the ones you care about. (For more on our philosophy about this, see our
blog post on why [Exceptions are bugs](https://www.gomomento.com/blog/exceptions-are-bugs). And send us any
feedback you have!)
-The preferred way of interpreting the return values from SimpleCacheClient methods is using [Pattern matching](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching). Here's a quick example:
+The preferred way of interpreting the return values from CacheClient methods is using [Pattern matching](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching). Here's a quick example:
```csharp
CacheGetResponse getResponse = await client.GetAsync(CACHE_NAME, KEY);
@@ -64,5 +64,5 @@ if (getResponse is CacheGetResponse.Error errorResponse)
}
```
-Note that, outside of SimpleCacheClient responses, exceptions can occur and should be handled as usual. For example, trying to instantiate a SimpleCacheClient with an invalid authentication token will result in an
+Note that, outside of CacheClient responses, exceptions can occur and should be handled as usual. For example, trying to instantiate a CacheClient with an invalid authentication token will result in an
IllegalArgumentException being thrown.
diff --git a/examples/MomentoWeb/MomentoWeb.csproj b/examples/MomentoWeb/MomentoWeb.csproj
new file mode 100644
index 00000000..742ae634
--- /dev/null
+++ b/examples/MomentoWeb/MomentoWeb.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+ PackageReference
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/MomentoWeb/Program.cs b/examples/MomentoWeb/Program.cs
new file mode 100644
index 00000000..6d362641
--- /dev/null
+++ b/examples/MomentoWeb/Program.cs
@@ -0,0 +1,138 @@
+using Momento.Sdk;
+using Momento.Sdk.Auth;
+using Momento.Sdk.Config;
+using Momento.Sdk.Exceptions;
+using Momento.Sdk.Responses;
+
+var authProvider = new EnvMomentoTokenProvider("MOMENTO_API_KEY");
+
+var defaultTtl = TimeSpan.FromSeconds(60);
+
+using (ICacheClient client = new CacheClient(Configurations.Laptop.V1(), authProvider, defaultTtl))
+{
+ await AdvancedExamples.CreateCacheExample(client);
+ await AdvancedExamples.ListCachesExample(client);
+ await AdvancedExamples.SetGetDeleteExample(client);
+ await AdvancedExamples.DeleteCacheExample(client);
+ await AdvancedExamples.EagerConnectionExample();
+
+ Console.WriteLine("\nProgram has completed successfully.");
+}
+
+
+public class AdvancedExamples {
+ private const string CacheName = "momento-example";
+ private const string Key = "MyKey";
+ private const string Value = "MyData";
+
+ public static async Task CreateCacheExample(ICacheClient client)
+ {
+
+ Console.WriteLine($"Creating cache {CacheName}");
+ var createCacheResponse = await client.CreateCacheAsync(CacheName);
+ // Check the create response for an error and handle as appropriate.
+ if (createCacheResponse is CreateCacheResponse.Error createCacheError)
+ {
+ if (createCacheError.ErrorCode == MomentoErrorCode.LIMIT_EXCEEDED_ERROR)
+ {
+ Console.WriteLine("Error: cache limit exceeded. We need to talk to support@moentohq.com! Exiting.");
+ }
+ else
+ {
+ Console.WriteLine($"Error creating cache: {createCacheError.Message}. Exiting.");
+ }
+ // Any error is considered fatal.
+ Environment.Exit(1);
+ }
+ // If there's already a cache by this name, alert the user.
+ if (createCacheResponse is CreateCacheResponse.CacheAlreadyExists)
+ {
+ Console.WriteLine($"A cache with the name {CacheName} already exists");
+ }
+ }
+
+ public static async Task ListCachesExample(ICacheClient client) {
+ Console.WriteLine("\nListing caches:");
+ ListCachesResponse listCachesResponse = await client.ListCachesAsync();
+ if (listCachesResponse is ListCachesResponse.Success listCachesSuccess)
+ {
+ foreach (CacheInfo cacheInfo in listCachesSuccess.Caches)
+ {
+ Console.WriteLine($"- {cacheInfo.Name}");
+ }
+ }
+ else if (listCachesResponse is ListCachesResponse.Error listCachesError)
+ {
+ // We do not consider this a fatal error, so we just report it.
+ Console.WriteLine($"Error listing caches: {listCachesError.Message}");
+ }
+ }
+
+ public static async Task SetGetDeleteExample(ICacheClient client) {
+ Console.WriteLine($"\nSetting key: {Key} with value: {Value}");
+ var setResponse = await client.SetAsync(CacheName, Key, Value);
+ if (setResponse is CacheSetResponse.Error setError)
+ {
+ // Warn the user of the error and exit.
+ Console.WriteLine($"Error setting value: {setError.Message}. Exiting.");
+ Environment.Exit(1);
+ }
+
+ Console.WriteLine($"\nGetting value for key: {Key}");
+ CacheGetResponse getResponse = await client.GetAsync(CacheName, Key);
+ if (getResponse is CacheGetResponse.Hit getHit)
+ {
+ Console.WriteLine($"Looked up value: {getHit.ValueString}, Stored value: {Value}");
+ }
+ else if (getResponse is CacheGetResponse.Miss)
+ {
+ // This shouldn't be fatal but should be reported.
+ Console.WriteLine($"Error: got a cache miss for {Key}!");
+ }
+ else if (getResponse is CacheGetResponse.Error getError)
+ {
+ // Also not considered fatal.
+ Console.WriteLine($"Error getting value: {getError.Message}!");
+ }
+
+ Console.WriteLine($"\nDeleting key {Key}");
+ var deleteKeyResponse = await client.DeleteAsync(CacheName, Key);
+ if (deleteKeyResponse is CacheDeleteResponse.Error deleteKeyError)
+ {
+ // Also not considered fatal.
+ Console.WriteLine($"Error deleting key: {deleteKeyError.Message}!");
+ }
+ }
+
+ public static async Task DeleteCacheExample(ICacheClient client) {
+
+ Console.WriteLine($"\nDeleting cache {CacheName}");
+ var deleteCacheResponse = await client.DeleteCacheAsync(CacheName);
+ if (deleteCacheResponse is DeleteCacheResponse.Error deleteCacheError)
+ {
+ // Report fatal error and exit
+ Console.WriteLine($"Error deleting cache: {deleteCacheError.Message}. Exiting.");
+ Environment.Exit(1);
+ }
+ }
+
+ ///
+ /// By default, cache clients will connect lazily when the first request is
+ /// issued. This example shows how to configure a client to connect eagerly
+ /// when it is instantiated, and to specify a distinct timeout for the
+ /// connection to be established.
+ ///
+ public static async Task EagerConnectionExample()
+ {
+ var authProvider = new EnvMomentoTokenProvider("MOMENTO_API_KEY");
+ var defaultTtl = TimeSpan.FromSeconds(60);
+ var config = Configurations.Laptop.V1();
+ var eagerConnectionTimeout = TimeSpan.FromSeconds(10);
+
+ Console.WriteLine("Creating a momento client with eager connection");
+ using (var client = await CacheClient.CreateAsync(config, authProvider, defaultTtl, eagerConnectionTimeout))
+ {
+ Console.WriteLine("Successfully created a momento client with eager connection");
+ }
+ }
+}
diff --git a/examples/MomentoWeb/README.md b/examples/MomentoWeb/README.md
new file mode 100644
index 00000000..85300b34
--- /dev/null
+++ b/examples/MomentoWeb/README.md
@@ -0,0 +1,72 @@
+
+
+# Momento Web Example
+
+This example is intended to show off the Momento web cache client
+functionality, including:
+
+* instantiating the Momento client
+* creating, listing, and deleting caches
+* setting and retrieving values for cache keys
+* response processing and error handling
+
+## Prerequisites
+
+* [`dotnet`](https://dotnet.microsoft.com/en-us/download) 6.0 or higher is required
+* A Momento API key is required. You can generate one using the [Momento Console](https://console.gomomento.com/api-keys).
+
+## Running the application example
+
+Run the following from within the `examples` directory:
+
+```bash
+MOMENTO_API_KEY= dotnet run --project MomentoWeb
+```
+
+Within the `MomentoWeb` directory you can run:
+
+```bash
+MOMENTO_API_KEY= dotnet run
+```
+
+## Error Handling
+
+Errors that occur in calls to CacheClient methods are surfaced to developers as part of the return values of
+the calls, as opposed to by throwing exceptions. This makes them more visible, and allows your IDE to be more
+helpful in ensuring that you've handled the ones you care about. (For more on our philosophy about this, see our
+blog post on why [Exceptions are bugs](https://www.gomomento.com/blog/exceptions-are-bugs). And send us any
+feedback you have!)
+
+The preferred way of interpreting the return values from CacheClient methods is using [Pattern matching](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching). Here's a quick example:
+
+```csharp
+CacheGetResponse getResponse = await client.GetAsync(CACHE_NAME, KEY);
+if (getResponse is CacheGetResponse.Hit hitResponse)
+{
+ Console.WriteLine($"\nLooked up value: {hitResponse.String()}, Stored value: {VALUE}");
+} else {
+ // you can handle other cases via pattern matching in `else if` blocks, or a default case
+ // via the `else` block. For each return value your IDE should be able to give you code
+ // completion indicating the other possible types; in this case, `CacheGetResponse.Miss` and
+ // `CacheGetResponse.Error`.
+}
+```
+
+Using this approach, you get a type-safe `hitResponse` object in the case of a cache hit. But if the cache read
+results in a Miss or an error, you'll also get a type-safe object that you can use to get more info about what happened.
+
+In cases where you get an error response, `Error` types will always include an `ErrorCode` that you can use to check
+the error type:
+
+```csharp
+CacheGetResponse getResponse = await client.GetAsync(CACHE_NAME, KEY);
+if (getResponse is CacheGetResponse.Error errorResponse)
+{
+ if (errorResponse.ErrorCode == MomentoErrorCode.TIMEOUT_ERROR) {
+ // this would represent a client-side timeout, and you could fall back to your original data source
+ }
+}
+```
+
+Note that, outside of CacheClient responses, exceptions can occur and should be handled as usual. For example, trying to instantiate a CacheClient with an invalid authentication token will result in an
+IllegalArgumentException being thrown.
diff --git a/examples/README.md b/examples/README.md
index 671391a9..f79fe0b9 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -18,13 +18,13 @@ MOMENTO_API_KEY= dotnet run --project MomentoApplication
## Error Handling
-Errors that occur in calls to SimpleCacheClient methods are surfaced to developers as part of the return values of
+Errors that occur in calls to CacheClient methods are surfaced to developers as part of the return values of
the calls, as opposed to by throwing exceptions. This makes them more visible, and allows your IDE to be more
helpful in ensuring that you've handled the ones you care about. (For more on our philosophy about this, see our
blog post on why [Exceptions are bugs](https://www.gomomento.com/blog/exceptions-are-bugs). And send us any
feedback you have!)
-The preferred way of interpreting the return values from SimpleCacheClient methods is using [Pattern matching](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching). Here's a quick example:
+The preferred way of interpreting the return values from CacheClient methods is using [Pattern matching](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching). Here's a quick example:
```csharp
CacheGetResponse getResponse = await client.GetAsync(CACHE_NAME, KEY);
@@ -55,7 +55,7 @@ if (getResponse is CacheGetResponse.Error errorResponse)
}
```
-Note that, outside of SimpleCacheClient responses, exceptions can occur and should be handled as usual. For example, trying to instantiate a SimpleCacheClient with an invalid authentication token will result in an
+Note that, outside of CacheClient responses, exceptions can occur and should be handled as usual. For example, trying to instantiate a CacheClient with an invalid authentication token will result in an
IllegalArgumentException being thrown.
## Running the load generator example
diff --git a/examples/TopicExample/TopicExample.csproj b/examples/TopicExample/TopicExample.csproj
index 3c7b1ac0..4855fcbb 100644
--- a/examples/TopicExample/TopicExample.csproj
+++ b/examples/TopicExample/TopicExample.csproj
@@ -9,6 +9,6 @@
-
+