Skip to content

Commit 172a0eb

Browse files
zhilingcfeast-ci-bot
authored andcommitted
Add get command (feast-dev#85)
1 parent e81358f commit 172a0eb

File tree

6 files changed

+343
-31
lines changed

6 files changed

+343
-31
lines changed

cli/feast/cmd/get.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
8+
"github.com/gojek/feast/cli/feast/pkg/printer"
9+
"github.com/gojek/feast/protos/generated/go/feast/core"
10+
"github.com/spf13/cobra"
11+
)
12+
13+
// listCmd represents the list command
14+
var getCmd = &cobra.Command{
15+
Use: "get [resource] [id]",
16+
Short: "Get and print the details of the desired resource.",
17+
Long: `Get and print the details of the desired resource.
18+
19+
Valid resources include:
20+
- entity
21+
- feature
22+
- storage
23+
- job
24+
25+
Examples:
26+
- feast get entity myentity`,
27+
RunE: func(cmd *cobra.Command, args []string) error {
28+
if len(args) == 0 {
29+
return cmd.Help()
30+
}
31+
32+
if len(args) != 2 {
33+
return errors.New("invalid number of arguments for list command")
34+
}
35+
36+
initConn()
37+
err := get(args[0], args[1])
38+
if err != nil {
39+
return fmt.Errorf("failed to list %s: %v", args[0], err)
40+
}
41+
return nil
42+
},
43+
}
44+
45+
func init() {
46+
rootCmd.AddCommand(getCmd)
47+
}
48+
49+
func get(resource string, id string) error {
50+
ctx := context.Background()
51+
52+
switch resource {
53+
case "feature":
54+
return getFeature(ctx, core.NewUIServiceClient(coreConn), id)
55+
case "entity":
56+
return getEntity(ctx, core.NewUIServiceClient(coreConn), id)
57+
case "storage":
58+
return getStorage(ctx, core.NewUIServiceClient(coreConn), id)
59+
case "job":
60+
return getJob(ctx, core.NewJobServiceClient(coreConn), id)
61+
default:
62+
return fmt.Errorf("invalid resource %s: please choose one of [features, entities, storage, jobs]", resource)
63+
}
64+
}
65+
66+
func getFeature(ctx context.Context, cli core.UIServiceClient, id string) error {
67+
response, err := cli.GetFeature(ctx, &core.UIServiceTypes_GetFeatureRequest{Id: id})
68+
if err != nil {
69+
return err
70+
}
71+
printer.PrintFeatureDetail(response.GetFeature())
72+
return nil
73+
}
74+
75+
func getEntity(ctx context.Context, cli core.UIServiceClient, id string) error {
76+
response, err := cli.GetEntity(ctx, &core.UIServiceTypes_GetEntityRequest{Id: id})
77+
if err != nil {
78+
return err
79+
}
80+
printer.PrintEntityDetail(response.GetEntity())
81+
return nil
82+
}
83+
84+
func getStorage(ctx context.Context, cli core.UIServiceClient, id string) error {
85+
response, err := cli.GetStorage(ctx, &core.UIServiceTypes_GetStorageRequest{Id: id})
86+
if err != nil {
87+
return err
88+
}
89+
printer.PrintStorageDetail(response.GetStorage())
90+
return nil
91+
}
92+
93+
func getJob(ctx context.Context, cli core.JobServiceClient, id string) error {
94+
response, err := cli.GetJob(ctx, &core.JobServiceTypes_GetJobRequest{Id: id})
95+
if err != nil {
96+
return err
97+
}
98+
printer.PrintJobDetail(response.GetJob())
99+
return nil
100+
}

cli/feast/cmd/jobs.go

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"io/ioutil"
2222

2323
"github.com/gojek/feast/cli/feast/pkg/parse"
24-
"github.com/gojek/feast/cli/feast/pkg/printer"
2524
"github.com/gojek/feast/protos/generated/go/feast/core"
2625

2726
"github.com/spf13/cobra"
@@ -48,21 +47,6 @@ var jobsRunCmd = &cobra.Command{
4847
},
4948
}
5049

