Repository: oom-ai/oomstore
Pull request #1110 (2022-01-27 09:22:45)

Jinghan/refactor metadata method `CreateRevision`
This PR refactors metadata method `CreateRevision`: not return `snapshotTableName`.

In the previous PR, method `CreateRevision` no longer create `snapshotTable`, it shouldn't return `snapshotTableName` any more.


Patch:
diff --git a/internal/database/metadata/mock_metadata/store.go b/internal/database/metadata/mock_metadata/store.go
index 4f9f81ff..ef0778ee 100644
--- a/internal/database/metadata/mock_metadata/store.go
+++ b/internal/database/metadata/mock_metadata/store.go
@@ -97,13 +97,12 @@ func (mr *MockStoreMockRecorder) CreateGroup(ctx, opt interface{}) *gomock.Call
 }
 
 // CreateRevision mocks base method.
-func (m *MockStore) CreateRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, string, error) {
+func (m *MockStore) CreateRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "CreateRevision", ctx, opt)
 	ret0, _ := ret[0].(int)
-	ret1, _ := ret[1].(string)
-	ret2, _ := ret[2].(error)
-	return ret0, ret1, ret2
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
 }
 
 // CreateRevision indicates an expected call of CreateRevision.
@@ -488,13 +487,12 @@ func (mr *MockDBStoreMockRecorder) CreateGroup(ctx, opt interface{}) *gomock.Cal
 }
 
 // CreateRevision mocks base method.
-func (m *MockDBStore) CreateRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, string, error) {
+func (m *MockDBStore) CreateRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "CreateRevision", ctx, opt)
 	ret0, _ := ret[0].(int)
-	ret1, _ := ret[1].(string)
-	ret2, _ := ret[2].(error)
-	return ret0, ret1, ret2
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
 }
 
 // CreateRevision indicates an expected call of CreateRevision.
diff --git a/internal/database/metadata/mysql/db.go b/internal/database/metadata/mysql/db.go
index 7bffa8d6..596d3489 100644
--- a/internal/database/metadata/mysql/db.go
+++ b/internal/database/metadata/mysql/db.go
@@ -104,17 +104,16 @@ func (db *DB) ListGroup(ctx context.Context, entityID *int, groupIDs *[]int) (ty
 	return sqlutil.ListGroup(ctx, db, entityID, groupIDs)
 }
 
