Skip to content

Commit

Permalink
Update rest endpoints (#1121)
Browse files Browse the repository at this point in the history
Signed-off-by: Terence <terencelimxp@gmail.com>
  • Loading branch information
terryyylim authored Nov 2, 2020
1 parent 34811e7 commit 663daec
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ protected void configure(HttpSecurity http) throws Exception {

if (feastProperties.securityProperties().isDisableRestControllerAuth()) {
matchersToBypass.add("/api/v1/**");
matchersToBypass.add("/api/v2/**");
}

// Bypasses security/authentication for the following paths
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,12 @@
import feast.proto.core.CoreServiceProto.GetFeatureStatisticsRequest;
import feast.proto.core.CoreServiceProto.GetFeatureStatisticsRequest.Builder;
import feast.proto.core.CoreServiceProto.GetFeatureStatisticsResponse;
import feast.proto.core.CoreServiceProto.ListEntitiesRequest;
import feast.proto.core.CoreServiceProto.ListEntitiesResponse;
import feast.proto.core.CoreServiceProto.ListFeatureSetsRequest;
import feast.proto.core.CoreServiceProto.ListFeatureSetsResponse;
import feast.proto.core.CoreServiceProto.ListFeatureTablesRequest;
import feast.proto.core.CoreServiceProto.ListFeatureTablesResponse;
import feast.proto.core.CoreServiceProto.ListFeaturesRequest;
import feast.proto.core.CoreServiceProto.ListFeaturesResponse;
import feast.proto.core.CoreServiceProto.ListProjectsResponse;
Expand All @@ -55,7 +59,7 @@
@RestController
@CrossOrigin
@Slf4j
@RequestMapping(value = "/api/v1", produces = "application/json")
@RequestMapping(value = "/api", produces = "application/json")
public class CoreServiceRestController {

private final FeastProperties feastProperties;
Expand All @@ -80,7 +84,7 @@ public CoreServiceRestController(
*
* @return (200 OK) Returns {@link GetFeastCoreVersionResponse} in JSON.
*/
@RequestMapping(value = "/version", method = RequestMethod.GET)
@RequestMapping(value = "/v1/version", method = RequestMethod.GET)
public GetFeastCoreVersionResponse getVersion() {
GetFeastCoreVersionResponse response =
GetFeastCoreVersionResponse.newBuilder().setVersion(feastProperties.getVersion()).build();
Expand All @@ -99,7 +103,7 @@ public GetFeastCoreVersionResponse getVersion() {
* default. Asterisk can be used as wildcard to filter * feature sets.
* @return (200 OK) Return {@link ListFeatureSetsResponse} in JSON.
*/
@RequestMapping(value = "/feature-sets", method = RequestMethod.GET)
@RequestMapping(value = "/v1/feature-sets", method = RequestMethod.GET)
public ListFeatureSetsResponse listFeatureSets(
@RequestParam(defaultValue = Project.DEFAULT_NAME) String project, @RequestParam String name)
throws InvalidProtocolBufferException {
Expand All @@ -120,7 +124,7 @@ public ListFeatureSetsResponse listFeatureSets(
* <code>default</code>.
* @return (200 OK) Return {@link ListFeaturesResponse} in JSON.
*/
@RequestMapping(value = "/features", method = RequestMethod.GET)
@RequestMapping(value = "/v1/features", method = RequestMethod.GET)
public ListFeaturesResponse listFeatures(
@RequestParam String[] entities, @RequestParam(required = false) Optional<String> project) {
ListFeaturesRequest.Filter.Builder filterBuilder =
Expand Down Expand Up @@ -152,7 +156,7 @@ public ListFeaturesResponse listFeatures(
* in the feature set will be used for statistics.
* @return (200 OK) Returns {@link GetFeatureStatisticsResponse} in JSON.
*/
@RequestMapping(value = "/feature-statistics", method = RequestMethod.GET)
@RequestMapping(value = "/v1/feature-statistics", method = RequestMethod.GET)
public GetFeatureStatisticsResponse getFeatureStatistics(
@RequestParam(name = "feature_set_id") String featureSetId,
@RequestParam(required = false) Optional<String[]> features,
Expand Down Expand Up @@ -186,14 +190,44 @@ public GetFeatureStatisticsResponse getFeatureStatistics(
*
* @return (200 OK) Returns {@link ListProjectsResponse} in JSON.
*/
@RequestMapping(value = "/projects", method = RequestMethod.GET)
@RequestMapping(value = "/v1/projects", method = RequestMethod.GET)
public ListProjectsResponse listProjects() {
List<Project> projects = projectService.listProjects();
return ListProjectsResponse.newBuilder()
.addAllProjects(projects.stream().map(Project::getName).collect(Collectors.toList()))
.build();
}

/**
* GET /entities : Retrieve a list of Entities according to filtering parameters of Feast project
* name. If none matches, an empty JSON response is returned.
*
* @param project Request Parameter: Name of feast project to search in.
* @return (200 OK) Return {@link ListEntitiesResponse} in JSON.
*/
@RequestMapping(value = "/v2/entities", method = RequestMethod.GET)
public ListEntitiesResponse listEntities(
@RequestParam(defaultValue = Project.DEFAULT_NAME) String project) {
ListEntitiesRequest.Filter.Builder filterBuilder =
ListEntitiesRequest.Filter.newBuilder().setProject(project);
return specService.listEntities(filterBuilder.build());
}

/**
* GET /feature-tables : Retrieve a list of Feature Tables according to filtering parameters of
* Feast project name. If none matches, an empty JSON response is returned.
*
* @param project Request Parameter: Name of feast project to search in.
* @return (200 OK) Return {@link ListFeatureTablesResponse} in JSON.
*/
@RequestMapping(value = "/v2/feature-tables", method = RequestMethod.GET)
public ListFeatureTablesResponse listFeatureTables(
@RequestParam(defaultValue = Project.DEFAULT_NAME) String project) {
ListFeatureTablesRequest.Filter.Builder filterBuilder =
ListFeatureTablesRequest.Filter.newBuilder().setProject(project);
return specService.listFeatureTables(filterBuilder.build());
}

private Timestamp utcTimeStringToTimestamp(String utcTimeString) {
long epochSecond =
LocalDate.parse(utcTimeString, DateTimeFormatter.ISO_DATE)
Expand Down
121 changes: 87 additions & 34 deletions core/src/test/java/feast/core/controller/CoreServiceRestIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,18 @@
import feast.common.it.SimpleCoreClient;
import feast.core.model.Project;
import feast.proto.core.CoreServiceGrpc;
import feast.proto.core.EntityProto;
import feast.proto.core.FeatureSetProto.FeatureSet;
import feast.proto.core.FeatureTableProto;
import feast.proto.types.ValueProto;
import feast.proto.types.ValueProto.ValueType.Enum;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.path.json.JsonPath;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -205,19 +210,53 @@ public void listFeatures() {
.body("features", aMapWithSize(2));
}

@Test
public void listEntities() {
String uri1 =
UriComponentsBuilder.fromPath("/api/v2/entities")
.queryParam("project", "default")
.buildAndExpand()
.toString();
String responseBody =
get(uri1)
.then()
.log()
.everything()
.assertThat()
.contentType(ContentType.JSON)
.extract()
.response()
.getBody()
.asString();
List<String> entityList = JsonPath.from(responseBody).getList("entities");
assertEquals(entityList.size(), 2);
}

@Test
public void listFeatureTables() {
String uri1 =
UriComponentsBuilder.fromPath("/api/v2/feature-tables")
.queryParam("project", "default")
.buildAndExpand()
.toString();
String responseBody =
get(uri1)
.then()
.log()
.everything()
.assertThat()
.contentType(ContentType.JSON)
.extract()
.response()
.getBody()
.asString();
List<String> featureTableList = JsonPath.from(responseBody).getList("tables");
assertEquals(featureTableList.size(), 1);
}

@BeforeEach
private void createFakeFeatureSets() {
// spec:
// name: merchant_ratings
// entities:
// - name: merchant_id
// valueType: STRING
// features:
// - name: average_rating
// valueType: DOUBLE
// - name: total_ratings
// valueType: INT64
// project: default
private void createSpecs() {
// Apply feature sets
FeatureSet merchantFeatureSet =
DataGenerator.createFeatureSet(
DataGenerator.getDefaultSource(),
Expand All @@ -227,17 +266,6 @@ private void createFakeFeatureSets() {
ImmutableMap.of("average_rating", Enum.DOUBLE, "total_ratings", Enum.INT64));
apiClient.simpleApplyFeatureSet(merchantFeatureSet);

// spec:
// name: another_merchant_ratings
// entities:
// - name: merchant_id
// valueType: STRING
// features:
// - name: another_average_rating
// valueType: DOUBLE
// - name: another_total_ratings
// valueType: INT64
// project: default
FeatureSet anotherMerchantFeatureSet =
DataGenerator.createFeatureSet(
DataGenerator.getDefaultSource(),
Expand All @@ -249,17 +277,6 @@ private void createFakeFeatureSets() {
"another_total_ratings", Enum.INT64));
apiClient.simpleApplyFeatureSet(anotherMerchantFeatureSet);

// spec:
// name: yet_another_merchant_feature_set
// entities:
// - name: merchant_id
// valueType: STRING
// features:
// - name: merchant_prop1
// valueType: BOOL
// - name: merchant_prop2
// valueType: FLOAT
// project: merchant
FeatureSet yetAnotherMerchantFeatureSet =
DataGenerator.createFeatureSet(
DataGenerator.getDefaultSource(),
Expand All @@ -268,6 +285,42 @@ private void createFakeFeatureSets() {
ImmutableMap.of("merchant_id", Enum.STRING),
ImmutableMap.of("merchant_prop1", Enum.BOOL, "merchant_prop2", Enum.FLOAT));
apiClient.simpleApplyFeatureSet(yetAnotherMerchantFeatureSet);

// Apply entities
EntityProto.EntitySpecV2 entitySpec1 =
DataGenerator.createEntitySpecV2(
"entity1",
"Entity 1 description",
ValueProto.ValueType.Enum.STRING,
avro.shaded.com.google.common.collect.ImmutableMap.of("label_key", "label_value"));
EntityProto.EntitySpecV2 entitySpec2 =
DataGenerator.createEntitySpecV2(
"entity2",
"Entity 2 description",
ValueProto.ValueType.Enum.STRING,
avro.shaded.com.google.common.collect.ImmutableMap.of("label_key2", "label_value2"));
apiClient.simpleApplyEntity("default", entitySpec1);
apiClient.simpleApplyEntity("default", entitySpec2);

// Apply feature table
FeatureTableProto.FeatureTableSpec featureTableSpec =
DataGenerator.createFeatureTableSpec(
"featuretable1",
Arrays.asList("entity1", "entity2"),
new HashMap<>() {
{
put("feature1", ValueProto.ValueType.Enum.STRING);
put("feature2", ValueProto.ValueType.Enum.FLOAT);
}
},
7200,
ImmutableMap.of("feat_key2", "feat_value2"))
.toBuilder()
.setBatchSource(
DataGenerator.createFileDataSourceSpec("file:///path/to/file", "ts_col", ""))
.build();
apiClient.applyFeatureTable("default", featureTableSpec);

RestAssured.port = port;
}
}

0 comments on commit 663daec

Please sign in to comment.