Skip to content

Commit 7b03cc4

Browse files
add featureregistry field support for featureview (#9967) (#6962)
[upstream:18666fbc36225b39c6c35756bbd0d71a5da661fc] Signed-off-by: Modular Magician <[email protected]>
1 parent 502291e commit 7b03cc4

File tree

5 files changed

+689
-0
lines changed

5 files changed

+689
-0
lines changed

.changelog/9967.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
vertexai: added `feature_registry_source` field to `google_vertex_ai_feature_online_store_featureview` resource
3+
```

google-beta/services/vertexai/resource_vertex_ai_feature_online_store_featureview.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,41 @@ func ResourceVertexAIFeatureOnlineStoreFeatureview() *schema.Resource {
8989
},
9090
},
9191
},
92+
ExactlyOneOf: []string{"big_query_source", "feature_registry_source"},
93+
},
94+
"feature_registry_source": {
95+
Type: schema.TypeList,
96+
Optional: true,
97+
Description: `Configures the features from a Feature Registry source that need to be loaded onto the FeatureOnlineStore.`,
98+
MaxItems: 1,
99+
Elem: &schema.Resource{
100+
Schema: map[string]*schema.Schema{
101+
"feature_groups": {
102+
Type: schema.TypeList,
103+
Required: true,
104+
Description: `List of features that need to be synced to Online Store.`,
105+
Elem: &schema.Resource{
106+
Schema: map[string]*schema.Schema{
107+
"feature_group_id": {
108+
Type: schema.TypeString,
109+
Required: true,
110+
Description: `Identifier of the feature group.`,
111+
},
112+
"feature_ids": {
113+
Type: schema.TypeList,
114+
Required: true,
115+
Description: `Identifiers of features under the feature group.`,
116+
Elem: &schema.Schema{
117+
Type: schema.TypeString,
118+
},
119+
},
120+
},
121+
},
122+
},
123+
},
124+
},
125+
ConflictsWith: []string{"vector_search_config"},
126+
ExactlyOneOf: []string{"big_query_source", "feature_registry_source"},
92127
},
93128
"labels": {
94129
Type: schema.TypeMap,
@@ -190,6 +225,7 @@ For details on allowed values, see the [API documentation](https://cloud.google.
190225
},
191226
},
192227
},
228+
ConflictsWith: []string{"feature_registry_source"},
193229
},
194230
"create_time": {
195231
Type: schema.TypeString,
@@ -245,6 +281,12 @@ func resourceVertexAIFeatureOnlineStoreFeatureviewCreate(d *schema.ResourceData,
245281
} else if v, ok := d.GetOkExists("big_query_source"); !tpgresource.IsEmptyValue(reflect.ValueOf(bigQuerySourceProp)) && (ok || !reflect.DeepEqual(v, bigQuerySourceProp)) {
246282
obj["bigQuerySource"] = bigQuerySourceProp
247283
}
284+
featureRegistrySourceProp, err := expandVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySource(d.Get("feature_registry_source"), d, config)
285+
if err != nil {
286+
return err
287+
} else if v, ok := d.GetOkExists("feature_registry_source"); !tpgresource.IsEmptyValue(reflect.ValueOf(featureRegistrySourceProp)) && (ok || !reflect.DeepEqual(v, featureRegistrySourceProp)) {
288+
obj["featureRegistrySource"] = featureRegistrySourceProp
289+
}
248290
vectorSearchConfigProp, err := expandVertexAIFeatureOnlineStoreFeatureviewVectorSearchConfig(d.Get("vector_search_config"), d, config)
249291
if err != nil {
250292
return err
@@ -377,6 +419,9 @@ func resourceVertexAIFeatureOnlineStoreFeatureviewRead(d *schema.ResourceData, m
377419
if err := d.Set("big_query_source", flattenVertexAIFeatureOnlineStoreFeatureviewBigQuerySource(res["bigQuerySource"], d, config)); err != nil {
378420
return fmt.Errorf("Error reading FeatureOnlineStoreFeatureview: %s", err)
379421
}
422+
if err := d.Set("feature_registry_source", flattenVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySource(res["featureRegistrySource"], d, config)); err != nil {
423+
return fmt.Errorf("Error reading FeatureOnlineStoreFeatureview: %s", err)
424+
}
380425
if err := d.Set("vector_search_config", flattenVertexAIFeatureOnlineStoreFeatureviewVectorSearchConfig(res["vectorSearchConfig"], d, config)); err != nil {
381426
return fmt.Errorf("Error reading FeatureOnlineStoreFeatureview: %s", err)
382427
}
@@ -418,6 +463,12 @@ func resourceVertexAIFeatureOnlineStoreFeatureviewUpdate(d *schema.ResourceData,
418463
} else if v, ok := d.GetOkExists("big_query_source"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, bigQuerySourceProp)) {
419464
obj["bigQuerySource"] = bigQuerySourceProp
420465
}
466+
featureRegistrySourceProp, err := expandVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySource(d.Get("feature_registry_source"), d, config)
467+
if err != nil {
468+
return err
469+
} else if v, ok := d.GetOkExists("feature_registry_source"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, featureRegistrySourceProp)) {
470+
obj["featureRegistrySource"] = featureRegistrySourceProp
471+
}
421472
labelsProp, err := expandVertexAIFeatureOnlineStoreFeatureviewEffectiveLabels(d.Get("effective_labels"), d, config)
422473
if err != nil {
423474
return err
@@ -441,6 +492,10 @@ func resourceVertexAIFeatureOnlineStoreFeatureviewUpdate(d *schema.ResourceData,
441492
updateMask = append(updateMask, "bigQuerySource")
442493
}
443494

495+
if d.HasChange("feature_registry_source") {
496+
updateMask = append(updateMask, "featureRegistrySource")
497+
}
498+
444499
if d.HasChange("effective_labels") {
445500
updateMask = append(updateMask, "labels")
446501
}
@@ -616,6 +671,46 @@ func flattenVertexAIFeatureOnlineStoreFeatureviewBigQuerySourceEntityIdColumns(v
616671
return v
617672
}
618673

674+
func flattenVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySource(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
675+
if v == nil {
676+
return nil
677+
}
678+
original := v.(map[string]interface{})
679+
if len(original) == 0 {
680+
return nil
681+
}
682+
transformed := make(map[string]interface{})
683+
transformed["feature_groups"] =
684+
flattenVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySourceFeatureGroups(original["featureGroups"], d, config)
685+
return []interface{}{transformed}
686+
}
687+
func flattenVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySourceFeatureGroups(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
688+
if v == nil {
689+
return v
690+
}
691+
l := v.([]interface{})
692+
transformed := make([]interface{}, 0, len(l))
693+
for _, raw := range l {
694+
original := raw.(map[string]interface{})
695+
if len(original) < 1 {
696+
// Do not include empty json objects coming back from the api
697+
continue
698+
}
699+
transformed = append(transformed, map[string]interface{}{
700+
"feature_group_id": flattenVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySourceFeatureGroupsFeatureGroupId(original["featureGroupId"], d, config),
701+
"feature_ids": flattenVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySourceFeatureGroupsFeatureIds(original["featureIds"], d, config),
702+
})
703+
}
704+
return transformed
705+
}
706+
func flattenVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySourceFeatureGroupsFeatureGroupId(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
707+
return v
708+
}
709+
710+
func flattenVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySourceFeatureGroupsFeatureIds(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
711+
return v
712+
}
713+
619714
func flattenVertexAIFeatureOnlineStoreFeatureviewVectorSearchConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
620715
if v == nil {
621716
return nil
@@ -775,6 +870,62 @@ func expandVertexAIFeatureOnlineStoreFeatureviewBigQuerySourceEntityIdColumns(v
775870
return v, nil
776871
}
777872

873+
func expandVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySource(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
874+
l := v.([]interface{})
875+
if len(l) == 0 || l[0] == nil {
876+
return nil, nil
877+
}
878+
raw := l[0]
879+
original := raw.(map[string]interface{})
880+
transformed := make(map[string]interface{})
881+
882+
transformedFeatureGroups, err := expandVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySourceFeatureGroups(original["feature_groups"], d, config)
883+
if err != nil {
884+
return nil, err
885+
} else if val := reflect.ValueOf(transformedFeatureGroups); val.IsValid() && !tpgresource.IsEmptyValue(val) {
886+
transformed["featureGroups"] = transformedFeatureGroups
887+
}
888+
889+
return transformed, nil
890+
}
891+
892+
func expandVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySourceFeatureGroups(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
893+
l := v.([]interface{})
894+
req := make([]interface{}, 0, len(l))
895+
for _, raw := range l {
896+
if raw == nil {
897+
continue
898+
}
899+
original := raw.(map[string]interface{})
900+
transformed := make(map[string]interface{})
901+
902+
transformedFeatureGroupId, err := expandVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySourceFeatureGroupsFeatureGroupId(original["feature_group_id"], d, config)
903+
if err != nil {
904+
return nil, err
905+
} else if val := reflect.ValueOf(transformedFeatureGroupId); val.IsValid() && !tpgresource.IsEmptyValue(val) {
906+
transformed["featureGroupId"] = transformedFeatureGroupId
907+
}
908+
909+
transformedFeatureIds, err := expandVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySourceFeatureGroupsFeatureIds(original["feature_ids"], d, config)
910+
if err != nil {
911+
return nil, err
912+
} else if val := reflect.ValueOf(transformedFeatureIds); val.IsValid() && !tpgresource.IsEmptyValue(val) {
913+
transformed["featureIds"] = transformedFeatureIds
914+
}
915+
916+
req = append(req, transformed)
917+
}
918+
return req, nil
919+
}
920+
921+
func expandVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySourceFeatureGroupsFeatureGroupId(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
922+
return v, nil
923+
}
924+
925+
func expandVertexAIFeatureOnlineStoreFeatureviewFeatureRegistrySourceFeatureGroupsFeatureIds(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
926+
return v, nil
927+
}
928+
778929
func expandVertexAIFeatureOnlineStoreFeatureviewVectorSearchConfig(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
779930
l := v.([]interface{})
780931
if len(l) == 0 || l[0] == nil {

google-beta/services/vertexai/resource_vertex_ai_feature_online_store_featureview_generated_test.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,128 @@ data "google_project" "project" {
129129
`, context)
130130
}
131131

