Skip to content

Commit c5e1bd9

Browse files
committed
Update Feast Java SDK client to support project namespacing
1 parent c46b892 commit c5e1bd9

4 files changed

Lines changed: 116 additions & 78 deletions

File tree

sdk/java/pom.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@
5656
<version>${junit.version}</version>
5757
<scope>test</scope>
5858
</dependency>
59+
<dependency>
60+
<groupId>org.junit.jupiter</groupId>
61+
<artifactId>junit-jupiter-api</artifactId>
62+
<version>${junit.version}</version>
63+
<scope>test</scope>
64+
</dependency>
5965
<dependency>
6066
<groupId>org.junit.jupiter</groupId>
6167
<artifactId>junit-jupiter-params</artifactId>
@@ -85,6 +91,14 @@
8591
<nohelp>true</nohelp>
8692
</configuration>
8793
</plugin>
94+
<plugin>
95+
<artifactId>maven-surefire-plugin</artifactId>
96+
<version>2.22.2</version>
97+
</plugin>
98+
<plugin>
99+
<artifactId>maven-failsafe-plugin</artifactId>
100+
<version>2.22.2</version>
101+
</plugin>
88102
</plugins>
89103
</build>
90104

sdk/java/src/main/java/com/gojek/feast/v1alpha1/FeastClient.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717
package com.gojek.feast.v1alpha1;
1818

