diff --git a/momento-sdk/src/intTest/java/momento/sdk/ListTest.java b/momento-sdk/src/intTest/java/momento/sdk/ListTest.java index bc06ebd8..bd44e5d1 100644 --- a/momento-sdk/src/intTest/java/momento/sdk/ListTest.java +++ b/momento-sdk/src/intTest/java/momento/sdk/ListTest.java @@ -1,5 +1,6 @@ package momento.sdk; +import static momento.sdk.TestUtils.randomBytes; import static momento.sdk.TestUtils.randomString; import static org.assertj.core.api.Assertions.assertThat; @@ -443,6 +444,63 @@ public void listConcatenateFrontStringHappyPath() { .containsExactlyElementsOf(newExpectedList)); } + @Test + public void listConcatenateFrontByteArrayListNameByteArrayHappyPath() { + final byte[] listName = randomBytes(); + final List oldValues = + Arrays.asList("val1".getBytes(), "val2".getBytes(), "val3".getBytes()); + final List newValues = + Arrays.asList("val4".getBytes(), "val5".getBytes(), "val6".getBytes()); + + assertThat(cacheClient.listFetch(cacheName, listName)) + .succeedsWithin(FIVE_SECONDS) + .isInstanceOf(ListFetchResponse.Miss.class); + + assertThat( + cacheClient.listConcatenateFrontByteArray( + cacheName, listName, oldValues, null, CollectionTtl.fromCacheTtl())) + .succeedsWithin(FIVE_SECONDS) + .isInstanceOf(ListConcatenateFrontResponse.Success.class); + + assertThat(cacheClient.listFetch(cacheName, listName)) + .succeedsWithin(FIVE_SECONDS) + .asInstanceOf(InstanceOfAssertFactories.type(ListFetchResponse.Hit.class)) + .satisfies( + hit -> + assertThat(hit.valueListByteArray()) + .hasSize(3) + .containsExactlyElementsOf(oldValues)); + + assertThat(cacheClient.listConcatenateFrontByteArray(cacheName, listName, newValues)) + .succeedsWithin(FIVE_SECONDS) + .isInstanceOf(ListConcatenateFrontResponse.Success.class); + + final Iterable expectedList = Iterables.concat(newValues, oldValues); + assertThat(cacheClient.listFetch(cacheName, listName)) + .succeedsWithin(FIVE_SECONDS) + .asInstanceOf(InstanceOfAssertFactories.type(ListFetchResponse.Hit.class)) + .satisfies( + hit -> + assertThat(hit.valueListByteArray()) + .hasSize(6) + .containsExactlyElementsOf(expectedList)); + + // Add the original values again and truncate the list to 6 items + assertThat(cacheClient.listConcatenateFrontByteArray(cacheName, listName, oldValues, 6)) + .succeedsWithin(FIVE_SECONDS) + .isInstanceOf(ListConcatenateFrontResponse.Success.class); + + final Iterable newExpectedList = Iterables.concat(oldValues, newValues); + assertThat(cacheClient.listFetch(cacheName, listName)) + .succeedsWithin(FIVE_SECONDS) + .asInstanceOf(InstanceOfAssertFactories.type(ListFetchResponse.Hit.class)) + .satisfies( + hit -> + assertThat(hit.valueListByteArray()) + .hasSize(6) + .containsExactlyElementsOf(newExpectedList)); + } + @Test public void listConcatenateFrontByteArrayHappyPath() { final String listName = randomString(); @@ -563,13 +621,14 @@ public void shouldFailListConcatenateFrontWhenNullListName() { // With ttl specified in method signature assertThat( cacheClient.listConcatenateFrontByteArray( - cacheName, null, byteArrayValues, 0, CollectionTtl.fromCacheTtl())) + cacheName, (String) null, byteArrayValues, 0, CollectionTtl.fromCacheTtl())) .succeedsWithin(FIVE_SECONDS) .asInstanceOf(InstanceOfAssertFactories.type(ListConcatenateFrontResponse.Error.class)) .satisfies(error -> assertThat(error).hasCauseInstanceOf(InvalidArgumentException.class)); // Without ttl specified in method signature - assertThat(cacheClient.listConcatenateFrontByteArray(cacheName, null, byteArrayValues, 0)) + assertThat( + cacheClient.listConcatenateFrontByteArray(cacheName, (String) null, byteArrayValues, 0)) .succeedsWithin(FIVE_SECONDS) .asInstanceOf(InstanceOfAssertFactories.type(ListConcatenateFrontResponse.Error.class)) .satisfies(error -> assertThat(error).hasCauseInstanceOf(InvalidArgumentException.class)); @@ -1280,6 +1339,34 @@ public void shouldFailListRemoveValueWhenNullElement() { .satisfies(error -> assertThat(error).hasCauseInstanceOf(InvalidArgumentException.class)); } + @Test + public void shouldRetainAllValuesWhenListRetainListNameByteArrayWithPositiveStartEndIndices() { + final String listName = randomString(); + final byte[] listNameByteArray = listName.getBytes(); + final List stringValues = Arrays.asList("val1", "val2", "val3", "val4"); + + assertThat( + cacheClient.listConcatenateFront( + cacheName, listName, stringValues, null, CollectionTtl.fromCacheTtl())) + .succeedsWithin(FIVE_SECONDS) + .isInstanceOf(ListConcatenateFrontResponse.Success.class); + + assertThat(cacheClient.listFetch(cacheName, listNameByteArray, null, null)) + .succeedsWithin(FIVE_SECONDS) + .asInstanceOf(InstanceOfAssertFactories.type(ListFetchResponse.Hit.class)) + .satisfies(hit -> assertThat(hit.valueListString()).hasSize(4).containsAll(stringValues)); + + assertThat(cacheClient.listRetain(cacheName, listNameByteArray, 1, 3)) + .succeedsWithin(FIVE_SECONDS) + .isInstanceOf(ListRetainResponse.Success.class); + + List expectedList = Arrays.asList("val2", "val3"); + assertThat(cacheClient.listFetch(cacheName, listNameByteArray, null, null)) + .succeedsWithin(FIVE_SECONDS) + .asInstanceOf(InstanceOfAssertFactories.type(ListFetchResponse.Hit.class)) + .satisfies(hit -> assertThat(hit.valueListString()).hasSize(2).containsAll(expectedList)); + } + @Test public void shouldRetainAllValuesWhenListRetainWithPositiveStartEndIndices() { final String listName = randomString(); diff --git a/momento-sdk/src/main/java/momento/sdk/CacheClient.java b/momento-sdk/src/main/java/momento/sdk/CacheClient.java index 9438c179..6381f0e9 100644 --- a/momento-sdk/src/main/java/momento/sdk/CacheClient.java +++ b/momento-sdk/src/main/java/momento/sdk/CacheClient.java @@ -1605,6 +1605,28 @@ public CompletableFuture listConcatenateFront( return scsDataClient.listConcatenateFront(cacheName, listName, values, null, null); } + /** + * Adds the given values to the front of a list. + * + * @param cacheName The cache containing the list. + * @param listName The list in which to add the values. + * @param values The values to add to the list. + * @param truncateBackToSize If the list exceeds this length, remove excess from the front of the + * list. Must be positive. Will not truncate if not provided. + * @param ttl TTL for the set in cache. This TTL takes precedence over the TTL used when + * initializing a cache client. Defaults to the client's TTL if not provided. + * @return Future containing the result of the list concatenate front operation: {@link + * ListConcatenateFrontResponse.Success} or {@link ListConcatenateFrontResponse.Error}. + */ + public CompletableFuture listConcatenateFrontByteArray( + @Nonnull String cacheName, + @Nonnull byte[] listName, + @Nonnull Iterable values, + @Nullable Integer truncateBackToSize, + @Nullable CollectionTtl ttl) { + return scsDataClient.listConcatenateFrontByteArray( + cacheName, listName, values, truncateBackToSize, ttl); + } /** * Adds the given values to the front of a list. * @@ -1628,6 +1650,26 @@ public CompletableFuture listConcatenateFrontByteA cacheName, listName, values, truncateBackToSize, ttl); } + /** + * Adds the given values to the front of a list. Refreshes the list with the client's default TTL. + * + * @param cacheName The cache containing the list. + * @param listName The list in which to add the values. + * @param values The values to add to the list. + * @param truncateBackToSize If the list exceeds this length, remove excess from the front of the + * list. Must be positive. Will not truncate if not provided. + * @return Future containing the result of the list concatenate front operation: {@link + * ListConcatenateFrontResponse.Success} or {@link ListConcatenateFrontResponse.Error}. + */ + public CompletableFuture listConcatenateFrontByteArray( + @Nonnull String cacheName, + @Nonnull byte[] listName, + @Nonnull Iterable values, + @Nullable Integer truncateBackToSize) { + return scsDataClient.listConcatenateFrontByteArray( + cacheName, listName, values, truncateBackToSize, null); + } + /** * Adds the given values to the front of a list. Refreshes the list with the client's default TTL. * @@ -1648,6 +1690,20 @@ public CompletableFuture listConcatenateFrontByteA cacheName, listName, values, truncateBackToSize, null); } + /** + * Adds the given values to the front of a list. Refreshes the list with the client's default TTL. + * + * @param cacheName The cache containing the list. + * @param listName The list in which to add the values. + * @param values The values to add to the list. + * @return Future containing the result of the list concatenate front operation: {@link + * ListConcatenateFrontResponse.Success} or {@link ListConcatenateFrontResponse.Error}. + */ + public CompletableFuture listConcatenateFrontByteArray( + @Nonnull String cacheName, @Nonnull byte[] listName, @Nonnull Iterable values) { + return scsDataClient.listConcatenateFrontByteArray(cacheName, listName, values, null, null); + } + /** * Adds the given values to the front of a list. Refreshes the list with the client's default TTL. * @@ -1662,6 +1718,27 @@ public CompletableFuture listConcatenateFrontByteA return scsDataClient.listConcatenateFrontByteArray(cacheName, listName, values, null, null); } + /** + * Fetches all elements of a list between the given indices. + * + * @param cacheName The cache containing the list. + * @param listName The list to fetch. + * @param startIndex Start index (inclusive) for the fetch operation. Defaults to 0 if not + * provided. + * @param endIndex End index (exclusive) for the fetch operation. Defaults to the end of the list + * if not provided. + * @return Future containing the result of the list fetch operation: {@link ListFetchResponse.Hit} + * containing the fetched data, {@link ListFetchResponse.Miss} if no data was found, or {@link + * ListFetchResponse.Error}. + */ + public CompletableFuture listFetch( + @Nonnull String cacheName, + @Nonnull byte[] listName, + @Nullable Integer startIndex, + @Nullable Integer endIndex) { + return scsDataClient.listFetch(cacheName, listName, startIndex, endIndex); + } + /** * Fetches all elements of a list between the given indices. * @@ -1683,6 +1760,20 @@ public CompletableFuture listFetch( return scsDataClient.listFetch(cacheName, listName, startIndex, endIndex); } + /** + * Fetches all elements of a list between the given indices. + * + * @param cacheName The cache containing the list. + * @param listName The list to fetch. + * @return Future containing the result of the list fetch operation: {@link ListFetchResponse.Hit} + * containing the fetched data, {@link ListFetchResponse.Miss} if no data was found, or {@link + * ListFetchResponse.Error}. + */ + public CompletableFuture listFetch( + @Nonnull String cacheName, @Nonnull byte[] listName) { + return scsDataClient.listFetch(cacheName, listName, null, null); + } + /** * Fetches all elements of a list between the given indices. * @@ -1987,6 +2078,24 @@ public CompletableFuture listRemoveValue( return scsDataClient.listRemoveValue(cacheName, listName, value); } + /** + * Retains only the elements of a list that are between the given indices. + * + * @param cacheName The cache containing the list. + * @param listName The list to cut down. + * @param startIndex - Start index (inclusive) for list retain operation. + * @param endIndex - End index (exclusive) for list retain operation. + * @return Future containing the result of the list retain value operation: {@link + * ListRetainResponse.Success} or {@link ListRetainResponse.Error}. + */ + public CompletableFuture listRetain( + @Nonnull String cacheName, + @Nonnull byte[] listName, + @Nullable Integer startIndex, + @Nullable Integer endIndex) { + return scsDataClient.listRetain(cacheName, listName, startIndex, endIndex); + } + /** * Retains only the elements of a list that are between the given indices. * diff --git a/momento-sdk/src/main/java/momento/sdk/ScsDataClient.java b/momento-sdk/src/main/java/momento/sdk/ScsDataClient.java index 0eeca00c..0038b20b 100644 --- a/momento-sdk/src/main/java/momento/sdk/ScsDataClient.java +++ b/momento-sdk/src/main/java/momento/sdk/ScsDataClient.java @@ -951,6 +951,29 @@ CompletableFuture listConcatenateFront( } } + CompletableFuture listConcatenateFrontByteArray( + @Nonnull String cacheName, + @Nonnull byte[] listName, + @Nonnull Iterable values, + @Nullable Integer truncateBackToSize, + @Nullable CollectionTtl ttl) { + try { + checkCacheNameValid(cacheName); + checkListNameValid(listName); + ensureValidValue(values); + + if (ttl == null) { + ttl = CollectionTtl.of(itemDefaultTtl); + } + + return sendListConcatenateFront( + cacheName, convert(listName), convertByteArrayIterable(values), truncateBackToSize, ttl); + } catch (Exception e) { + return CompletableFuture.completedFuture( + new ListConcatenateFrontResponse.Error(CacheServiceExceptionMapper.convert(e))); + } + } + CompletableFuture listConcatenateFrontByteArray( @Nonnull String cacheName, @Nonnull String listName, @@ -974,6 +997,22 @@ CompletableFuture listConcatenateFrontByteArray( } } + CompletableFuture listFetch( + @Nonnull String cacheName, + @Nonnull byte[] listName, + @Nullable Integer startIndex, + @Nullable Integer endIndex) { + try { + checkCacheNameValid(cacheName); + checkListNameValid(listName); + checkIndexRangeValid(startIndex, endIndex); + return sendListFetch(cacheName, convert(listName), startIndex, endIndex); + } catch (Exception e) { + return CompletableFuture.completedFuture( + new ListFetchResponse.Error(CacheServiceExceptionMapper.convert(e))); + } + } + CompletableFuture listFetch( @Nonnull String cacheName, @Nonnull String listName, @@ -1150,6 +1189,23 @@ CompletableFuture listRemoveValue( } } + CompletableFuture listRetain( + @Nonnull String cacheName, + @Nonnull byte[] listName, + @Nullable Integer startIndex, + @Nullable Integer endIndex) { + try { + checkCacheNameValid(cacheName); + checkListNameValid(listName); + checkIndexRangeValid(startIndex, endIndex); + + return sendListRetain(cacheName, convert(listName), startIndex, endIndex); + } catch (Exception e) { + return CompletableFuture.completedFuture( + new ListRetainResponse.Error(CacheServiceExceptionMapper.convert(e))); + } + } + CompletableFuture listRetain( @Nonnull String cacheName, @Nonnull String listName, diff --git a/momento-sdk/src/main/java/momento/sdk/ValidationUtils.java b/momento-sdk/src/main/java/momento/sdk/ValidationUtils.java index 71091287..effa43a0 100644 --- a/momento-sdk/src/main/java/momento/sdk/ValidationUtils.java +++ b/momento-sdk/src/main/java/momento/sdk/ValidationUtils.java @@ -73,6 +73,12 @@ static void checkDictionaryNameValid(String dictionaryName) { } } + static void checkListNameValid(byte[] listName) { + if (listName == null) { + throw new InvalidArgumentException(LIST_NAME_CANNOT_BE_NULL); + } + } + static void checkListNameValid(String listName) { if (listName == null) { throw new InvalidArgumentException(LIST_NAME_CANNOT_BE_NULL);