132+
func TestAccVertexAIFeatureOnlineStoreFeatureview_vertexAiFeatureonlinestoreFeatureviewFeatureRegistryExample(t *testing.T) {
133+
t.Parallel()
134+
135+
context := map[string]interface{}{
136+
"random_suffix": acctest.RandString(t, 10),
137+
}
138+
139+
acctest.VcrTest(t, resource.TestCase{
140+
PreCheck: func() { acctest.AccTestPreCheck(t) },
141+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
142+
CheckDestroy: testAccCheckVertexAIFeatureOnlineStoreFeatureviewDestroyProducer(t),
143+
Steps: []resource.TestStep{
144+
{
145+
Config: testAccVertexAIFeatureOnlineStoreFeatureview_vertexAiFeatureonlinestoreFeatureviewFeatureRegistryExample(context),
146+
},
147+
{
148+
ResourceName: "google_vertex_ai_feature_online_store_featureview.featureview_featureregistry",
149+
ImportState: true,
150+
ImportStateVerify: true,
151+
ImportStateVerifyIgnore: []string{"name", "feature_online_store", "region", "labels", "terraform_labels"},
152+
},
153+
},
154+
})
155+
}
156+
157+
func testAccVertexAIFeatureOnlineStoreFeatureview_vertexAiFeatureonlinestoreFeatureviewFeatureRegistryExample(context map[string]interface{}) string {
158+
return acctest.Nprintf(`
159+
resource "google_vertex_ai_feature_online_store" "featureonlinestore" {
160+
name = "tf_test_example_feature_view_feature_registry%{random_suffix}"
161+
labels = {
162+
foo = "bar"
163+
}
164+
region = "us-central1"
165+
bigtable {
166+
auto_scaling {
167+
min_node_count = 1
168+
max_node_count = 2
169+
cpu_utilization_target = 80
170+
}
171+
}
172+
}
173+
174+
resource "google_bigquery_dataset" "sample_dataset" {
175+
dataset_id = "tf_test_example_feature_view_feature_registry%{random_suffix}"
176+
friendly_name = "test"
177+
description = "This is a test description"
178+
location = "US"
179+
}
180+
181+
resource "google_bigquery_table" "sample_table" {
182+
deletion_protection = false
183+
dataset_id = google_bigquery_dataset.sample_dataset.dataset_id
184+
table_id = "tf_test_example_feature_view_feature_registry%{random_suffix}"
185+
186+
schema = <<EOF
187+
[
188+
{
189+
"name": "feature_id",
190+
"type": "STRING",
191+
"mode": "NULLABLE"
192+
},
193+
{
194+
"name": "tf_test_example_feature_view_feature_registry%{random_suffix}",
195+
"type": "STRING",
196+
"mode": "NULLABLE"
197+
},
198+
{
199+
"name": "feature_timestamp",
200+
"type": "TIMESTAMP",
201+
"mode": "NULLABLE"
202+
}
203+
]
204+
EOF
205+
}
206+
207+
resource "google_vertex_ai_feature_group" "sample_feature_group" {
208+
name = "tf_test_example_feature_view_feature_registry%{random_suffix}"
209+
description = "A sample feature group"
210+
region = "us-central1"
211+
labels = {
212+
label-one = "value-one"
213+
}
214+
big_query {
215+
big_query_source {
216+
# The source table must have a column named 'feature_timestamp' of type TIMESTAMP.
217+
input_uri = "bq://${google_bigquery_table.sample_table.project}.${google_bigquery_table.sample_table.dataset_id}.${google_bigquery_table.sample_table.table_id}"
218+
}
219+
entity_id_columns = ["feature_id"]
220+
}
221+
}
222+
223+
224+
225+
resource "google_vertex_ai_feature_group_feature" "sample_feature" {
226+
name = "tf_test_example_feature_view_feature_registry%{random_suffix}"
227+
region = "us-central1"
228+
feature_group = google_vertex_ai_feature_group.sample_feature_group.name
229+
description = "A sample feature"
230+
labels = {
231+
label-one = "value-one"
232+
}
233+
}
234+
235+
236+
resource "google_vertex_ai_feature_online_store_featureview" "featureview_featureregistry" {
237+
name = "tf_test_example_feature_view_feature_registry%{random_suffix}"
238+
region = "us-central1"
239+
feature_online_store = google_vertex_ai_feature_online_store.featureonlinestore.name
240+
sync_config {
241+
cron = "0 0 * * *"
242+
}
243+
feature_registry_source {
244+
245+
feature_groups {
246+
feature_group_id = google_vertex_ai_feature_group.sample_feature_group.name
247+
feature_ids = [google_vertex_ai_feature_group_feature.sample_feature.name]
248+
}
249+
}
250+
}
251+
`, context)
252+
}
253+
132254
func TestAccVertexAIFeatureOnlineStoreFeatureview_vertexAiFeatureonlinestoreFeatureviewWithVectorSearchExample(t *testing.T) {
133255
t.Parallel()
134256

0 commit comments

Comments
 (0)