19-
import feast.serving.ServingAPIProto.FeatureSetRequest;
19+
import feast.serving.ServingAPIProto.FeatureReference;
2020
import feast.serving.ServingAPIProto.GetFeastServingInfoRequest;
2121
import feast.serving.ServingAPIProto.GetFeastServingInfoResponse;
2222
import feast.serving.ServingAPIProto.GetOnlineFeaturesRequest;
@@ -61,13 +61,13 @@ public GetFeastServingInfoResponse getFeastServingInfo() {
6161
*
6262
* <p>See {@link #getOnlineFeatures(List, List, boolean)}
6363
*
64-
* @param featureIds list of feature id to retrieve, feature id follows this format
65-
* [feature_set_name]:[version]:[feature_name]
64+
* @param features list of string feature references to retrieve, feature reference follows this format
65+
* [project]/[name]:[version]
6666
* @param rows list of {@link Row} to select the entities to retrieve the features for
6767
* @return list of {@link Row} containing features
6868
*/
69-
public List<Row> getOnlineFeatures(List<String> featureIds, List<Row> rows) {
70-
return getOnlineFeatures(featureIds, rows, false);
69+
public List<Row> getOnlineFeatures(List<String> features, List<Row> rows, String defaultProject) {
70+
return getOnlineFeatures(features, rows, defaultProject, false);
7171
}
7272

7373
/**
@@ -93,8 +93,8 @@ public List<Row> getOnlineFeatures(List<String> featureIds, List<Row> rows) {
9393
* @return list of {@link Row} containing features
9494
*/
9595
public List<Row> getOnlineFeatures(
96-
List<String> featureIds, List<Row> rows, boolean omitEntitiesInResponse) {
97-
List<FeatureSetRequest> featureSets = RequestUtil.createFeatureSets(featureIds);
96+
List<String> featureRefStrings, List<Row> rows, String defaultProject, boolean omitEntitiesInResponse) {
97+
List<FeatureReference> features = RequestUtil.createFeatureRefs(featureRefStrings, defaultProject);
9898
List<EntityRow> entityRows =
9999
rows.stream()
100100
.map(
@@ -108,7 +108,7 @@ public List<Row> getOnlineFeatures(
108108
GetOnlineFeaturesResponse response =
109109
stub.getOnlineFeatures(
110110
GetOnlineFeaturesRequest.newBuilder()
111-
.addAllFeatureSets(featureSets)
111+
.addAllFeatures(features)
112112
.addAllEntityRows(entityRows)
113113
.setOmitEntitiesInResponse(omitEntitiesInResponse)
114114
.build());

sdk/java/src/main/java/com/gojek/feast/v1alpha1/RequestUtil.java

Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,60 +16,73 @@
1616
*/
1717
package com.gojek.feast.v1alpha1;
1818

19-
import feast.serving.ServingAPIProto.FeatureSetRequest;
19+
import feast.serving.ServingAPIProto.FeatureReference;
2020
import java.util.ArrayList;
21-
import java.util.HashMap;
2221
import java.util.List;
23-
import java.util.Map;
24-
import java.util.stream.Collectors;
25-
import org.apache.commons.lang3.tuple.ImmutablePair;
26-
import org.apache.commons.lang3.tuple.Pair;
2722

2823
@SuppressWarnings("WeakerAccess")
2924
public class RequestUtil {
30-
public static List<FeatureSetRequest> createFeatureSets(List<String> featureIds) {
31-
if (featureIds == null) {
32-
throw new IllegalArgumentException("featureIds cannot be null");
25+
26+
public static List<FeatureReference> createFeatureRefs(List<String> featureRefStrings,
27+
String defaultProject) {
28+
if (featureRefStrings == null) {
29+
throw new IllegalArgumentException("featureRefs cannot be null");
3330
}
3431

35-
// featureSetMap is a map of pair of feature set name and version -> a list of feature names
36-
Map<Pair<String, Integer>, List<String>> featureSetMap = new HashMap<>();
32+
List<FeatureReference> featureRefs = new ArrayList<>();
33+
34+
for (String featureRefString : featureRefStrings) {
35+
String project;
36+
String name;
37+
int version = 0;
38+
String[] featureSplit;
39+
String[] projectSplit = featureRefString.split("/");
3740

38-
for (String featureId : featureIds) {
39-
String[] parts = featureId.split(":");
40-
if (parts.length < 3) {
41+
if (projectSplit.length == 2) {
42+
project = projectSplit[0];
43+
featureSplit = projectSplit[1].split(":");
44+
} else if (projectSplit.length == 1) {
45+
project = defaultProject;
46+
featureSplit = projectSplit[0].split(":");
47+
} else {
4148
throw new IllegalArgumentException(
4249
String.format(
43-
"Feature id '%s' has invalid format. Expected format: <feature_set_name>:<version>:<feature_name>.",
44-
featureId));
50+
"Feature id '%s' has invalid format. Expected format: <project>:<feature-name>:<feature-version>.",
51+
featureRefString));
4552
}
46-
String featureSetName = parts[0];
47-
int featureSetVersion;
48-
try {
49-
featureSetVersion = Integer.parseInt(parts[1]);
50-
} catch (NumberFormatException e) {
53+
54+
if (featureSplit.length == 2) {
55+
name = featureSplit[0];
56+
try {
57+
version = Integer.parseInt(featureSplit[1]);
58+
} catch (NumberFormatException e) {
59+
throw new IllegalArgumentException(
60+
String.format(
61+
"Feature id '%s' contains invalid version. Expected format: <project>/<feature-name>:<feature-version>.",
62+
featureRefString));
63+
}
64+
} else if (projectSplit.length == 1) {
65+
name = featureSplit[0];
66+
} else {
5167
throw new IllegalArgumentException(
5268
String.format(
53-
"Feature id '%s' contains invalid version. Expected format: <feature_set_name>:<version>:<feature_name>.",
54-
parts[1]));
69+
"Feature id '%s' has invalid format. Expected format: <project>/<feature-name>:<feature-version>.",
70+
featureRefString));
5571
}
5672

57-
Pair<String, Integer> key = new ImmutablePair<>(featureSetName, featureSetVersion);
58-
if (!featureSetMap.containsKey(key)) {
59-
featureSetMap.put(key, new ArrayList<>());
73+
if (project.isEmpty() || name.isEmpty() || version < 0) {
74+
throw new IllegalArgumentException(
75+
String.format(
76+
"Feature id '%s' has invalid format. Expected format: <project>/<feature-name>:<feature-version>.",
77+
featureRefString));
6078
}
61-
String featureName = parts[2];
62-
featureSetMap.get(key).add(featureName);
79+
80+
featureRefs.add(
81+
FeatureReference.newBuilder().setName(name).setProject(project).setVersion(version)
82+
.build());
6383
}
6484

65-
return featureSetMap.entrySet().stream()
66-
.map(
67-
entry ->
68-
FeatureSetRequest.newBuilder()
69-
.setName(entry.getKey().getKey())
70-
.setVersion(entry.getKey().getValue())
71-
.addAllFeatureNames(entry.getValue())
72-
.build())
73-
.collect(Collectors.toList());
85+
; return featureRefs;
86+
7487
}
7588
}

sdk/java/src/test/java/com/gojek/feast/v1alpha1/RequestUtilTest.java

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import static org.junit.jupiter.api.Assertions.assertThrows;
2121

2222
import com.google.protobuf.TextFormat;
23-
import feast.serving.ServingAPIProto.FeatureSetRequest;
23+
import feast.serving.ServingAPIProto.FeatureReference;
2424
import java.util.Arrays;
2525
import java.util.Collections;
2626
import java.util.Comparator;
@@ -36,43 +36,56 @@ class RequestUtilTest {
3636
private static Stream<Arguments> provideValidFeatureIds() {
3737
return Stream.of(
3838
Arguments.of(
39-
Collections.singletonList("driver:1:driver_id"),
39+
Collections.singletonList("driver_project/driver_id:1"),
4040
Collections.singletonList(
41-
FeatureSetRequest.newBuilder()
42-
.setName("driver")
43-
.setVersion(1)
44-
.addFeatureNames("driver_id"))),
41+
FeatureReference.newBuilder()
42+
.setProject("driver_project")
43+
.setName("driver_id")
44+
.setVersion(1).build()
45+
)
46+
),
4547
Arguments.of(
46-
Arrays.asList("driver:1:driver_id", "driver:1:driver_name"),
47-
Collections.singletonList(
48-
FeatureSetRequest.newBuilder()
49-
.setName("driver")
48+
Arrays.asList("driver_project/driver_id:1", "driver_project/driver_name:1"),
49+
Arrays.asList(
50+
FeatureReference.newBuilder()
51+
.setProject("driver_project")
52+
.setName("driver_id")
53+
.setVersion(1)
54+
.build(),
55+
FeatureReference.newBuilder()
56+
.setProject("driver_project")
57+
.setName("driver_name")
5058
.setVersion(1)
51-
.addAllFeatureNames(Arrays.asList("driver_id", "driver_name"))
52-
.build())),
59+
.build())
60+
),
5361
Arguments.of(
54-
Arrays.asList("driver:1:driver_id", "driver:1:driver_name", "booking:2:booking_id"),
62+
Arrays.asList("driver_project/driver_id:1", "driver_project/driver_name:1", "booking_project/driver_name:1"),
5563
Arrays.asList(
56-
FeatureSetRequest.newBuilder()
57-
.setName("driver")
64+
FeatureReference.newBuilder()
65+
.setProject("driver_project")
66+
.setVersion(1)
67+
.setName("driver_id")
68+
.build(),
69+
FeatureReference.newBuilder()
70+
.setProject("driver_project")
5871
.setVersion(1)
59-
.addAllFeatureNames(Arrays.asList("driver_id", "driver_name"))
72+
.setName("driver_name")
6073
.build(),
61-
FeatureSetRequest.newBuilder()
62-
.setName("booking")
63-
.setVersion(2)
64-
.addFeatureNames("booking_id")
74+
FeatureReference.newBuilder()
75+
.setProject("booking_project")
76+
.setVersion(1)
77+
.setName("driver_name")
6578
.build())));
6679
}
6780

6881
@ParameterizedTest
6982
@MethodSource("provideValidFeatureIds")
7083
void createFeatureSets_ShouldReturnFeatureSetsForValidFeatureIds(
71-
List<String> input, List<FeatureSetRequest> expected) {
72-
List<FeatureSetRequest> actual = RequestUtil.createFeatureSets(input);
84+
List<String> input, List<FeatureReference> expected) {
85+
List<FeatureReference> actual = RequestUtil.createFeatureRefs(input, "my-project");
7386
// Order of the actual and expected featureSets do no not matter
74-
actual.sort(Comparator.comparing(FeatureSetRequest::getName));
75-
expected.sort(Comparator.comparing(FeatureSetRequest::getName));
87+
actual.sort(Comparator.comparing(FeatureReference::getName));
88+
expected.sort(Comparator.comparing(FeatureReference::getName));
7689
assertEquals(expected.size(), actual.size());
7790
for (int i = 0; i < expected.size(); i++) {
7891
String expectedString = TextFormat.printer().printToString(expected.get(i));
@@ -81,23 +94,21 @@ void createFeatureSets_ShouldReturnFeatureSetsForValidFeatureIds(
8194
}
8295
}
8396

84-
private static Stream<Arguments> provideInvalidFeatureIds() {
97+
private static Stream<Arguments> provideInvalidFeatureRefs() {
8598
return Stream.of(
86-
Arguments.of(Collections.singletonList("feature_set_only")),
87-
Arguments.of(Collections.singletonList("missing:feature_name")),
88-
Arguments.of(Collections.singletonList("invalid:version:value")),
99+
Arguments.of(Collections.singletonList("missing:bad_version")),
89100
Arguments.of(Collections.singletonList("")));
90101
}
91102

92103
@ParameterizedTest
93-
@MethodSource("provideInvalidFeatureIds")
94-
void createFeatureSets_ShouldThrowExceptionForInvalidFeatureIds(List<String> input) {
95-
assertThrows(IllegalArgumentException.class, () -> RequestUtil.createFeatureSets(input));
104+
@MethodSource("provideInvalidFeatureRefs")
105+
void createFeatureSets_ShouldThrowExceptionForInvalidFeatureRefs(List<String> input) {
106+
assertThrows(IllegalArgumentException.class, () -> RequestUtil.createFeatureRefs(input, "my-project"));
96107
}
97108

98109
@ParameterizedTest
99110
@NullSource
100-
void createFeatureSets_ShouldThrowExceptionForNullFeatureIds(List<String> input) {
101-
assertThrows(IllegalArgumentException.class, () -> RequestUtil.createFeatureSets(input));
111+
void createFeatureSets_ShouldThrowExceptionForNullFeatureRefs(List<String> input) {
112+
assertThrows(IllegalArgumentException.class, () -> RequestUtil.createFeatureRefs(input, "my-project"));
102113
}
103114
}

0 commit comments

Comments
 (0)