-func (db *DB) CreateRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, string, error) {
+func (db *DB) CreateRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, error) {
 	var (
-		revisionID    int
-		snapshotTable string
-		err           error
+		revisionID int
+		err        error
 	)
 	err = db.WithTransaction(ctx, func(c context.Context, tx metadata.DBStore) error {
-		revisionID, snapshotTable, err = tx.CreateRevision(c, opt)
+		revisionID, err = tx.CreateRevision(c, opt)
 		return err
 	})
-	return revisionID, snapshotTable, err
+	return revisionID, err
 }
 
 func (db *DB) UpdateRevision(ctx context.Context, opt metadata.UpdateRevisionOpt) error {
diff --git a/internal/database/metadata/mysql/revision.go b/internal/database/metadata/mysql/revision.go
index 898b024e..79482b97 100644
--- a/internal/database/metadata/mysql/revision.go
+++ b/internal/database/metadata/mysql/revision.go
@@ -9,7 +9,7 @@ import (
 	"github.com/oom-ai/oomstore/internal/database/metadata"
 )
 
-func createRevision(ctx context.Context, sqlxCtx metadata.SqlxContext, opt metadata.CreateRevisionOpt) (int, string, error) {
+func createRevision(ctx context.Context, sqlxCtx metadata.SqlxContext, opt metadata.CreateRevisionOpt) (int, error) {
 	var snapshotTable, cdcTable string
 	if opt.SnapshotTable != nil {
 		snapshotTable = *opt.SnapshotTable
@@ -23,15 +23,15 @@ func createRevision(ctx context.Context, sqlxCtx metadata.SqlxContext, opt metad
 	if err != nil {
 		if er, ok := err.(*mysql.MySQLError); ok {
 			if er.Number == ER_DUP_ENTRY {
-				return 0, "", errdefs.Errorf("revision already exists: groupID=%d, revision=%d", opt.GroupID, opt.Revision)
+				return 0, errdefs.Errorf("revision already exists: groupID=%d, revision=%d", opt.GroupID, opt.Revision)
 			}
 		}
-		return 0, "", errdefs.WithStack(err)
+		return 0, errdefs.WithStack(err)
 	}
 	revisionID, err := res.LastInsertId()
 	if err != nil {
-		return 0, "", errdefs.WithStack(err)
+		return 0, errdefs.WithStack(err)
 	}
 
-	return int(revisionID), snapshotTable, nil
+	return int(revisionID), nil
 }
diff --git a/internal/database/metadata/mysql/tx.go b/internal/database/metadata/mysql/tx.go
index 81b0a0bb..dcd0a012 100644
--- a/internal/database/metadata/mysql/tx.go
+++ b/internal/database/metadata/mysql/tx.go
@@ -72,7 +72,7 @@ func (tx *Tx) GetFeatureByName(ctx context.Context, groupName string, featureNam
 	return sqlutil.GetFeatureByName(ctx, tx, groupName, featureName)
 }
 
-func (tx *Tx) CreateRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, string, error) {
+func (tx *Tx) CreateRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, error) {
 	return createRevision(ctx, tx, opt)
 }
 
diff --git a/internal/database/metadata/postgres/db.go b/internal/database/metadata/postgres/db.go
index 9c5179b0..e350e047 100644
--- a/internal/database/metadata/postgres/db.go
+++ b/internal/database/metadata/postgres/db.go
@@ -93,17 +93,16 @@ func (db *DB) GetFeatureByName(ctx context.Context, groupName string, featureNam
 	return sqlutil.GetFeatureByName(ctx, db, groupName, featureName)
 }
 
-func (db *DB) CreateRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, string, error) {
+func (db *DB) CreateRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, error) {
 	var (
-		revisionID    int
-		snapshotTable string
-		err           error
+		revisionID int
+		err        error
 	)
 	err = db.WithTransaction(ctx, func(c context.Context, tx metadata.DBStore) error {
-		revisionID, snapshotTable, err = tx.CreateRevision(ctx, opt)
+		revisionID, err = tx.CreateRevision(ctx, opt)
 		return err
 	})
-	return revisionID, snapshotTable, err
+	return revisionID, err
 }
 
 func (db *DB) UpdateRevision(ctx context.Context, opt metadata.UpdateRevisionOpt) error {
diff --git a/internal/database/metadata/postgres/revision.go b/internal/database/metadata/postgres/revision.go
index d750aa61..3a91dd61 100644
--- a/internal/database/metadata/postgres/revision.go
+++ b/internal/database/metadata/postgres/revision.go
@@ -10,7 +10,7 @@ import (
 	"github.com/oom-ai/oomstore/internal/database/metadata"
 )
 
-func createRevision(ctx context.Context, sqlxCtx metadata.SqlxContext, opt metadata.CreateRevisionOpt) (int, string, error) {
+func createRevision(ctx context.Context, sqlxCtx metadata.SqlxContext, opt metadata.CreateRevisionOpt) (int, error) {
 	var snapshotTable, cdcTable string
 	if opt.SnapshotTable != nil {
 		snapshotTable = *opt.SnapshotTable
@@ -24,11 +24,11 @@ func createRevision(ctx context.Context, sqlxCtx metadata.SqlxContext, opt metad
 	if err := sqlxCtx.GetContext(ctx, &revisionID, insertQuery, opt.GroupID, opt.Revision, snapshotTable, cdcTable, opt.Anchored, opt.Description); err != nil {
 		if e2, ok := err.(*pq.Error); ok {
 			if e2.Code == pgerrcode.UniqueViolation {
-				return 0, "", errdefs.Errorf("revision already exists: groupID=%d, revision=%d", opt.GroupID, opt.Revision)
+				return 0, errdefs.Errorf("revision already exists: groupID=%d, revision=%d", opt.GroupID, opt.Revision)
 			}
 		}
-		return 0, "", errdefs.WithStack(err)
+		return 0, errdefs.WithStack(err)
 	}
 
-	return revisionID, snapshotTable, nil
+	return revisionID, nil
 }
diff --git a/internal/database/metadata/postgres/tx.go b/internal/database/metadata/postgres/tx.go
index fb911e38..2ab96e4f 100644
--- a/internal/database/metadata/postgres/tx.go
+++ b/internal/database/metadata/postgres/tx.go
@@ -72,7 +72,7 @@ func (tx *Tx) GetFeatureByName(ctx context.Context, groupName string, featureNam
 	return sqlutil.GetFeatureByName(ctx, tx, groupName, featureName)
 }
 
-func (tx *Tx) CreateRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, string, error) {
+func (tx *Tx) CreateRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, error) {
 	return createRevision(ctx, tx, opt)
 }
 
diff --git a/internal/database/metadata/sqlite/db.go b/internal/database/metadata/sqlite/db.go
index ebb6a680..e5cd99b6 100644
--- a/internal/database/metadata/sqlite/db.go
+++ b/internal/database/metadata/sqlite/db.go
@@ -94,17 +94,16 @@ func (db *DB) ListGroup(ctx context.Context, entityID *int, groupIDs *[]int) (ty
 	return sqlutil.ListGroup(ctx, db, entityID, groupIDs)
 }
 
-func (db *DB) CreateRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, string, error) {
+func (db *DB) CreateRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, error) {
 	var (
-		revisionID    int
-		snapshotTable string
-		err           error
+		revisionID int
+		err        error
 	)
 	err = db.WithTransaction(ctx, func(c context.Context, tx metadata.DBStore) error {
-		revisionID, snapshotTable, err = tx.CreateRevision(c, opt)
+		revisionID, err = tx.CreateRevision(c, opt)
 		return err
 	})
-	return revisionID, snapshotTable, err
+	return revisionID, err
 }
 
 func (db *DB) UpdateRevision(ctx context.Context, opt metadata.UpdateRevisionOpt) error {
diff --git a/internal/database/metadata/sqlite/revision.go b/internal/database/metadata/sqlite/revision.go
index 7f506ee4..85732850 100644
--- a/internal/database/metadata/sqlite/revision.go
+++ b/internal/database/metadata/sqlite/revision.go
@@ -10,7 +10,7 @@ import (
 	"github.com/oom-ai/oomstore/internal/database/metadata"
 )
 
-func createRevision(ctx context.Context, sqlxCtx metadata.SqlxContext, opt metadata.CreateRevisionOpt) (int, string, error) {
+func createRevision(ctx context.Context, sqlxCtx metadata.SqlxContext, opt metadata.CreateRevisionOpt) (int, error) {
 	var snapshotTable, cdcTable string
 	if opt.SnapshotTable != nil {
 		snapshotTable = *opt.SnapshotTable
@@ -24,15 +24,15 @@ func createRevision(ctx context.Context, sqlxCtx metadata.SqlxContext, opt metad
 	if err != nil {
 		if sqliteErr, ok := err.(*sqlite.Error); ok {
 			if sqliteErr.Code() == sqlite3.SQLITE_CONSTRAINT_UNIQUE {
-				return 0, "", errdefs.Errorf("revision already exists: groupID=%d, revision=%d", opt.GroupID, opt.Revision)
+				return 0, errdefs.Errorf("revision already exists: groupID=%d, revision=%d", opt.GroupID, opt.Revision)
 			}
 		}
-		return 0, "", errdefs.WithStack(err)
+		return 0, errdefs.WithStack(err)
 	}
 	revisionID, err := res.LastInsertId()
 	if err != nil {
-		return 0, "", errdefs.WithStack(err)
+		return 0, errdefs.WithStack(err)
 	}
 
-	return int(revisionID), snapshotTable, nil
+	return int(revisionID), nil
 }
diff --git a/internal/database/metadata/sqlite/tx.go b/internal/database/metadata/sqlite/tx.go
index 5d21cb35..bf30b934 100644
--- a/internal/database/metadata/sqlite/tx.go
+++ b/internal/database/metadata/sqlite/tx.go
@@ -73,7 +73,7 @@ func (tx *Tx) GetFeatureByName(ctx context.Context, groupName string, featureNam
 
 }
 
-func (tx *Tx) CreateRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, string, error) {
+func (tx *Tx) CreateRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, error) {
 	return createRevision(ctx, tx, opt)
 }
 
diff --git a/internal/database/metadata/store.go b/internal/database/metadata/store.go
index d6c73c06..3b7dbd3a 100644
--- a/internal/database/metadata/store.go
+++ b/internal/database/metadata/store.go
@@ -40,7 +40,7 @@ type DBStore interface {
 	ListGroup(ctx context.Context, entityID *int, groupIDs *[]int) (types.GroupList, error)
 
 	// revision
-	CreateRevision(ctx context.Context, opt CreateRevisionOpt) (int, string, error)
+	CreateRevision(ctx context.Context, opt CreateRevisionOpt) (int, error)
 	UpdateRevision(ctx context.Context, opt UpdateRevisionOpt) error
 	GetRevision(ctx context.Context, id int) (*types.Revision, error)
 	GetRevisionBy(ctx context.Context, groupID int, revision int64) (*types.Revision, error)
diff --git a/oomcli/cmd/serialize.go b/oomcli/cmd/serialize.go
index 0b604527..65e001da 100644
--- a/oomcli/cmd/serialize.go
+++ b/oomcli/cmd/serialize.go
@@ -171,7 +171,6 @@ func parseRecords(tokens []TokenList, wide, truncate bool) ([][]string, error) {
 
 func serializeInYaml(w io.Writer, items interface{}) error {
 	encoder := yaml.NewEncoder(w)
-	encoder.SetIndent(2)
 	defer encoder.Close()
 
 	slice := reflect.ValueOf(items).Field(0)
diff --git a/pkg/oomstore/import.go b/pkg/oomstore/import.go
index cc939dbd..5371df60 100644
--- a/pkg/oomstore/import.go
+++ b/pkg/oomstore/import.go
@@ -57,7 +57,7 @@ func (s *OomStore) csvReaderImport(ctx context.Context, opt *importOpt, dataSour
 		return 0, err
 	}
 
-	newRevisionID, _, err := s.createRevision(ctx, metadata.CreateRevisionOpt{
+	newRevisionID, err := s.createRevision(ctx, metadata.CreateRevisionOpt{
 		Revision:      time.Now().UnixMilli(),
 		GroupID:       opt.group.ID,
 		SnapshotTable: nil,
@@ -113,7 +113,7 @@ func (s *OomStore) tableLinkImport(ctx context.Context, opt *importOpt, dataSour
 	} else {
 		revision = *opt.Revision
 	}
-	newRevisionID, _, err := s.createRevision(ctx, metadata.CreateRevisionOpt{
+	newRevisionID, err := s.createRevision(ctx, metadata.CreateRevisionOpt{
 		Revision:      revision,
 		GroupID:       opt.group.ID,
 		SnapshotTable: &dataSource.TableName,
diff --git a/pkg/oomstore/import_stream.go b/pkg/oomstore/import_stream.go
index 06231dd6..7e70d251 100644
--- a/pkg/oomstore/import_stream.go
+++ b/pkg/oomstore/import_stream.go
@@ -113,7 +113,7 @@ func (s *OomStore) tableLinkImportStream(ctx context.Context, opt types.ImportSt
 		return err
 	}
 
-	_, _, err = s.createRevision(ctx, metadata.CreateRevisionOpt{
+	_, err = s.createRevision(ctx, metadata.CreateRevisionOpt{
 		Revision:    *tableSchema.TimeRange.MinUnixMilli,
 		GroupID:     group.ID,
 		CdcTable:    &dataSource.TableName,
diff --git a/pkg/oomstore/push_processor.go b/pkg/oomstore/push_processor.go
index 6e668ff3..b98a4de1 100644
--- a/pkg/oomstore/push_processor.go
+++ b/pkg/oomstore/push_processor.go
@@ -163,7 +163,7 @@ func (s *OomStore) newRevisionForStream(ctx context.Context, groupID int, revisi
 
 	snapshotTable := ""
 	cdcTable := dbutil.OfflineStreamCdcTableName(groupID, revision)
-	if _, _, err := s.createRevision(ctx, metadata.CreateRevisionOpt{
+	if _, err := s.createRevision(ctx, metadata.CreateRevisionOpt{
 		GroupID:       groupID,
 		Revision:      revision,
 		SnapshotTable: &snapshotTable,
diff --git a/pkg/oomstore/revision.go b/pkg/oomstore/revision.go
index d4e3dee0..bfa5e6da 100644
--- a/pkg/oomstore/revision.go
+++ b/pkg/oomstore/revision.go
@@ -25,9 +25,8 @@ func (s *OomStore) GetRevisionBy(ctx context.Context, groupID int, revision int6
 	return s.metadata.GetRevisionBy(ctx, groupID, revision)
 }
 
-func (s *OomStore) createRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, string, error) {
+func (s *OomStore) createRevision(ctx context.Context, opt metadata.CreateRevisionOpt) (int, error) {
 	var revisionID int
-	var snapshotTable string
 	var dummyRevision *types.Revision
 
 	if err := s.metadata.WithTransaction(ctx, func(c context.Context, tx metadata.DBStore) error {
@@ -37,7 +36,7 @@ func (s *OomStore) createRevision(ctx context.Context, opt metadata.CreateRevisi
 				return err
 			}
 
-			if _, _, err = tx.CreateRevision(ctx, metadata.CreateRevisionOpt{
+			if _, err = tx.CreateRevision(ctx, metadata.CreateRevisionOpt{
 				Revision:    0,
 				GroupID:     opt.GroupID,
 				Description: "dummy revision will be used at Join and Export",
@@ -51,19 +50,19 @@ func (s *OomStore) createRevision(ctx context.Context, opt metadata.CreateRevisi
 			}
 		}
 
-		revisionID, snapshotTable, err = tx.CreateRevision(ctx, opt)
+		revisionID, err = tx.CreateRevision(ctx, opt)
 		return err
 	}); err != nil {
-		return 0, "", err
+		return 0, err
 	}
 
 	if dummyRevision != nil {
 		if err := s.createSnapshotAndCdcTable(ctx, dummyRevision); err != nil {
-			return 0, "", err
+			return 0, err
 		}
 	}
 
-	return revisionID, snapshotTable, nil
+	return revisionID, nil
 }
 
 func (s *OomStore) createSnapshotAndCdcTable(ctx context.Context, revision *types.Revision) error {


Test patch:
diff --git a/internal/database/metadata/test_impl/revision.go b/internal/database/metadata/test_impl/revision.go
index 1bce4f80..b0f2b1e3 100644
--- a/internal/database/metadata/test_impl/revision.go
+++ b/internal/database/metadata/test_impl/revision.go
@@ -79,7 +79,7 @@ func TestCreateRevision(t *testing.T, prepareStore PrepareStoreFn, destroyStore
 
 	for _, tc := range testCases {
 		t.Run(tc.description, func(t *testing.T) {
-			actual, _, err := store.CreateRevision(ctx, tc.opt)
+			actual, err := store.CreateRevision(ctx, tc.opt)
 			require.Equal(t, tc.expected, actual)
 			if tc.expectedError != nil {
 				require.EqualError(t, err, tc.expectedError.Error())
@@ -104,7 +104,7 @@ func TestUpdateRevision(t *testing.T, prepareStore PrepareStoreFn, destroyStore
 	_, groupID := prepareEntityAndGroup(t, ctx, store)
 	group, err := store.GetGroup(ctx, groupID)
 	require.NoError(t, err)
-	revisionID, _, err := store.CreateRevision(ctx, metadata.CreateRevisionOpt{
+	revisionID, err := store.CreateRevision(ctx, metadata.CreateRevisionOpt{
 		Revision:      1000,
 		GroupID:       groupID,
 		SnapshotTable: stringPtr("offline_stream_snapshot_device_info_1000"),
@@ -203,7 +203,7 @@ func TestGetRevision(t *testing.T, prepareStore PrepareStoreFn, destroyStore Des
 	defer store.Close()
 
 	_, groupID := prepareEntityAndGroup(t, ctx, store)
-	revisionID, _, err := store.CreateRevision(ctx, metadata.CreateRevisionOpt{
+	revisionID, err := store.CreateRevision(ctx, metadata.CreateRevisionOpt{
 		Revision:      1000,
 		GroupID:       groupID,
 		SnapshotTable: stringPtr("device_info_1000"),
@@ -266,7 +266,7 @@ func TestGetRevisionBy(t *testing.T, prepareStore PrepareStoreFn, destroyStore D
 	defer store.Close()
 
 	_, groupID := prepareEntityAndGroup(t, ctx, store)
-	revisionID, _, err := store.CreateRevision(ctx, metadata.CreateRevisionOpt{
+	revisionID, err := store.CreateRevision(ctx, metadata.CreateRevisionOpt{
 		Revision:      1000,
 		GroupID:       groupID,
 		SnapshotTable: stringPtr("device_info_1000"),
@@ -390,7 +390,7 @@ func prepareRevisions(t *testing.T, ctx context.Context, store metadata.Store) (
 	})
 	require.NoError(t, err)
 	require.NoError(t, store.Refresh())
-	revisionID1, _, err := store.CreateRevision(ctx, metadata.CreateRevisionOpt{
+	revisionID1, err := store.CreateRevision(ctx, metadata.CreateRevisionOpt{
 		Revision:      1000,
 		GroupID:       groupID,
 		SnapshotTable: stringPtr("device_info_1000"),
@@ -398,7 +398,7 @@ func prepareRevisions(t *testing.T, ctx context.Context, store metadata.Store) (
 	})
 	require.NoError(t, err)
 
-	revisionID2, _, err := store.CreateRevision(ctx, metadata.CreateRevisionOpt{
+	revisionID2, err := store.CreateRevision(ctx, metadata.CreateRevisionOpt{
 		Revision:      2000,
 		GroupID:       groupID,
 		SnapshotTable: stringPtr("device_info_2000"),
diff --git a/oomcli/test/test_get_meta_entity.sh b/oomcli/test/test_get_meta_entity.sh
index 1cae26b0..fb47bcac 100755
--- a/oomcli/test/test_get_meta_entity.sh
+++ b/oomcli/test/test_get_meta_entity.sh
@@ -30,16 +30,16 @@ kind: Entity
 name: device
 description: device
 groups:
-  - name: phone
-    category: batch
-    description: phone
-    features:
-      - name: price
-        value-type: int64
-        description: price
-      - name: model
-        value-type: string
-        description: model
+    - name: phone
+      category: batch
+      description: phone
+      features:
+        - name: price
+          value-type: int64
+          description: price
+        - name: model
+          value-type: string
+          description: model
 '
 
 actual=$(oomcli get meta entity device -o yaml)
@@ -48,48 +48,48 @@ assert_eq "$case" "$expected" "$actual"
 case='oomcli get meta entity -o yaml: multiple entities'
 expected='
 items:
-  - kind: Entity
-    name: device
-    description: device
-    groups:
-      - name: phone
-        category: batch
-        description: phone
-        features:
-          - name: price
-            value-type: int64
-            description: price
-          - name: model
-            value-type: string
-            description: model
-  - kind: Entity
-    name: user
-    description: user
-    groups:
-      - name: student
-        category: batch
-        description: student
-        features:
-          - name: name
-            value-type: string
-            description: name
-          - name: gender
-            value-type: string
-            description: gender
-          - name: age
-            value-type: int64
-            description: age
-      - name: user-click
-        category: stream
-        snapshot-interval: 1s
-        description: user click post feature
-        features:
-          - name: last_5_click_posts
-            value-type: string
-            description: user last 5 click posts
-          - name: number_of_user_starred_posts
-            value-type: int64
-            description: number of posts that users starred today
+    - kind: Entity
+      name: device
+      description: device
+      groups:
+        - name: phone
+          category: batch
+          description: phone
+          features:
+            - name: price
+              value-type: int64
+              description: price
+            - name: model
+              value-type: string
+              description: model
+    - kind: Entity
+      name: user
+      description: user
+      groups:
+        - name: student
+          category: batch
+          description: student
+          features:
+            - name: name
+              value-type: string
+              description: name
+            - name: gender
+              value-type: string
+              description: gender
+            - name: age
+              value-type: int64
+              description: age
+        - name: user-click
+          category: stream
+          snapshot-interval: 1s
+          description: user click post feature
+          features:
+            - name: last_5_click_posts
+              value-type: string
+              description: user last 5 click posts
+            - name: number_of_user_starred_posts
+              value-type: int64
+              description: number of posts that users starred today
 '
 
 actual=$(oomcli get meta entity -o yaml)
diff --git a/oomcli/test/test_get_meta_feature.sh b/oomcli/test/test_get_meta_feature.sh
index 46d78416..294a793c 100755
--- a/oomcli/test/test_get_meta_feature.sh
+++ b/oomcli/test/test_get_meta_feature.sh
@@ -59,41 +59,41 @@ assert_eq "$case" "$expected" "$actual"
 case='oomcli get meta feature: multiple features'
 expected='
 items:
-  - kind: Feature
-    name: price
-    group-name: phone
-    value-type: int64
-    description: price
-  - kind: Feature
-    name: model
-    group-name: phone
-    value-type: string
-    description: model
-  - kind: Feature
-    name: name
-    group-name: student
-    value-type: string
-    description: name
-  - kind: Feature
-    name: gender
-    group-name: student
-    value-type: string
-    description: gender
-  - kind: Feature
-    name: age
-    group-name: student
-    value-type: int64
-    description: age
-  - kind: Feature
-    name: last_5_click_posts
-    group-name: user-click
-    value-type: string
-    description: user last 5 click posts
-  - kind: Feature
-    name: number_of_user_starred_posts
-    group-name: user-click
-    value-type: int64
-    description: number of posts that users starred today
+    - kind: Feature
+      name: price
+      group-name: phone
+      value-type: int64
+      description: price
+    - kind: Feature
+      name: model
+      group-name: phone
+      value-type: string
+      description: model
+    - kind: Feature
+      name: name
+      group-name: student
+      value-type: string
+      description: name
+    - kind: Feature
+      name: gender
+      group-name: student
+      value-type: string
+      description: gender
+    - kind: Feature
+      name: age
+      group-name: student
+      value-type: int64
+      description: age
+    - kind: Feature
+      name: last_5_click_posts
+      group-name: user-click
+      value-type: string
+      description: user last 5 click posts
+    - kind: Feature
+      name: number_of_user_starred_posts
+      group-name: user-click
+      value-type: int64
+      description: number of posts that users starred today
 '
 
 actual=$(oomcli get meta feature -o yaml)
diff --git a/oomcli/test/test_get_meta_group.sh b/oomcli/test/test_get_meta_group.sh
index d4f57be2..364c0552 100755
--- a/oomcli/test/test_get_meta_group.sh
+++ b/oomcli/test/test_get_meta_group.sh
@@ -40,12 +40,12 @@ entity-name: device
 category: batch
 description: phone
 features:
-  - name: price
-    value-type: int64
-    description: price
-  - name: model
-    value-type: string
-    description: model
+    - name: price
+      value-type: int64
+      description: price
+    - name: model
+      value-type: string
+      description: model
 '
 
 actual=$(oomcli get meta group phone -o yaml)
@@ -54,46 +54,46 @@ assert_eq "$case" "$expected" "$actual"
 case='oomcli get meta group -o yaml: multiple groups'
 expected='
 items:
-  - kind: Group
-    name: phone
-    entity-name: device
-    category: batch
-    description: phone
-    features:
-      - name: price
-        value-type: int64
-        description: price
-      - name: model
-        value-type: string
-        description: model
-  - kind: Group
-    name: student
-    entity-name: user
-    category: batch
-    description: student
-    features:
-      - name: name
-        value-type: string
-        description: name
-      - name: gender
-        value-type: string
-        description: gender
-      - name: age
-        value-type: int64
-        description: age
-  - kind: Group
-    name: user-click
-    entity-name: user
-    category: stream
-    snapshot-interval: 1s
-    description: user click post feature
-    features:
-      - name: last_5_click_posts
-        value-type: string
-        description: user last 5 click posts
-      - name: number_of_user_starred_posts
-        value-type: int64
-        description: number of posts that users starred today
+    - kind: Group
+      name: phone
+      entity-name: device
+      category: batch
+      description: phone
+      features:
+        - name: price
+          value-type: int64
+          description: price
+        - name: model
+          value-type: string
+          description: model
+    - kind: Group
+      name: student
+      entity-name: user
+      category: batch
+      description: student
+      features:
+        - name: name
+          value-type: string
+          description: name
+        - name: gender
+          value-type: string
+          description: gender
+        - name: age
+          value-type: int64
+          description: age
+    - kind: Group
+      name: user-click
+      entity-name: user
+      category: stream
+      snapshot-interval: 1s
+      description: user click post feature
+      features:
+        - name: last_5_click_posts
+          value-type: string
+          description: user last 5 click posts
+        - name: number_of_user_starred_posts
+          value-type: int64
+          description: number of posts that users starred today
 '
 actual=$(oomcli get meta group -o yaml)
 assert_eq "$case" "$expected" "$actual"