Skip to content

Commit

Permalink
Add content-ID to REST API v2 entries endpoint (#5879)
Browse files Browse the repository at this point in the history
Fixes #5878
  • Loading branch information
snazy committed Jan 17, 2023
1 parent 69cc884 commit f779ce0
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 57 deletions.
Expand Up @@ -26,6 +26,7 @@
import static org.projectnessie.gc.repository.NessieRepositoryConnector.CONTENT_BATCH_SIZE;

import java.util.Map;
import java.util.UUID;
import java.util.function.IntFunction;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
Expand Down Expand Up @@ -76,7 +77,9 @@ public void batchContentsFetchingSomeKeysNoValues() throws Exception {

IntFunction<ContentKey> key = i -> ContentKey.of("key-" + i);
IntFunction<EntriesResponse.Entry> keyEntry =
i -> EntriesResponse.Entry.entry(key.apply(i), Content.Type.ICEBERG_TABLE);
i ->
EntriesResponse.Entry.entry(
key.apply(i), Content.Type.ICEBERG_TABLE, UUID.randomUUID().toString());

GetEntriesBuilder getEntries = mock(GetEntriesBuilder.class);
when(getEntries.reference(ref)).thenReturn(getEntries);
Expand Down Expand Up @@ -107,7 +110,9 @@ public void batchContentsFetchingSomeKeysAndValues() throws Exception {
IntFunction<ContentKey> key = i -> ContentKey.of("key-" + i);
IntFunction<Content> content = i -> IcebergTable.of("meta-" + i, 42, 43, 44, 45, "cid-" + i);
IntFunction<EntriesResponse.Entry> keyEntry =
i -> EntriesResponse.Entry.entry(key.apply(i), Content.Type.ICEBERG_TABLE);
i ->
EntriesResponse.Entry.entry(
key.apply(i), Content.Type.ICEBERG_TABLE, UUID.randomUUID().toString());

GetEntriesBuilder getEntries = mock(GetEntriesBuilder.class);
when(getEntries.reference(ref)).thenReturn(getEntries);
Expand Down Expand Up @@ -142,7 +147,9 @@ public void batchContentsFetchingBatchKeys() throws Exception {
IntFunction<ContentKey> key = i -> ContentKey.of("key-" + i);
IntFunction<Content> content = i -> IcebergTable.of("meta-" + i, 42, 43, 44, 45, "cid-" + i);
IntFunction<EntriesResponse.Entry> keyEntry =
i -> EntriesResponse.Entry.entry(key.apply(i), Content.Type.ICEBERG_TABLE);
i ->
EntriesResponse.Entry.entry(
key.apply(i), Content.Type.ICEBERG_TABLE, UUID.randomUUID().toString());

GetEntriesBuilder getEntries = mock(GetEntriesBuilder.class);
when(getEntries.reference(ref)).thenReturn(getEntries);
Expand Down Expand Up @@ -180,7 +187,9 @@ public void batchContentsFetchingBatchKeysPlus1() throws Exception {
IntFunction<ContentKey> key = i -> ContentKey.of("key-" + i);
IntFunction<Content> content = i -> IcebergTable.of("meta-" + i, 42, 43, 44, 45, "cid-" + i);
IntFunction<EntriesResponse.Entry> keyEntry =
i -> EntriesResponse.Entry.entry(key.apply(i), Content.Type.ICEBERG_TABLE);
i ->
EntriesResponse.Entry.entry(
key.apply(i), Content.Type.ICEBERG_TABLE, UUID.randomUUID().toString());

GetEntriesBuilder getEntries = mock(GetEntriesBuilder.class);
when(getEntries.reference(ref)).thenReturn(getEntries);
Expand Down Expand Up @@ -231,7 +240,9 @@ public void batchContentsFetchingTenBatches() throws Exception {
IntFunction<ContentKey> key = i -> ContentKey.of("key-" + i);
IntFunction<Content> content = i -> IcebergTable.of("meta-" + i, 42, 43, 44, 45, "cid-" + i);
IntFunction<EntriesResponse.Entry> keyEntry =
i -> EntriesResponse.Entry.entry(key.apply(i), Content.Type.ICEBERG_TABLE);
i ->
EntriesResponse.Entry.entry(
key.apply(i), Content.Type.ICEBERG_TABLE, UUID.randomUUID().toString());

GetEntriesBuilder getEntries = mock(GetEntriesBuilder.class);
when(getEntries.reference(ref)).thenReturn(getEntries);
Expand Down
Expand Up @@ -60,7 +60,6 @@
import org.projectnessie.model.Content;
import org.projectnessie.model.ContentKey;
import org.projectnessie.model.Detached;
import org.projectnessie.model.EntriesResponse;
import org.projectnessie.model.IcebergTable;
import org.projectnessie.model.LogResponse.LogEntry;
import org.projectnessie.model.Operation.Put;
Expand Down Expand Up @@ -232,14 +231,11 @@ public Stream<Map.Entry<ContentKey, Content>> allContents(
// 1L is the very first, commit - the first non-live commit
// 2L is the oldest live-commit - need to fetch the visible keys from that one
soft.assertThat(l).isEqualTo(2L);
Stream<EntriesResponse.Entry> keysStream =
List<ContentKey> keys =
IntStream.range(0, markAndSweep.numKeysAtCutOff)
.mapToObj(
i -> markAndSweep.numToContentKey(markAndSweep.numCommits + i))
.map(ck -> EntriesResponse.Entry.entry(ck, ICEBERG_TABLE));

List<ContentKey> keys =
keysStream.map(EntriesResponse.Entry::getName).collect(Collectors.toList());
.collect(Collectors.toList());
soft.assertThat(keys).hasSize(markAndSweep.numKeysAtCutOff);
return keys.stream()
.map(
Expand Down
14 changes: 13 additions & 1 deletion model/src/main/java/org/projectnessie/model/EntriesResponse.java
Expand Up @@ -15,11 +15,14 @@
*/
package org.projectnessie.model;

import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.util.List;
import javax.annotation.Nullable;
import javax.validation.constraints.NotNull;
import org.immutables.value.Value;
import org.projectnessie.model.ser.Views;

@Value.Immutable
@JsonSerialize(as = ImmutableEntriesResponse.class)
Expand Down Expand Up @@ -50,8 +53,17 @@ static ImmutableEntry.Builder builder() {
@Value.Parameter(order = 1)
ContentKey getName();

@JsonView(Views.V2.class)
@Value.Parameter(order = 3)
@Nullable // for V1 backwards compatibility
String getContentId();

static Entry entry(ContentKey name, Content.Type type) {
return ImmutableEntry.of(name, type);
return entry(name, type, null);
}

static Entry entry(ContentKey name, Content.Type type, String contentId) {
return ImmutableEntry.of(name, type, contentId);
}
}
}
Expand Up @@ -177,17 +177,18 @@ public void verifyAllContentAndOperationTypes() throws BaseNessieClientServerExc

List<Entry> entries =
getApi().getEntries().refName(branch.getName()).stream().collect(Collectors.toList());
List<Entry> expect =
Map<ContentKey, Content.Type> expect =
contentAndOps.stream()
.filter(c -> c.operation instanceof Put)
.map(c -> Entry.entry(c.operation.getKey(), c.type))
.collect(Collectors.toList());
List<Entry> notExpect =
.collect(Collectors.toMap(c -> c.operation.getKey(), c -> c.type));
Map<ContentKey, Content.Type> notExpect =
contentAndOps.stream()
.filter(c -> c.operation instanceof Delete)
.map(c -> Entry.entry(c.operation.getKey(), c.type))
.collect(Collectors.toList());
soft.assertThat(entries).containsAll(expect).doesNotContainAnyElementsOf(notExpect);
.collect(Collectors.toMap(c -> c.operation.getKey(), c -> c.type));
soft.assertThat(entries)
.map(e -> Maps.immutableEntry(e.getName(), e.getType()))
.containsAll(expect.entrySet())
.doesNotContainAnyElementsOf(notExpect.entrySet());

// Diff against of committed HEAD and previous commit must yield the content in the
// Put operations
Expand Down Expand Up @@ -277,7 +278,9 @@ public void verifyContentAndOperationTypesIndividually(
List<Entry> entries =
getApi().getEntries().refName(branch.getName()).stream().collect(Collectors.toList());
soft.assertThat(entries)
.containsExactly(Entry.entry(fixedContentKey, contentAndOperationType.type));
.hasSize(1)
.extracting(Entry::getName, Entry::getType)
.containsExactly(tuple(fixedContentKey, contentAndOperationType.type));

// Diff against of committed HEAD and previous commit must yield the content in the
// Put operation
Expand Down Expand Up @@ -334,7 +337,8 @@ public void verifyContentAndOperationTypesIndividually(
List<Entry> entries =
getApi().getEntries().refName(branch.getName()).stream().collect(Collectors.toList());
soft.assertThat(entries)
.containsExactly(Entry.entry(fixedContentKey, contentAndOperationType.type));
.extracting(Entry::getName, Entry::getType)
.containsExactly(tuple(fixedContentKey, contentAndOperationType.type));

// Diff against of committed HEAD and previous commit must yield the content in the
// Put operations
Expand Down
Expand Up @@ -15,11 +15,13 @@
*/
package org.projectnessie.jaxrs.tests;

import static java.util.Arrays.asList;
import static com.google.common.collect.Maps.immutableEntry;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
Expand Down Expand Up @@ -68,10 +70,11 @@ public void filterEntriesByType(ReferenceMode refMode) throws BaseNessieClientSe
.commit();
List<Entry> entries =
getApi().getEntries().reference(refMode.transform(branch)).get().getEntries();
List<Entry> expected =
asList(
Entry.entry(a, Content.Type.ICEBERG_TABLE), Entry.entry(b, Content.Type.ICEBERG_VIEW));
soft.assertThat(entries).containsExactlyInAnyOrderElementsOf(expected);
Map<ContentKey, Content.Type> expect =
ImmutableMap.of(a, Content.Type.ICEBERG_TABLE, b, Content.Type.ICEBERG_VIEW);
soft.assertThat(entries)
.map(e -> immutableEntry(e.getName(), e.getType()))
.containsExactlyInAnyOrderElementsOf(expect.entrySet());

entries =
getApi()
Expand All @@ -80,7 +83,9 @@ public void filterEntriesByType(ReferenceMode refMode) throws BaseNessieClientSe
.filter("entry.contentType=='ICEBERG_TABLE'")
.get()
.getEntries();
soft.assertThat(entries).containsExactly(expected.get(0));
soft.assertThat(entries)
.map(e -> immutableEntry(e.getName(), e.getType()))
.containsExactly(immutableEntry(a, Content.Type.ICEBERG_TABLE));

entries =
getApi()
Expand All @@ -89,7 +94,9 @@ public void filterEntriesByType(ReferenceMode refMode) throws BaseNessieClientSe
.filter("entry.contentType=='ICEBERG_VIEW'")
.get()
.getEntries();
soft.assertThat(entries).containsExactly(expected.get(1));
soft.assertThat(entries)
.map(e -> immutableEntry(e.getName(), e.getType()))
.containsExactly(immutableEntry(b, Content.Type.ICEBERG_VIEW));

entries =
getApi()
Expand All @@ -98,7 +105,9 @@ public void filterEntriesByType(ReferenceMode refMode) throws BaseNessieClientSe
.filter("entry.contentType in ['ICEBERG_TABLE', 'ICEBERG_VIEW']")
.get()
.getEntries();
soft.assertThat(entries).containsExactlyInAnyOrderElementsOf(expected);
soft.assertThat(entries)
.map(e -> immutableEntry(e.getName(), e.getType()))
.containsExactlyInAnyOrderElementsOf(expect.entrySet());
}

@ParameterizedTest
Expand Down Expand Up @@ -321,7 +330,8 @@ public void filterEntriesByNamespaceAndPrefixDepth(ReferenceMode refMode)
.getEntries();
soft.assertThat(entries)
.hasSize(1)
.containsExactly(Entry.entry(ContentKey.of("a"), Content.Type.NAMESPACE));
.map(e -> immutableEntry(e.getName(), e.getType()))
.containsExactly(immutableEntry(ContentKey.of("a"), Content.Type.NAMESPACE));

entries =
getApi()
Expand All @@ -333,10 +343,11 @@ public void filterEntriesByNamespaceAndPrefixDepth(ReferenceMode refMode)
.getEntries();
soft.assertThat(entries)
.hasSize(3)
.map(e -> immutableEntry(e.getName(), e.getType()))
.containsExactlyInAnyOrder(
Entry.entry(ContentKey.of("a", "boo"), Content.Type.NAMESPACE),
Entry.entry(ContentKey.of("a", "b"), Content.Type.NAMESPACE),
Entry.entry(ContentKey.of("a", "thirdTable"), Content.Type.ICEBERG_TABLE));
immutableEntry(ContentKey.of("a", "boo"), Content.Type.NAMESPACE),
immutableEntry(ContentKey.of("a", "b"), Content.Type.NAMESPACE),
immutableEntry(ContentKey.of("a", "thirdTable"), Content.Type.ICEBERG_TABLE));

entries =
getApi()
Expand All @@ -348,9 +359,10 @@ public void filterEntriesByNamespaceAndPrefixDepth(ReferenceMode refMode)
.getEntries();
soft.assertThat(entries)
.hasSize(2)
.map(e -> immutableEntry(e.getName(), e.getType()))
.containsExactlyInAnyOrder(
Entry.entry(ContentKey.of("a", "b", "fourthTable"), Content.Type.ICEBERG_TABLE),
Entry.entry(ContentKey.of("a", "b", "c"), Content.Type.NAMESPACE));
immutableEntry(ContentKey.of("a", "b", "fourthTable"), Content.Type.ICEBERG_TABLE),
immutableEntry(ContentKey.of("a", "b", "c"), Content.Type.NAMESPACE));

entries =
getApi()
Expand All @@ -362,9 +374,10 @@ public void filterEntriesByNamespaceAndPrefixDepth(ReferenceMode refMode)
.getEntries();
soft.assertThat(entries)
.hasSize(2)
.map(e -> immutableEntry(e.getName(), e.getType()))
.containsExactlyInAnyOrder(
Entry.entry(ContentKey.of("a", "b", "c", "secondTable"), Content.Type.ICEBERG_TABLE),
Entry.entry(ContentKey.of("a", "b", "c", "firstTable"), Content.Type.ICEBERG_TABLE));
immutableEntry(ContentKey.of("a", "b", "c", "secondTable"), Content.Type.ICEBERG_TABLE),
immutableEntry(ContentKey.of("a", "b", "c", "firstTable"), Content.Type.ICEBERG_TABLE));

entries =
getApi()
Expand All @@ -386,10 +399,11 @@ public void filterEntriesByNamespaceAndPrefixDepth(ReferenceMode refMode)
.getEntries();
soft.assertThat(entries)
.hasSize(3)
.map(e -> immutableEntry(e.getName(), e.getType()))
.containsExactlyInAnyOrder(
Entry.entry(ContentKey.of("a", "boo", "fifthTable"), Content.Type.ICEBERG_TABLE),
Entry.entry(ContentKey.of("a", "b", "fourthTable"), Content.Type.ICEBERG_TABLE),
Entry.entry(ContentKey.of("a", "b", "c"), Content.Type.NAMESPACE));
immutableEntry(ContentKey.of("a", "boo", "fifthTable"), Content.Type.ICEBERG_TABLE),
immutableEntry(ContentKey.of("a", "b", "fourthTable"), Content.Type.ICEBERG_TABLE),
immutableEntry(ContentKey.of("a", "b", "c"), Content.Type.NAMESPACE));

if (ReferenceMode.DETACHED != refMode) {
// check that implicit namespaces are properly detected
Expand Down Expand Up @@ -418,10 +432,10 @@ public void fetchEntriesByNamelessReference() throws BaseNessieClientServerExcep
.commit();
List<Entry> entries = getApi().getEntries().hashOnRef(branch.getHash()).get().getEntries();
soft.assertThat(entries)
.containsExactlyInAnyOrderElementsOf(
Arrays.<Entry>asList(
Entry.entry(a, Content.Type.ICEBERG_TABLE),
Entry.entry(b, Content.Type.ICEBERG_VIEW)));
.map(e -> immutableEntry(e.getName(), e.getType()))
.containsExactlyInAnyOrder(
immutableEntry(a, Content.Type.ICEBERG_TABLE),
immutableEntry(b, Content.Type.ICEBERG_VIEW));
}

private void checkNamespaces(
Expand Down
Expand Up @@ -214,10 +214,10 @@ public void testNamespaceDeletion() throws BaseNessieClientServerException {
contentAndOps.stream().map(c -> c.operation).forEach(commit::operation);
commit.commit();

List<Entry> entries =
List<ContentKey> entries =
contentAndOps.stream()
.filter(c -> c.operation instanceof Put)
.map(c -> Entry.entry(c.operation.getKey(), c.type))
.map(c -> c.operation.getKey())
.collect(Collectors.toList());

CommitMultipleOperationsBuilder commit2 =
Expand All @@ -226,16 +226,13 @@ public void testNamespaceDeletion() throws BaseNessieClientServerException {
.branch(branch)
.commitMeta(CommitMeta.fromMessage("create namespaces"));
entries.stream()
.map(e -> e.getName().getNamespace())
.map(ContentKey::getNamespace)
.distinct()
.forEach(
ns -> {
commit2.operation(Put.of(ContentKey.of(ns.getElements()), ns));
});
.forEach(ns -> commit2.operation(Put.of(ContentKey.of(ns.getElements()), ns)));
commit2.commit();

for (Entry e : entries) {
Namespace namespace = e.getName().getNamespace();
for (ContentKey contentKey : entries) {
Namespace namespace = contentKey.getNamespace();
soft.assertThat(
getApi()
.getNamespace()
Expand Down
Expand Up @@ -43,6 +43,7 @@
import org.projectnessie.model.ContentKey;
import org.projectnessie.model.DiffResponse;
import org.projectnessie.model.DiffResponse.DiffEntry;
import org.projectnessie.model.EntriesResponse;
import org.projectnessie.model.IcebergTable;
import org.projectnessie.model.ImmutableBranch;
import org.projectnessie.model.ImmutableOperations;
Expand Down Expand Up @@ -121,6 +122,17 @@ public void testBasic() {
.as(Branch.class);
Assertions.assertNotEquals(newReference.getHash(), commitResponse.getHash());

EntriesResponse entries =
rest()
.get("trees/tree/{branch}/entries", newReference.getName())
.then()
.statusCode(200)
.extract()
.as(EntriesResponse.class);
assertThat(entries.getEntries())
.hasSize(1)
.allSatisfy(e -> assertThat(e.getContentId()).isNull());

// fetch the content
IcebergTable table =
rest()
Expand Down

0 comments on commit f779ce0

Please sign in to comment.