51-
var jobsInfoCmd = &cobra.Command{
52-
Use: "info [job_id]",
53-
Short: "Get details for a single job",
54-
RunE: func(cmd *cobra.Command, args []string) error {
55-
if len(args) == 0 {
56-
return cmd.Help()
57-
}
58-
if len(args) > 1 {
59-
return errors.New("invalid number of arguments for jobs info command")
60-
}
61-
ctx := context.Background()
62-
return getJob(ctx, args[0])
63-
},
64-
}
65-
6650
var jobsAbortCmd = &cobra.Command{
6751
Use: "stop [job_id]",
6852
Short: "Stop the given job",
@@ -80,7 +64,6 @@ var jobsAbortCmd = &cobra.Command{
8064

8165
func init() {
8266
jobsCmd.AddCommand(jobsRunCmd)
83-
jobsCmd.AddCommand(jobsInfoCmd)
8467
jobsCmd.AddCommand(jobsAbortCmd)
8568
rootCmd.AddCommand(jobsCmd)
8669
}
@@ -106,17 +89,6 @@ func runJob(ctx context.Context, path string) error {
10689
return nil
10790
}
10891

109-
func getJob(ctx context.Context, id string) error {
110-
initConn()
111-
jobsClient := core.NewJobServiceClient(coreConn)
112-
response, err := jobsClient.GetJob(ctx, &core.JobServiceTypes_GetJobRequest{Id: id})
113-
if err != nil {
114-
return err
115-
}
116-
printer.PrintJobDetail(response.GetJob())
117-
return nil
118-
}
119-
12092
func abortJob(ctx context.Context, id string) error {
12193
initConn()
12294
jobsClient := core.NewJobServiceClient(coreConn)

cli/feast/pkg/printer/job.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ import (
2222
"github.com/gojek/feast/protos/generated/go/feast/core"
2323
)
2424

25-
// PrintJobDetail pretty prints the given job detail
26-
func PrintJobDetail(jobDetail *core.JobServiceTypes_JobDetail) {
25+
// PrintJobDetail pretty prints the given job detail.
26+
// Prints and returns the resultant formatted string.
27+
func PrintJobDetail(jobDetail *core.JobServiceTypes_JobDetail) string {
2728
lines := []string{fmt.Sprintf("%s:\t%s", "Id", jobDetail.GetId()),
2829
fmt.Sprintf("%s:\t%s", "Ext Id", jobDetail.GetExtId()),
2930
fmt.Sprintf("%s:\t%s", "Type", jobDetail.GetType()),
@@ -46,7 +47,9 @@ func PrintJobDetail(jobDetail *core.JobServiceTypes_JobDetail) {
4647
for _, feature := range jobDetail.GetFeatures() {
4748
lines = append(lines, printFeature(feature, jobDetail.GetMetrics()))
4849
}
49-
fmt.Println(strings.Join(lines, "\n"))
50+
out := strings.Join(lines, "\n")
51+
fmt.Println(out)
52+
return out
5053
}
5154

5255
func printEntity(entityName string, metrics map[string]float64) string {
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package printer
2+
3+
import (
4+
"testing"
5+
6+
"github.com/golang/protobuf/ptypes/timestamp"
7+
8+
"github.com/gojek/feast/protos/generated/go/feast/core"
9+
"github.com/gojek/feast/protos/generated/go/feast/specs"
10+
"github.com/gojek/feast/protos/generated/go/feast/types"
11+
)
12+
13+
func TestPrintFeature(t *testing.T) {
14+
tt := []struct {
15+
name string
16+
input *core.UIServiceTypes_FeatureDetail
17+
expected string
18+
}{
19+
{
20+
name: "with storage",
21+
input: &core.UIServiceTypes_FeatureDetail{
22+
Spec: &specs.FeatureSpec{
23+
Id: "test.none.test_feature_two",
24+
Owner: "bob@example.com",
25+
Name: "test_feature_two",
26+
Description: "testing feature",
27+
Uri: "https://github.com/bob/example",
28+
Granularity: types.Granularity_NONE,
29+
ValueType: types.ValueType_INT64,
30+
Entity: "test",
31+
DataStores: &specs.DataStores{
32+
Serving: &specs.DataStore{
33+
Id: "REDIS",
34+
},
35+
Warehouse: &specs.DataStore{
36+
Id: "BIGQUERY",
37+
},
38+
},
39+
},
40+
BigqueryView: "bqurl",
41+
Jobs: []string{"job1", "job2"},
42+
LastUpdated: &timestamp.Timestamp{Seconds: 1},
43+
Created: &timestamp.Timestamp{Seconds: 1},
44+
},
45+
expected: `Id: test.none.test_feature_two
46+
Entity: test
47+
Owner: bob@example.com
48+
Description: testing feature
49+
ValueType: INT64
50+
Uri: https://github.com/bob/example
51+
DataStores:
52+
Serving: REDIS
53+
Warehouse: BIGQUERY
54+
Created: 1970-01-01T07:30:01+07:30
55+
LastUpdated: 1970-01-01T07:30:01+07:30
56+
Related Jobs:
57+
- job1
58+
- job2`,
59+
}, {
60+
name: "no storage",
61+
input: &core.UIServiceTypes_FeatureDetail{
62+
Spec: &specs.FeatureSpec{
63+
Id: "test.none.test_feature_two",
64+
Owner: "bob@example.com",
65+
Name: "test_feature_two",
66+
Description: "testing feature",
67+
Uri: "https://github.com/bob/example",
68+
Granularity: types.Granularity_NONE,
69+
ValueType: types.ValueType_INT64,
70+
Entity: "test",
71+
},
72+
BigqueryView: "bqurl",
73+
Jobs: []string{"job1", "job2"},
74+
LastUpdated: &timestamp.Timestamp{Seconds: 1},
75+
Created: &timestamp.Timestamp{Seconds: 1},
76+
},
77+
expected: `Id: test.none.test_feature_two
78+
Entity: test
79+
Owner: bob@example.com
80+
Description: testing feature
81+
ValueType: INT64
82+
Uri: https://github.com/bob/example
83+
Created: 1970-01-01T07:30:01+07:30
84+
LastUpdated: 1970-01-01T07:30:01+07:30
85+
Related Jobs:
86+
- job1
87+
- job2`,
88+
},
89+
}
90+
91+
for _, tc := range tt {
92+
t.Run(tc.name, func(t *testing.T) {
93+
out := PrintFeatureDetail(tc.input)
94+
if out != tc.expected {
95+
t.Errorf("Expected output:\n%s \nActual:\n%s \n", tc.expected, out)
96+
}
97+
})
98+
}
99+
}
100+
101+
func TestPrintEntity(t *testing.T) {
102+
entityDetail := &core.UIServiceTypes_EntityDetail{
103+
Spec: &specs.EntitySpec{
104+
Name: "test",
105+
Description: "my test entity",
106+
Tags: []string{"tag1", "tag2"},
107+
},
108+
Jobs: []string{"job1", "job2"},
109+
LastUpdated: &timestamp.Timestamp{Seconds: 1},
110+
}
111+
out := PrintEntityDetail(entityDetail)
112+
expected := `Name: test
113+
Description: my test entity
114+
Tags: tag1,tag2
115+
LastUpdated: 1970-01-01T07:30:01+07:30
116+
Related Jobs:
117+
- job1
118+
- job2`
119+
if out != expected {
120+
t.Errorf("Expected output:\n%s \nActual:\n%s \n", expected, out)
121+
}
122+
}
123+
124+
func TestPrintStorage(t *testing.T) {
125+
storageDetail := &core.UIServiceTypes_StorageDetail{
126+
Spec: &specs.StorageSpec{
127+
Id: "REDIS1",
128+
Type: "redis",
129+
Options: map[string]string{
130+
"option1": "value1",
131+
"option2": "value2",
132+
},
133+
},
134+
LastUpdated: &timestamp.Timestamp{Seconds: 1},
135+
}
136+
out := PrintStorageDetail(storageDetail)
137+
expected := `Id: REDIS1
138+
Type: redis
139+
Options:
140+
option1: value1
141+
option2: value2
142+
LastUpdated: 1970-01-01T07:30:01+07:30`
143+
if out != expected {
144+
t.Errorf("Expected output:\n%s \nActual:\n%s \n", expected, out)
145+
}
146+
}

0 commit comments

Comments
 (0)