diff --git a/hopsworks-IT/src/test/ruby/spec/featureview_spec.rb b/hopsworks-IT/src/test/ruby/spec/featureview_spec.rb index 34dd982d74..8d956ea02f 100644 --- a/hopsworks-IT/src/test/ruby/spec/featureview_spec.rb +++ b/hopsworks-IT/src/test/ruby/spec/featureview_spec.rb @@ -351,8 +351,59 @@ create_feature_view_with_json(@project.id, featurestore_id, json_data) expect_status_details(400) end + + it "should not be able to create a feature view from a query object without features" do + # create feature group + featurestore_id = get_featurestore_id(@project.id) + features = [ + {type: "INT", name: "testfeature", primary: true}, + {type: "INT", name: "testfeature1"}, + ] + fg = create_cached_featuregroup_checked_return_fg(@project.id, featurestore_id, "test_fg_#{short_random_id}", + features: features) + # create queryDTO object + query = { + leftFeatureGroup: { + id: fg[:id], + type: fg[:type], + }, + leftFeatures: [] + } + json_result = create_feature_view(@project.id, featurestore_id, query) + expect_status_details(400) + end + + it "should not be able to create a feature view from a query object that joins query without features" do + # create feature group + featurestore_id = get_featurestore_id(@project.id) + features = [ + {type: "INT", name: "testfeature", primary: true}, + {type: "INT", name: "testfeature1"}, + ] + fg = create_cached_featuregroup_checked_return_fg(@project.id, featurestore_id, "test_fg_#{short_random_id}", + features: features) + # create queryDTO object + query = { + leftFeatureGroup: { + id: fg[:id], + type: fg[:type], + }, + leftFeatures: [{name: 'a_testfeature'}, {name: 'a_testfeature1'}], + joins: [ + { + leftFeatureGroup: { + id: fg[:id], + type: fg[:type], + }, + leftFeatures: [] + } + ] + } + json_result = create_feature_view(@project.id, featurestore_id, query) + expect_status_details(400) + end - it "should be able to create a feature view from a query object - 1" do + it "should be able to create a feature view from a query object - 1" do # create feature group featurestore_id = get_featurestore_id(@project.id) features = [ diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/featureview/FeatureViewInputValidator.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/featureview/FeatureViewInputValidator.java index c56e167e76..a4dc55dfba 100644 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/featureview/FeatureViewInputValidator.java +++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/featurestore/featureview/FeatureViewInputValidator.java @@ -17,8 +17,10 @@ package io.hops.hopsworks.api.featurestore.featureview; import io.hops.hopsworks.common.featurestore.featureview.FeatureViewDTO; +import io.hops.hopsworks.common.featurestore.query.QueryDTO; import io.hops.hopsworks.common.featurestore.query.Query; import io.hops.hopsworks.common.featurestore.query.QueryController; +import io.hops.hopsworks.common.featurestore.query.join.JoinDTO; import io.hops.hopsworks.common.featurestore.trainingdatasets.TrainingDatasetInputValidation; import io.hops.hopsworks.common.featurestore.utils.FeaturestoreInputValidation; import io.hops.hopsworks.exceptions.FeaturestoreException; @@ -59,6 +61,17 @@ public void validateCreationInput(FeatureViewDTO featureViewDTO) throws Features throw new FeaturestoreException(RESTCodes.FeaturestoreErrorCode.FEATURE_VIEW_CREATION_ERROR, Level.FINE, "`Query` is missing from input."); } + validateCreationInput(featureViewDTO.getQuery()); + } + + public void validateCreationInput(QueryDTO queryDTO) throws FeaturestoreException { + if (queryDTO.getLeftFeatures().isEmpty()) { + throw new FeaturestoreException(RESTCodes.FeaturestoreErrorCode.FEATURE_VIEW_CREATION_ERROR, Level.FINE, + "Feature View queries must have features"); + } + for (JoinDTO joinDTO : queryDTO.getJoins()) { + validateCreationInput(joinDTO.getQuery()); + } } public void validateVersion(Integer version) throws FeaturestoreException {