-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BEAM-13633] [Playground] Implement method to get a default example for each SDKs #16484
Changes from 2 commits
509b7ff
e9cbe79
68edd97
ab16bea
d181cce
5743386
38673a4
4071bdb
3047469
d6e5d0b
f80ebdd
e035006
c3c5c79
931b79e
29e6f61
5688841
43fc19b
27b0157
b9b77b9
2187918
3655716
7fe3d8b
6830670
af655a5
b95103c
f4ae9a7
276ddd1
94a1613
1550e3a
8e6e1e5
af3623c
a7d6ded
3c8bd03
f273192
ebf5888
29c28b5
e413153
da6baa0
1ea1026
d8b7bdb
c189363
6e63cfb
0bcb1c4
66a3ca5
52622a3
4f54530
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -262,7 +262,7 @@ func (controller *playgroundController) GetPrecompiledObjects(ctx context.Contex | |
// GetPrecompiledObjectCode returns the code of the specific example | ||
func (controller *playgroundController) GetPrecompiledObjectCode(ctx context.Context, info *pb.GetPrecompiledObjectCodeRequest) (*pb.GetPrecompiledObjectCodeResponse, error) { | ||
cd := cloud_bucket.New() | ||
codeString, err := cd.GetPrecompiledObject(ctx, info.GetCloudPath()) | ||
codeString, err := cd.GetPrecompiledObjectCode(ctx, info.GetCloudPath()) | ||
if err != nil { | ||
logger.Errorf("GetPrecompiledObjectCode(): cloud storage error: %s", err.Error()) | ||
return nil, errors.InternalError("Error during getting Precompiled Object's code", "Error with cloud connection") | ||
|
@@ -294,3 +294,28 @@ func (controller *playgroundController) GetPrecompiledObjectLogs(ctx context.Con | |
response := pb.GetPrecompiledObjectLogsResponse{Output: logs} | ||
return &response, nil | ||
} | ||
|
||
// GetDefaultPrecompiledObject returns the default precompile object for sdk. | ||
func (controller *playgroundController) GetDefaultPrecompiledObject(ctx context.Context, info *pb.GetDefaultPrecompiledObjectRequest) (*pb.GetDefaultPrecompiledObjectResponse, error) { | ||
switch info.Sdk { | ||
case pb.Sdk_SDK_UNSPECIFIED, pb.Sdk_SDK_SCIO: | ||
logger.Errorf("GetDefaultPrecompiledObject(): unimplemented sdk: %s\n", info.Sdk) | ||
return nil, errors.InvalidArgumentError("Error during preparing", "Sdk is not implemented yet: %s", info.Sdk.String()) | ||
} | ||
|
||
bucket := cloud_bucket.New() | ||
precompiledObject, err := bucket.GetDefaultPrecompileObject(ctx, info.Sdk, controller.env.ApplicationEnvs.WorkingDir()) | ||
if err != nil { | ||
logger.Errorf("GetDefaultPrecompileObject(): cloud storage error: %s", err.Error()) | ||
return nil, errors.InternalError("Error during getting default Precompiled Object", "Error with cloud connection") | ||
} | ||
|
||
response := pb.GetDefaultPrecompiledObjectResponse{PrecompiledObject: &pb.PrecompiledObject{ | ||
CloudPath: precompiledObject.CloudPath, | ||
Name: precompiledObject.Name, | ||
Description: precompiledObject.Description, | ||
Type: precompiledObject.Type, | ||
PipelineOptions: precompiledObject.PipelineOptions, | ||
}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we return the code of the example as well? Or it will be requested from the frontend after receiving this response? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this information will be enough for frontend. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please also add the |
||
return &response, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure that this file should be in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's a good place for this file. What place do you propose? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, on the other hand, we can configure default examples from one place instead of looking for them in the code. What do you think about adding the default_example field to the corresponding There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @pavel-avilov could you please change the configs based on the discussion above? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
"SDK_JAVA": "SDK_JAVA/MinimalWordCount", | ||
"SDK_GO": "SDK_GO/MinimalWordCount", | ||
"SDK_PYTHON": "SDK_PYTHON/WordCountWithMetrics" | ||
} |
Large diffs are not rendered by default.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -32,16 +32,18 @@ import ( | |||||
) | ||||||
|
||||||
const ( | ||||||
BucketName = "playground-precompiled-objects" | ||||||
OutputExtension = "output" | ||||||
LogsExtension = "log" | ||||||
MetaInfoName = "meta.info" | ||||||
Timeout = time.Second * 10 | ||||||
javaExtension = "java" | ||||||
goExtension = "go" | ||||||
pyExtension = "py" | ||||||
scioExtension = "scala" | ||||||
separatorsNumber = 2 | ||||||
BucketName = "playground-precompiled-objects" | ||||||
OutputExtension = "output" | ||||||
LogsExtension = "log" | ||||||
MetaInfoName = "meta.info" | ||||||
Timeout = time.Second * 10 | ||||||
javaExtension = "java" | ||||||
goExtension = "go" | ||||||
pyExtension = "py" | ||||||
scioExtension = "scala" | ||||||
separatorsNumber = 2 | ||||||
defaultExamplesConfigName = "DEFAULT_EXAMPLES.json" | ||||||
configFolderName = "configs" | ||||||
) | ||||||
|
||||||
type ObjectInfo struct { | ||||||
|
@@ -93,8 +95,8 @@ func New() *CloudStorage { | |||||
return &CloudStorage{} | ||||||
} | ||||||
|
||||||
// GetPrecompiledObject returns the source code of the example | ||||||
func (cd *CloudStorage) GetPrecompiledObject(ctx context.Context, precompiledObjectPath string) (string, error) { | ||||||
// GetPrecompiledObjectCode returns the source code of the example | ||||||
func (cd *CloudStorage) GetPrecompiledObjectCode(ctx context.Context, precompiledObjectPath string) (string, error) { | ||||||
extension, err := getFileExtensionBySdk(precompiledObjectPath) | ||||||
if err != nil { | ||||||
return "", err | ||||||
|
@@ -173,6 +175,30 @@ func (cd *CloudStorage) GetPrecompiledObjects(ctx context.Context, targetSdk pb. | |||||
return &precompiledObjects, nil | ||||||
} | ||||||
|
||||||
// GetDefaultPrecompileObject returns the default precompiled object for the sdk | ||||||
func (cd *CloudStorage) GetDefaultPrecompileObject(ctx context.Context, targetSdk pb.Sdk, workingDir string) (*ObjectInfo, error) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||||||
defaultExampleToSdk, err := getDefaultExamplesFromJson(workingDir) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add error handling. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||||||
if err != nil { | ||||||
return nil, err | ||||||
} | ||||||
infoPath := filepath.Join(defaultExampleToSdk[targetSdk.String()], MetaInfoName) | ||||||
metaInfo, err := cd.getFileFromBucket(ctx, infoPath, "") | ||||||
if err != nil { | ||||||
return nil, err | ||||||
} | ||||||
|
||||||
precompiledObject := ObjectInfo{} | ||||||
err = json.Unmarshal(metaInfo, &precompiledObject) | ||||||
if err != nil { | ||||||
logger.Errorf("json.Unmarshal: %v", err.Error()) | ||||||
return nil, err | ||||||
} | ||||||
|
||||||
precompiledObject.CloudPath = filepath.Dir(infoPath) | ||||||
|
||||||
return &precompiledObject, nil | ||||||
} | ||||||
|
||||||
// getPrecompiledObjectsDirs finds directories with precompiled objects | ||||||
// Since there is no notion of directory at cloud storage, then | ||||||
// to avoid duplicates of a base path (directory) need to store it in a set/map. | ||||||
|
@@ -267,6 +293,9 @@ func getFileExtensionBySdk(precompiledObjectPath string) (string, error) { | |||||
|
||||||
// getFullFilePath get full path to the precompiled object file | ||||||
func getFullFilePath(objectDir string, extension string) string { | ||||||
if extension == "" { | ||||||
return objectDir | ||||||
} | ||||||
precompiledObjectName := filepath.Base(objectDir) //the base of the object's directory matches the name of the file | ||||||
fileName := strings.Join([]string{precompiledObjectName, extension}, ".") | ||||||
filePath := filepath.Join(objectDir, fileName) | ||||||
|
@@ -288,3 +317,18 @@ func getSdkName(path string) string { | |||||
sdkName := strings.Split(path, string(os.PathSeparator))[0] // the path of the form "sdkName/example/", where the first part is sdkName | ||||||
return sdkName | ||||||
} | ||||||
|
||||||
// getDefaultExamplesFromJson reads a json file that contains information about default examples for sdk and converts him to map | ||||||
func getDefaultExamplesFromJson(workingDir string) (map[string]string, error) { | ||||||
defaultExampleToSdk := map[string]string{} | ||||||
configPath := filepath.Join(workingDir, configFolderName, defaultExamplesConfigName) | ||||||
file, err := ioutil.ReadFile(configPath) | ||||||
if err != nil { | ||||||
return nil, err | ||||||
} | ||||||
err = json.Unmarshal(file, &defaultExampleToSdk) | ||||||
if err != nil { | ||||||
return nil, err | ||||||
} | ||||||
return defaultExampleToSdk, nil | ||||||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -18,13 +18,18 @@ package cloud_bucket | |||||
import ( | ||||||
pb "beam.apache.org/playground/backend/internal/api/v1" | ||||||
"context" | ||||||
"fmt" | ||||||
"io/fs" | ||||||
"os" | ||||||
"path/filepath" | ||||||
"reflect" | ||||||
"testing" | ||||||
) | ||||||
|
||||||
const ( | ||||||
precompiledObjectPath = "SDK_JAVA/MinimalWordCount" | ||||||
targetSdk = pb.Sdk_SDK_UNSPECIFIED | ||||||
defaultExamplesConfig = "{\n \"SDK_JAVA\": \"1\",\n \"SDK_GO\": \"2\",\n \"SDK_PYTHON\": \"3\"\n}" | ||||||
) | ||||||
|
||||||
var bucket *CloudStorage | ||||||
|
@@ -35,6 +40,35 @@ func init() { | |||||
ctx = context.Background() | ||||||
} | ||||||
|
||||||
func TestMain(m *testing.M) { | ||||||
err := setup() | ||||||
if err != nil { | ||||||
panic(fmt.Errorf("error during test setup: %s", err.Error())) | ||||||
} | ||||||
defer teardown() | ||||||
m.Run() | ||||||
} | ||||||
|
||||||
func setup() error { | ||||||
err := os.Mkdir(configFolderName, fs.ModePerm) | ||||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
filePath := filepath.Join(configFolderName, defaultExamplesConfigName) | ||||||
err = os.WriteFile(filePath, []byte(defaultExamplesConfig), 0600) | ||||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
return nil | ||||||
} | ||||||
|
||||||
func teardown() { | ||||||
err := os.RemoveAll(configFolderName) | ||||||
if err != nil { | ||||||
panic(fmt.Errorf("error during test setup: %s", err.Error())) | ||||||
} | ||||||
} | ||||||
|
||||||
func Test_getFullFilePath(t *testing.T) { | ||||||
type args struct { | ||||||
examplePath string | ||||||
|
@@ -250,6 +284,50 @@ func Benchmark_GetPrecompiledObjectOutput(b *testing.B) { | |||||
|
||||||
func Benchmark_GetPrecompiledObject(b *testing.B) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||
for i := 0; i < b.N; i++ { | ||||||
_, _ = bucket.GetPrecompiledObject(ctx, precompiledObjectPath) | ||||||
_, _ = bucket.GetPrecompiledObjectCode(ctx, precompiledObjectPath) | ||||||
} | ||||||
} | ||||||
|
||||||
func Benchmark_GetDefaultPrecompileObject(b *testing.B) { | ||||||
for i := 0; i < b.N; i++ { | ||||||
_, _ = bucket.GetDefaultPrecompileObject(ctx, targetSdk, "") | ||||||
} | ||||||
} | ||||||
|
||||||
func Test_getDefaultExamplesFromJson(t *testing.T) { | ||||||
expectedMap := map[string]string{"SDK_JAVA": "1", "SDK_GO": "2", "SDK_PYTHON": "3"} | ||||||
type args struct { | ||||||
workingDir string | ||||||
} | ||||||
tests := []struct { | ||||||
name string | ||||||
args args | ||||||
want map[string]string | ||||||
wantErr bool | ||||||
}{ | ||||||
{ | ||||||
name: "get object from json", | ||||||
args: args{workingDir: ""}, | ||||||
want: expectedMap, | ||||||
wantErr: false, | ||||||
}, | ||||||
{ | ||||||
name: "error if wrong json path", | ||||||
args: args{workingDir: "Wrong_path"}, | ||||||
want: nil, | ||||||
wantErr: true, | ||||||
}, | ||||||
} | ||||||
for _, tt := range tests { | ||||||
t.Run(tt.name, func(t *testing.T) { | ||||||
got, err := getDefaultExamplesFromJson(tt.args.workingDir) | ||||||
if (err != nil) != tt.wantErr { | ||||||
t.Errorf("getDefaultExamplesFromJson() error = %v, wantErr %v", err, tt.wantErr) | ||||||
return | ||||||
} | ||||||
if !reflect.DeepEqual(got, tt.want) { | ||||||
t.Errorf("getDefaultExamplesFromJson() got = %v, want %v", got, tt.want) | ||||||
} | ||||||
}) | ||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we can include SCIO as a supported SDK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.