You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@beam.apache.org by pa...@apache.org on 2021/10/21 16:18:45 UTC

[beam] branch master updated: Merge pull request #15761 from [BEAM-13008] Create gradle tasks for the Beam Playground

This is an automated email from the ASF dual-hosted git repository.

pabloem pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git


The following commit(s) were added to refs/heads/master by this push:
     new dc0548e  Merge pull request #15761 from [BEAM-13008] Create gradle tasks for the Beam Playground
dc0548e is described below

commit dc0548e20ca047cfa991d4138d63806e9d15086c
Author: Damon Douglas <da...@users.noreply.github.com>
AuthorDate: Thu Oct 21 16:17:13 2021 +0000

    Merge pull request #15761 from [BEAM-13008] Create gradle tasks for the Beam Playground
    
    * Implement Beam Playground gradle tasks
    
    * Fix trailing whitespace
    
    * Remove format from precommit
    
    * Fix order of includes and add descriptions
    
    * [BEAM-13008] add generated files and fix analysis issues (#2)
    
    * Reconcile errors with playgroundPrecommit output
    
    Co-authored-by: Aydar Farrakhov <st...@gmail.com>
---
 .gitignore                                         |   7 +
 build.gradle.kts                                   |  13 +-
 playground/README.md                               |  57 +++
 .../v1/playground.proto => api/v1/api.proto}       |   6 +-
 playground/backend/build.gradle.kts                |  52 +++
 playground/backend/cmd/server/controller.go        |   3 +-
 playground/backend/cmd/server/controller_test.go   |   2 +-
 playground/backend/cmd/server/server.go            |  11 +-
 playground/backend/go.sum                          |  55 +--
 .../api/{playground.pb.go => v1/api.pb.go}         | 338 ++++++++--------
 .../{playground_grpc.pb.go => v1/api_grpc.pb.go}   |  27 +-
 .../go_helper.go => environment/environment.go}    |  17 +-
 playground/backend/internal/executors/executor.go  |   2 +-
 playground/backend/internal/executors/go_helper.go |   2 +
 .../backend/internal/executors/java_helper_test.go |   2 +-
 playground/backend/internal/fs_tool/fs.go          |   2 +-
 playground/backend/internal/fs_tool/fs_test.go     |   2 +-
 playground/buf.gen.yaml                            |  32 ++
 playground/build.gradle.kts                        |  30 ++
 playground/frontend/README.md                      |   6 +-
 playground/frontend/analysis_options.yaml          |   4 +
 playground/frontend/build.gradle.kts               |  74 ++++
 playground/frontend/lib/api/v1/api.pb.dart         | 450 +++++++++++++++++++++
 playground/frontend/lib/api/v1/api.pbenum.dart     |  68 ++++
 playground/frontend/lib/api/v1/api.pbgrpc.dart     | 158 ++++++++
 playground/frontend/lib/api/v1/api.pbjson.dart     | 137 +++++++
 .../code_client/grpc_code_client.dart              |   2 +-
 .../code_repository_test.mocks.dart                |  86 ++++
 settings.gradle.kts                                |   3 +
 29 files changed, 1403 insertions(+), 245 deletions(-)

diff --git a/.gitignore b/.gitignore
index 33aac5b..857d52d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -113,3 +113,10 @@ website/www/site/code_samples
 website/www/site/_config_branch_repo.toml
 website/www/yarn-error.log
 !website/www/site/content
+
+# Dart/Flutter
+**/.dart_tool
+**/.packages
+**/.flutter-plugins
+**/.flutter-plugins-dependencies
+**/generated_plugin_registrant.dart
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index a0f6313..b10b88d 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -124,9 +124,7 @@ tasks.rat {
     // Ignore Flutter autogenerated files for Playground
     "playground/frontend/.metadata",
     "playground/frontend/pubspec.lock",
-    "playground/frontend/assets/**/*.svg",
-    "playground/frontend/assets/**/*.png",
-    "playground/frontend/assets/**/*.jpg",
+    "playground/frontend/**/*.svg",
 
     // Ignore .gitkeep file
     "**/.gitkeep"
@@ -249,6 +247,15 @@ task("goIntegrationTests") {
   dependsOn(":runners:google-cloud-dataflow-java:worker:shadowJar")
 }
 
+task("playgroundPreCommit") {
+  dependsOn(":playground:backend:tidy")
+  dependsOn(":playground:backend:test")
+
+  dependsOn(":playground:frontend:pubGet")
+  dependsOn(":playground:frontend:analyze")
+  dependsOn(":playground:frontend:test")
+}
+
 task("pythonPreCommit") {
   dependsOn(":sdks:python:test-suites:tox:pycommon:preCommitPyCommon")
   dependsOn(":sdks:python:test-suites:tox:py36:preCommitPy36")
diff --git a/playground/README.md b/playground/README.md
new file mode 100644
index 0000000..fe90b2e
--- /dev/null
+++ b/playground/README.md
@@ -0,0 +1,57 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+
+# Playground
+
+The Beam Playground is a web application to run Beam code snippets in a modern
+browser.  This directory holds code to build, test, and deploy the frontend
+and backend services.
+
+# Requirements
+
+The following requirements are needed for development, testing, and deploying.
+
+- [go 1.16+](https://golang.org)
+- [flutter](https://flutter.dev/)
+- Go protobuf dependencies (See [Go gRPC Quickstart](https://grpc.io/docs/languages/go/quickstart/))
+- Dart protobuf dependencies (See [Dart gRPC Quickstart](https://grpc.io/docs/languages/dart/))
+- [buf](https://docs.buf.build/installation)
+
+# Available Gradle Tasks
+
+## Perform overall pre-commit checks
+
+```
+cd beam
+./gradlew playgroundPrecommit
+```
+
+## To see available gradle tasks for playground:
+
+```
+cd beam
+./gradlew playground:tasks
+```
+
+## Re-generate protobuf
+
+```
+cd beam
+./gradlew playground:generateProto
+```
diff --git a/playground/playground/v1/playground.proto b/playground/api/v1/api.proto
similarity index 96%
rename from playground/playground/v1/playground.proto
rename to playground/api/v1/api.proto
index 7c26e00..b132980 100644
--- a/playground/playground/v1/playground.proto
+++ b/playground/api/v1/api.proto
@@ -17,9 +17,9 @@
  */
 
 syntax = "proto3";
+option go_package = "beam.apache.org/playground/backend/internal;playground";
 
-option go_package = "github.com/apache/beam/playground/v1;playground";
-package playground.v1;
+package api.v1;
 
 enum Sdk {
   SDK_UNSPECIFIED = 0;
@@ -92,4 +92,4 @@ service PlaygroundService {
 
   // Get the result of pipeline compilation.
   rpc GetCompileOutput(GetCompileOutputRequest) returns (GetCompileOutputResponse);
-}
\ No newline at end of file
+}
diff --git a/playground/backend/build.gradle.kts b/playground/backend/build.gradle.kts
new file mode 100644
index 0000000..0547b7c
--- /dev/null
+++ b/playground/backend/build.gradle.kts
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * License); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+description = "Apache Beam :: Playground :: Backend"
+
+task("format") {
+  group = "build"
+  description = "Format backend go code"
+  doLast {
+    exec {
+      executable("gofmt")
+      args("-w", ".")
+    }
+  }
+}
+
+task("tidy") {
+  group = "build"
+  description = "Run go mod tidy"
+  doLast {
+    exec {
+      executable("go")
+      args("mod", "tidy")
+    }
+  }
+}
+
+task("test") {
+  group = "verification"
+  description = "Test the backend"
+  doLast {
+    exec {
+      executable("go")
+      args("test", "internal/...")
+    }
+  }
+}
diff --git a/playground/backend/cmd/server/controller.go b/playground/backend/cmd/server/controller.go
index 13ab658..d38ff98 100644
--- a/playground/backend/cmd/server/controller.go
+++ b/playground/backend/cmd/server/controller.go
@@ -15,8 +15,9 @@
 package main
 
 import (
-	pb "beam.apache.org/playground/backend/internal/api"
 	"context"
+
+	pb "beam.apache.org/playground/backend/internal/api/v1"
 	"github.com/google/uuid"
 )
 
diff --git a/playground/backend/cmd/server/controller_test.go b/playground/backend/cmd/server/controller_test.go
index 38885ec..a7f971e 100644
--- a/playground/backend/cmd/server/controller_test.go
+++ b/playground/backend/cmd/server/controller_test.go
@@ -15,7 +15,7 @@
 package main
 
 import (
-	pb "beam.apache.org/playground/backend/internal/api"
+	pb "beam.apache.org/playground/backend/internal/api/v1"
 	"context"
 	"github.com/google/uuid"
 	"google.golang.org/grpc"
diff --git a/playground/backend/cmd/server/server.go b/playground/backend/cmd/server/server.go
index 05045ea..3102188 100644
--- a/playground/backend/cmd/server/server.go
+++ b/playground/backend/cmd/server/server.go
@@ -16,14 +16,15 @@
 package main
 
 import (
-	pb "beam.apache.org/playground/backend/internal/api"
-	"beam.apache.org/playground/backend/internal/environment"
 	"context"
+	"log"
+	"os"
+
+	pb "beam.apache.org/playground/backend/internal/api/v1"
+	"beam.apache.org/playground/backend/internal/environment"
 	"github.com/improbable-eng/grpc-web/go/grpcweb"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/grpclog"
-	"log"
-	"os"
 )
 
 // runServer is starting http server wrapped on grpc
@@ -39,7 +40,7 @@ func runServer() error {
 	handler := Wrap(grpcServer, getGrpcWebOptions())
 	errChan := make(chan error)
 
-	go listenHttp(ctx, errChan, envService.ServerEnvs, handler)
+	go listenHttp(ctx, errChan, envService, handler)
 
 	for {
 		select {
diff --git a/playground/backend/go.sum b/playground/backend/go.sum
index 3c4a244..5c43280 100644
--- a/playground/backend/go.sum
+++ b/playground/backend/go.sum
@@ -4,7 +4,6 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
 github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
@@ -31,13 +30,11 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
 github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
 github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
 github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
@@ -47,6 +44,7 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
 github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
@@ -61,7 +59,6 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
 github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
 github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@@ -69,8 +66,10 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB
 github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
+github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
 github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@@ -81,14 +80,20 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
 github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
+github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
 github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
 github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
+github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
 github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
 github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
 github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
+github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
 github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
+github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
 github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
 github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -113,8 +118,6 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
 github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -136,6 +139,7 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51
 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
 github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
@@ -175,6 +179,7 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV
 github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
 github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
@@ -182,9 +187,8 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
 github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
-github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
-github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@@ -192,6 +196,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
+github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
 github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
 github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
 github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
@@ -200,6 +205,7 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -212,10 +218,13 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
 github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo=
 github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
@@ -250,6 +259,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -294,7 +304,6 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -307,9 +316,12 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
 github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
 github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
 github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
 github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
@@ -367,11 +379,9 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
 golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20211007125505-59d4e928ea9d h1:QWMn1lFvU/nZ58ssWqiFJMd3DKIII8NYc4sn708XgKs=
-golang.org/x/net v0.0.0-20211007125505-59d4e928ea9d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -402,26 +412,20 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac h1:oN6lz7iLW/YC7un8pq+9bOLyXrprv2+DKfkJY+2LJJw=
-golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -454,11 +458,9 @@ google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dT
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506 h1:uLBY0yHDCj2PMQ98KWDSIDFwn9zK2zh+tgWtbvPPBjI=
 google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20211007155348-82e027067bd4 h1:YXPV/eKW0ZWRdB5tyI6aPoaa2Wxb4OSlFrTREMdwn64=
-google.golang.org/genproto v0.0.0-20211007155348-82e027067bd4/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
@@ -474,7 +476,6 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
 google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
 google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E=
 google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
@@ -488,7 +489,6 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
 google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
 google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
@@ -511,15 +511,16 @@ gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
 nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
-nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
-nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
diff --git a/playground/backend/internal/api/playground.pb.go b/playground/backend/internal/api/v1/api.pb.go
similarity index 55%
rename from playground/backend/internal/api/playground.pb.go
rename to playground/backend/internal/api/v1/api.pb.go
index cc3ebdf..b440b19 100644
--- a/playground/backend/internal/api/playground.pb.go
+++ b/playground/backend/internal/api/v1/api.pb.go
@@ -17,9 +17,9 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.27.1
+// 	protoc-gen-go v1.26.0
 // 	protoc        v3.17.3
-// source: playground.proto
+// source: api/v1/api.proto
 
 package playground
 
@@ -76,11 +76,11 @@ func (x Sdk) String() string {
 }
 
 func (Sdk) Descriptor() protoreflect.EnumDescriptor {
-	return file_playground_proto_enumTypes[0].Descriptor()
+	return file_api_v1_api_proto_enumTypes[0].Descriptor()
 }
 
 func (Sdk) Type() protoreflect.EnumType {
-	return &file_playground_proto_enumTypes[0]
+	return &file_api_v1_api_proto_enumTypes[0]
 }
 
 func (x Sdk) Number() protoreflect.EnumNumber {
@@ -89,7 +89,7 @@ func (x Sdk) Number() protoreflect.EnumNumber {
 
 // Deprecated: Use Sdk.Descriptor instead.
 func (Sdk) EnumDescriptor() ([]byte, []int) {
-	return file_playground_proto_rawDescGZIP(), []int{0}
+	return file_api_v1_api_proto_rawDescGZIP(), []int{0}
 }
 
 type Status int32
@@ -128,11 +128,11 @@ func (x Status) String() string {
 }
 
 func (Status) Descriptor() protoreflect.EnumDescriptor {
-	return file_playground_proto_enumTypes[1].Descriptor()
+	return file_api_v1_api_proto_enumTypes[1].Descriptor()
 }
 
 func (Status) Type() protoreflect.EnumType {
-	return &file_playground_proto_enumTypes[1]
+	return &file_api_v1_api_proto_enumTypes[1]
 }
 
 func (x Status) Number() protoreflect.EnumNumber {
@@ -141,7 +141,7 @@ func (x Status) Number() protoreflect.EnumNumber {
 
 // Deprecated: Use Status.Descriptor instead.
 func (Status) EnumDescriptor() ([]byte, []int) {
-	return file_playground_proto_rawDescGZIP(), []int{1}
+	return file_api_v1_api_proto_rawDescGZIP(), []int{1}
 }
 
 // RunCodeRequest represents a code text and options of SDK which executes the code.
@@ -151,13 +151,13 @@ type RunCodeRequest struct {
 	unknownFields protoimpl.UnknownFields
 
 	Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"`
-	Sdk  Sdk    `protobuf:"varint,2,opt,name=sdk,proto3,enum=playground.v1.Sdk" json:"sdk,omitempty"`
+	Sdk  Sdk    `protobuf:"varint,2,opt,name=sdk,proto3,enum=api.v1.Sdk" json:"sdk,omitempty"`
 }
 
 func (x *RunCodeRequest) Reset() {
 	*x = RunCodeRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_playground_proto_msgTypes[0]
+		mi := &file_api_v1_api_proto_msgTypes[0]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -170,7 +170,7 @@ func (x *RunCodeRequest) String() string {
 func (*RunCodeRequest) ProtoMessage() {}
 
 func (x *RunCodeRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_playground_proto_msgTypes[0]
+	mi := &file_api_v1_api_proto_msgTypes[0]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -183,7 +183,7 @@ func (x *RunCodeRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use RunCodeRequest.ProtoReflect.Descriptor instead.
 func (*RunCodeRequest) Descriptor() ([]byte, []int) {
-	return file_playground_proto_rawDescGZIP(), []int{0}
+	return file_api_v1_api_proto_rawDescGZIP(), []int{0}
 }
 
 func (x *RunCodeRequest) GetCode() string {
@@ -212,7 +212,7 @@ type RunCodeResponse struct {
 func (x *RunCodeResponse) Reset() {
 	*x = RunCodeResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_playground_proto_msgTypes[1]
+		mi := &file_api_v1_api_proto_msgTypes[1]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -225,7 +225,7 @@ func (x *RunCodeResponse) String() string {
 func (*RunCodeResponse) ProtoMessage() {}
 
 func (x *RunCodeResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_playground_proto_msgTypes[1]
+	mi := &file_api_v1_api_proto_msgTypes[1]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -238,7 +238,7 @@ func (x *RunCodeResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use RunCodeResponse.ProtoReflect.Descriptor instead.
 func (*RunCodeResponse) Descriptor() ([]byte, []int) {
-	return file_playground_proto_rawDescGZIP(), []int{1}
+	return file_api_v1_api_proto_rawDescGZIP(), []int{1}
 }
 
 func (x *RunCodeResponse) GetPipelineUuid() string {
@@ -260,7 +260,7 @@ type CheckStatusRequest struct {
 func (x *CheckStatusRequest) Reset() {
 	*x = CheckStatusRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_playground_proto_msgTypes[2]
+		mi := &file_api_v1_api_proto_msgTypes[2]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -273,7 +273,7 @@ func (x *CheckStatusRequest) String() string {
 func (*CheckStatusRequest) ProtoMessage() {}
 
 func (x *CheckStatusRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_playground_proto_msgTypes[2]
+	mi := &file_api_v1_api_proto_msgTypes[2]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -286,7 +286,7 @@ func (x *CheckStatusRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use CheckStatusRequest.ProtoReflect.Descriptor instead.
 func (*CheckStatusRequest) Descriptor() ([]byte, []int) {
-	return file_playground_proto_rawDescGZIP(), []int{2}
+	return file_api_v1_api_proto_rawDescGZIP(), []int{2}
 }
 
 func (x *CheckStatusRequest) GetPipelineUuid() string {
@@ -302,13 +302,13 @@ type CheckStatusResponse struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Status Status `protobuf:"varint,1,opt,name=status,proto3,enum=playground.v1.Status" json:"status,omitempty"`
+	Status Status `protobuf:"varint,1,opt,name=status,proto3,enum=api.v1.Status" json:"status,omitempty"`
 }
 
 func (x *CheckStatusResponse) Reset() {
 	*x = CheckStatusResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_playground_proto_msgTypes[3]
+		mi := &file_api_v1_api_proto_msgTypes[3]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -321,7 +321,7 @@ func (x *CheckStatusResponse) String() string {
 func (*CheckStatusResponse) ProtoMessage() {}
 
 func (x *CheckStatusResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_playground_proto_msgTypes[3]
+	mi := &file_api_v1_api_proto_msgTypes[3]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -334,7 +334,7 @@ func (x *CheckStatusResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use CheckStatusResponse.ProtoReflect.Descriptor instead.
 func (*CheckStatusResponse) Descriptor() ([]byte, []int) {
-	return file_playground_proto_rawDescGZIP(), []int{3}
+	return file_api_v1_api_proto_rawDescGZIP(), []int{3}
 }
 
 func (x *CheckStatusResponse) GetStatus() Status {
@@ -356,7 +356,7 @@ type GetCompileOutputRequest struct {
 func (x *GetCompileOutputRequest) Reset() {
 	*x = GetCompileOutputRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_playground_proto_msgTypes[4]
+		mi := &file_api_v1_api_proto_msgTypes[4]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -369,7 +369,7 @@ func (x *GetCompileOutputRequest) String() string {
 func (*GetCompileOutputRequest) ProtoMessage() {}
 
 func (x *GetCompileOutputRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_playground_proto_msgTypes[4]
+	mi := &file_api_v1_api_proto_msgTypes[4]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -382,7 +382,7 @@ func (x *GetCompileOutputRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetCompileOutputRequest.ProtoReflect.Descriptor instead.
 func (*GetCompileOutputRequest) Descriptor() ([]byte, []int) {
-	return file_playground_proto_rawDescGZIP(), []int{4}
+	return file_api_v1_api_proto_rawDescGZIP(), []int{4}
 }
 
 func (x *GetCompileOutputRequest) GetPipelineUuid() string {
@@ -399,13 +399,13 @@ type GetCompileOutputResponse struct {
 	unknownFields protoimpl.UnknownFields
 
 	Output            string `protobuf:"bytes,1,opt,name=output,proto3" json:"output,omitempty"`
-	CompilationStatus Status `protobuf:"varint,2,opt,name=compilation_status,json=compilationStatus,proto3,enum=playground.v1.Status" json:"compilation_status,omitempty"`
+	CompilationStatus Status `protobuf:"varint,2,opt,name=compilation_status,json=compilationStatus,proto3,enum=api.v1.Status" json:"compilation_status,omitempty"`
 }
 
 func (x *GetCompileOutputResponse) Reset() {
 	*x = GetCompileOutputResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_playground_proto_msgTypes[5]
+		mi := &file_api_v1_api_proto_msgTypes[5]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -418,7 +418,7 @@ func (x *GetCompileOutputResponse) String() string {
 func (*GetCompileOutputResponse) ProtoMessage() {}
 
 func (x *GetCompileOutputResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_playground_proto_msgTypes[5]
+	mi := &file_api_v1_api_proto_msgTypes[5]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -431,7 +431,7 @@ func (x *GetCompileOutputResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetCompileOutputResponse.ProtoReflect.Descriptor instead.
 func (*GetCompileOutputResponse) Descriptor() ([]byte, []int) {
-	return file_playground_proto_rawDescGZIP(), []int{5}
+	return file_api_v1_api_proto_rawDescGZIP(), []int{5}
 }
 
 func (x *GetCompileOutputResponse) GetOutput() string {
@@ -460,7 +460,7 @@ type GetRunOutputRequest struct {
 func (x *GetRunOutputRequest) Reset() {
 	*x = GetRunOutputRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_playground_proto_msgTypes[6]
+		mi := &file_api_v1_api_proto_msgTypes[6]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -473,7 +473,7 @@ func (x *GetRunOutputRequest) String() string {
 func (*GetRunOutputRequest) ProtoMessage() {}
 
 func (x *GetRunOutputRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_playground_proto_msgTypes[6]
+	mi := &file_api_v1_api_proto_msgTypes[6]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -486,7 +486,7 @@ func (x *GetRunOutputRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetRunOutputRequest.ProtoReflect.Descriptor instead.
 func (*GetRunOutputRequest) Descriptor() ([]byte, []int) {
-	return file_playground_proto_rawDescGZIP(), []int{6}
+	return file_api_v1_api_proto_rawDescGZIP(), []int{6}
 }
 
 func (x *GetRunOutputRequest) GetPipelineUuid() string {
@@ -503,13 +503,13 @@ type GetRunOutputResponse struct {
 	unknownFields protoimpl.UnknownFields
 
 	Output            string `protobuf:"bytes,1,opt,name=output,proto3" json:"output,omitempty"`
-	CompilationStatus Status `protobuf:"varint,2,opt,name=compilation_status,json=compilationStatus,proto3,enum=playground.v1.Status" json:"compilation_status,omitempty"`
+	CompilationStatus Status `protobuf:"varint,2,opt,name=compilation_status,json=compilationStatus,proto3,enum=api.v1.Status" json:"compilation_status,omitempty"`
 }
 
 func (x *GetRunOutputResponse) Reset() {
 	*x = GetRunOutputResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_playground_proto_msgTypes[7]
+		mi := &file_api_v1_api_proto_msgTypes[7]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -522,7 +522,7 @@ func (x *GetRunOutputResponse) String() string {
 func (*GetRunOutputResponse) ProtoMessage() {}
 
 func (x *GetRunOutputResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_playground_proto_msgTypes[7]
+	mi := &file_api_v1_api_proto_msgTypes[7]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -535,7 +535,7 @@ func (x *GetRunOutputResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetRunOutputResponse.ProtoReflect.Descriptor instead.
 func (*GetRunOutputResponse) Descriptor() ([]byte, []int) {
-	return file_playground_proto_rawDescGZIP(), []int{7}
+	return file_api_v1_api_proto_rawDescGZIP(), []int{7}
 }
 
 func (x *GetRunOutputResponse) GetOutput() string {
@@ -552,130 +552,124 @@ func (x *GetRunOutputResponse) GetCompilationStatus() Status {
 	return Status_STATUS_UNSPECIFIED
 }
 
-var File_playground_proto protoreflect.FileDescriptor
-
-var file_playground_proto_rawDesc = []byte{
-	0x0a, 0x10, 0x70, 0x6c, 0x61, 0x79, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x12, 0x0d, 0x70, 0x6c, 0x61, 0x79, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x76,
-	0x31, 0x22, 0x4a, 0x0a, 0x0e, 0x52, 0x75, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x24, 0x0a, 0x03, 0x73, 0x64, 0x6b, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x70, 0x6c, 0x61, 0x79, 0x67, 0x72, 0x6f, 0x75, 0x6e,
-	0x64, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x64, 0x6b, 0x52, 0x03, 0x73, 0x64, 0x6b, 0x22, 0x36, 0x0a,
-	0x0f, 0x52, 0x75, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x12, 0x23, 0x0a, 0x0d, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x75, 0x75, 0x69,
-	0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e,
-	0x65, 0x55, 0x75, 0x69, 0x64, 0x22, 0x39, 0x0a, 0x12, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x74,
-	0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x70,
+var File_api_v1_api_proto protoreflect.FileDescriptor
+
+var file_api_v1_api_proto_rawDesc = []byte{
+	0x0a, 0x10, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x12, 0x06, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x22, 0x43, 0x0a, 0x0e, 0x52, 0x75,
+	0x6e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04,
+	0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65,
+	0x12, 0x1d, 0x0a, 0x03, 0x73, 0x64, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0b, 0x2e,
+	0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x64, 0x6b, 0x52, 0x03, 0x73, 0x64, 0x6b, 0x22,
+	0x36, 0x0a, 0x0f, 0x52, 0x75, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x75,
+	0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x69, 0x70, 0x65, 0x6c,
+	0x69, 0x6e, 0x65, 0x55, 0x75, 0x69, 0x64, 0x22, 0x39, 0x0a, 0x12, 0x43, 0x68, 0x65, 0x63, 0x6b,
+	0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a,
+	0x0d, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x55, 0x75,
+	0x69, 0x64, 0x22, 0x3d, 0x0a, 0x13, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75,
+	0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x73, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x61, 0x70, 0x69, 0x2e,
+	0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
+	0x73, 0x22, 0x3e, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x4f,
+	0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d,
+	0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x55, 0x75, 0x69,
+	0x64, 0x22, 0x71, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x4f,
+	0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a,
+	0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f,
+	0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x3d, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x61,
+	0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x0e, 0x32, 0x0e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75,
+	0x73, 0x52, 0x11, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74,
+	0x61, 0x74, 0x75, 0x73, 0x22, 0x3a, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x75, 0x6e, 0x4f, 0x75,
+	0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x70,
 	0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
 	0x28, 0x09, 0x52, 0x0c, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x55, 0x75, 0x69, 0x64,
-	0x22, 0x44, 0x0a, 0x13, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
-	0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x70, 0x6c, 0x61, 0x79, 0x67, 0x72,
-	0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06,
-	0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x3e, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d,
-	0x70, 0x69, 0x6c, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x75, 0x75,
-	0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69,
-	0x6e, 0x65, 0x55, 0x75, 0x69, 0x64, 0x22, 0x78, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d,
-	0x70, 0x69, 0x6c, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x44, 0x0a, 0x12, 0x63, 0x6f,
-	0x6d, 0x70, 0x69, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x70, 0x6c, 0x61, 0x79, 0x67, 0x72, 0x6f,
-	0x75, 0x6e, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x11, 0x63,
-	0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
-	0x22, 0x3a, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x75, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x69, 0x70, 0x65, 0x6c,
-	0x69, 0x6e, 0x65, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
-	0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x55, 0x75, 0x69, 0x64, 0x22, 0x74, 0x0a, 0x14,
-	0x47, 0x65, 0x74, 0x52, 0x75, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70,
-	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x44, 0x0a, 0x12,
-	0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74,
-	0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x70, 0x6c, 0x61, 0x79, 0x67,
-	0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52,
-	0x11, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74,
-	0x75, 0x73, 0x2a, 0x52, 0x0a, 0x03, 0x53, 0x64, 0x6b, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x44, 0x4b,
-	0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c,
-	0x0a, 0x08, 0x53, 0x44, 0x4b, 0x5f, 0x4a, 0x41, 0x56, 0x41, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06,
-	0x53, 0x44, 0x4b, 0x5f, 0x47, 0x4f, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x44, 0x4b, 0x5f,
-	0x50, 0x59, 0x54, 0x48, 0x4f, 0x4e, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x44, 0x4b, 0x5f,
-	0x53, 0x43, 0x49, 0x4f, 0x10, 0x04, 0x2a, 0x5d, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
-	0x12, 0x16, 0x0a, 0x12, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45,
-	0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x54, 0x41, 0x54,
-	0x55, 0x53, 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x13,
-	0x0a, 0x0f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x49, 0x4e, 0x49, 0x53, 0x48, 0x45,
-	0x44, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52,
-	0x52, 0x4f, 0x52, 0x10, 0x03, 0x32, 0xf1, 0x02, 0x0a, 0x11, 0x50, 0x6c, 0x61, 0x79, 0x67, 0x72,
-	0x6f, 0x75, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x48, 0x0a, 0x07, 0x52,
-	0x75, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x61, 0x79, 0x67, 0x72, 0x6f,
-	0x75, 0x6e, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x61, 0x79, 0x67, 0x72, 0x6f, 0x75,
-	0x6e, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x74,
-	0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x2e, 0x70, 0x6c, 0x61, 0x79, 0x67, 0x72, 0x6f, 0x75, 0x6e,
-	0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6c, 0x61, 0x79, 0x67, 0x72,
-	0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x74, 0x61,
-	0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0c, 0x47,
-	0x65, 0x74, 0x52, 0x75, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x22, 0x2e, 0x70, 0x6c,
-	0x61, 0x79, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52,
-	0x75, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
-	0x23, 0x2e, 0x70, 0x6c, 0x61, 0x79, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x76, 0x31, 0x2e,
-	0x47, 0x65, 0x74, 0x52, 0x75, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70,
-	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x69,
-	0x6c, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x26, 0x2e, 0x70, 0x6c, 0x61, 0x79, 0x67,
-	0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x70,
-	0x69, 0x6c, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x1a, 0x27, 0x2e, 0x70, 0x6c, 0x61, 0x79, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x76, 0x31,
-	0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75,
-	0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74,
-	0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x62,
-	0x65, 0x61, 0x6d, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x76,
-	0x31, 0x3b, 0x70, 0x6c, 0x61, 0x79, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x33,
+	0x22, 0x6d, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x52, 0x75, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70,
+	0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74,
+	0x12, 0x3d, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
+	0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x61,
+	0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x11, 0x63, 0x6f,
+	0x6d, 0x70, 0x69, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2a,
+	0x52, 0x0a, 0x03, 0x53, 0x64, 0x6b, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x44, 0x4b, 0x5f, 0x55, 0x4e,
+	0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x53,
+	0x44, 0x4b, 0x5f, 0x4a, 0x41, 0x56, 0x41, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x44, 0x4b,
+	0x5f, 0x47, 0x4f, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x44, 0x4b, 0x5f, 0x50, 0x59, 0x54,
+	0x48, 0x4f, 0x4e, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x44, 0x4b, 0x5f, 0x53, 0x43, 0x49,
+	0x4f, 0x10, 0x04, 0x2a, 0x5d, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a,
+	0x12, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46,
+	0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f,
+	0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x53,
+	0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x49, 0x4e, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x02,
+	0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52,
+	0x10, 0x03, 0x32, 0xb9, 0x02, 0x0a, 0x11, 0x50, 0x6c, 0x61, 0x79, 0x67, 0x72, 0x6f, 0x75, 0x6e,
+	0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x52, 0x75, 0x6e, 0x43,
+	0x6f, 0x64, 0x65, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e,
+	0x43, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x61, 0x70,
+	0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x12, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65,
+	0x63, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+	0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x74,
+	0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0c,
+	0x47, 0x65, 0x74, 0x52, 0x75, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x1b, 0x2e, 0x61,
+	0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x75, 0x6e, 0x4f, 0x75, 0x74, 0x70,
+	0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e,
+	0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x75, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52,
+	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f,
+	0x6d, 0x70, 0x69, 0x6c, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x1f, 0x2e, 0x61, 0x70,
+	0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x4f,
+	0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61,
+	0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65,
+	0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x38,
+	0x5a, 0x36, 0x62, 0x65, 0x61, 0x6d, 0x2e, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x6f, 0x72,
+	0x67, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x62, 0x61, 0x63,
+	0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x3b, 0x70, 0x6c,
+	0x61, 0x79, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
-	file_playground_proto_rawDescOnce sync.Once
-	file_playground_proto_rawDescData = file_playground_proto_rawDesc
+	file_api_v1_api_proto_rawDescOnce sync.Once
+	file_api_v1_api_proto_rawDescData = file_api_v1_api_proto_rawDesc
 )
 
-func file_playground_proto_rawDescGZIP() []byte {
-	file_playground_proto_rawDescOnce.Do(func() {
-		file_playground_proto_rawDescData = protoimpl.X.CompressGZIP(file_playground_proto_rawDescData)
+func file_api_v1_api_proto_rawDescGZIP() []byte {
+	file_api_v1_api_proto_rawDescOnce.Do(func() {
+		file_api_v1_api_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_v1_api_proto_rawDescData)
 	})
-	return file_playground_proto_rawDescData
-}
-
-var file_playground_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
-var file_playground_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
-var file_playground_proto_goTypes = []interface{}{
-	(Sdk)(0),                         // 0: playground.v1.Sdk
-	(Status)(0),                      // 1: playground.v1.Status
-	(*RunCodeRequest)(nil),           // 2: playground.v1.RunCodeRequest
-	(*RunCodeResponse)(nil),          // 3: playground.v1.RunCodeResponse
-	(*CheckStatusRequest)(nil),       // 4: playground.v1.CheckStatusRequest
-	(*CheckStatusResponse)(nil),      // 5: playground.v1.CheckStatusResponse
-	(*GetCompileOutputRequest)(nil),  // 6: playground.v1.GetCompileOutputRequest
-	(*GetCompileOutputResponse)(nil), // 7: playground.v1.GetCompileOutputResponse
-	(*GetRunOutputRequest)(nil),      // 8: playground.v1.GetRunOutputRequest
-	(*GetRunOutputResponse)(nil),     // 9: playground.v1.GetRunOutputResponse
-}
-var file_playground_proto_depIdxs = []int32{
-	0, // 0: playground.v1.RunCodeRequest.sdk:type_name -> playground.v1.Sdk
-	1, // 1: playground.v1.CheckStatusResponse.status:type_name -> playground.v1.Status
-	1, // 2: playground.v1.GetCompileOutputResponse.compilation_status:type_name -> playground.v1.Status
-	1, // 3: playground.v1.GetRunOutputResponse.compilation_status:type_name -> playground.v1.Status
-	2, // 4: playground.v1.PlaygroundService.RunCode:input_type -> playground.v1.RunCodeRequest
-	4, // 5: playground.v1.PlaygroundService.CheckStatus:input_type -> playground.v1.CheckStatusRequest
-	8, // 6: playground.v1.PlaygroundService.GetRunOutput:input_type -> playground.v1.GetRunOutputRequest
-	6, // 7: playground.v1.PlaygroundService.GetCompileOutput:input_type -> playground.v1.GetCompileOutputRequest
-	3, // 8: playground.v1.PlaygroundService.RunCode:output_type -> playground.v1.RunCodeResponse
-	5, // 9: playground.v1.PlaygroundService.CheckStatus:output_type -> playground.v1.CheckStatusResponse
-	9, // 10: playground.v1.PlaygroundService.GetRunOutput:output_type -> playground.v1.GetRunOutputResponse
-	7, // 11: playground.v1.PlaygroundService.GetCompileOutput:output_type -> playground.v1.GetCompileOutputResponse
+	return file_api_v1_api_proto_rawDescData
+}
+
+var file_api_v1_api_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
+var file_api_v1_api_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
+var file_api_v1_api_proto_goTypes = []interface{}{
+	(Sdk)(0),                         // 0: api.v1.Sdk
+	(Status)(0),                      // 1: api.v1.Status
+	(*RunCodeRequest)(nil),           // 2: api.v1.RunCodeRequest
+	(*RunCodeResponse)(nil),          // 3: api.v1.RunCodeResponse
+	(*CheckStatusRequest)(nil),       // 4: api.v1.CheckStatusRequest
+	(*CheckStatusResponse)(nil),      // 5: api.v1.CheckStatusResponse
+	(*GetCompileOutputRequest)(nil),  // 6: api.v1.GetCompileOutputRequest
+	(*GetCompileOutputResponse)(nil), // 7: api.v1.GetCompileOutputResponse
+	(*GetRunOutputRequest)(nil),      // 8: api.v1.GetRunOutputRequest
+	(*GetRunOutputResponse)(nil),     // 9: api.v1.GetRunOutputResponse
+}
+var file_api_v1_api_proto_depIdxs = []int32{
+	0, // 0: api.v1.RunCodeRequest.sdk:type_name -> api.v1.Sdk
+	1, // 1: api.v1.CheckStatusResponse.status:type_name -> api.v1.Status
+	1, // 2: api.v1.GetCompileOutputResponse.compilation_status:type_name -> api.v1.Status
+	1, // 3: api.v1.GetRunOutputResponse.compilation_status:type_name -> api.v1.Status
+	2, // 4: api.v1.PlaygroundService.RunCode:input_type -> api.v1.RunCodeRequest
+	4, // 5: api.v1.PlaygroundService.CheckStatus:input_type -> api.v1.CheckStatusRequest
+	8, // 6: api.v1.PlaygroundService.GetRunOutput:input_type -> api.v1.GetRunOutputRequest
+	6, // 7: api.v1.PlaygroundService.GetCompileOutput:input_type -> api.v1.GetCompileOutputRequest
+	3, // 8: api.v1.PlaygroundService.RunCode:output_type -> api.v1.RunCodeResponse
+	5, // 9: api.v1.PlaygroundService.CheckStatus:output_type -> api.v1.CheckStatusResponse
+	9, // 10: api.v1.PlaygroundService.GetRunOutput:output_type -> api.v1.GetRunOutputResponse
+	7, // 11: api.v1.PlaygroundService.GetCompileOutput:output_type -> api.v1.GetCompileOutputResponse
 	8, // [8:12] is the sub-list for method output_type
 	4, // [4:8] is the sub-list for method input_type
 	4, // [4:4] is the sub-list for extension type_name
@@ -683,13 +677,13 @@ var file_playground_proto_depIdxs = []int32{
 	0, // [0:4] is the sub-list for field type_name
 }
 
-func init() { file_playground_proto_init() }
-func file_playground_proto_init() {
-	if File_playground_proto != nil {
+func init() { file_api_v1_api_proto_init() }
+func file_api_v1_api_proto_init() {
+	if File_api_v1_api_proto != nil {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_playground_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_api_v1_api_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*RunCodeRequest); i {
 			case 0:
 				return &v.state
@@ -701,7 +695,7 @@ func file_playground_proto_init() {
 				return nil
 			}
 		}
-		file_playground_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_api_v1_api_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*RunCodeResponse); i {
 			case 0:
 				return &v.state
@@ -713,7 +707,7 @@ func file_playground_proto_init() {
 				return nil
 			}
 		}
-		file_playground_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_api_v1_api_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*CheckStatusRequest); i {
 			case 0:
 				return &v.state
@@ -725,7 +719,7 @@ func file_playground_proto_init() {
 				return nil
 			}
 		}
-		file_playground_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_api_v1_api_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*CheckStatusResponse); i {
 			case 0:
 				return &v.state
@@ -737,7 +731,7 @@ func file_playground_proto_init() {
 				return nil
 			}
 		}
-		file_playground_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_api_v1_api_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*GetCompileOutputRequest); i {
 			case 0:
 				return &v.state
@@ -749,7 +743,7 @@ func file_playground_proto_init() {
 				return nil
 			}
 		}
-		file_playground_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_api_v1_api_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*GetCompileOutputResponse); i {
 			case 0:
 				return &v.state
@@ -761,7 +755,7 @@ func file_playground_proto_init() {
 				return nil
 			}
 		}
-		file_playground_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_api_v1_api_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*GetRunOutputRequest); i {
 			case 0:
 				return &v.state
@@ -773,7 +767,7 @@ func file_playground_proto_init() {
 				return nil
 			}
 		}
-		file_playground_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_api_v1_api_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*GetRunOutputResponse); i {
 			case 0:
 				return &v.state
@@ -790,19 +784,19 @@ func file_playground_proto_init() {
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
-			RawDescriptor: file_playground_proto_rawDesc,
+			RawDescriptor: file_api_v1_api_proto_rawDesc,
 			NumEnums:      2,
 			NumMessages:   8,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
-		GoTypes:           file_playground_proto_goTypes,
-		DependencyIndexes: file_playground_proto_depIdxs,
-		EnumInfos:         file_playground_proto_enumTypes,
-		MessageInfos:      file_playground_proto_msgTypes,
+		GoTypes:           file_api_v1_api_proto_goTypes,
+		DependencyIndexes: file_api_v1_api_proto_depIdxs,
+		EnumInfos:         file_api_v1_api_proto_enumTypes,
+		MessageInfos:      file_api_v1_api_proto_msgTypes,
 	}.Build()
-	File_playground_proto = out.File
-	file_playground_proto_rawDesc = nil
-	file_playground_proto_goTypes = nil
-	file_playground_proto_depIdxs = nil
+	File_api_v1_api_proto = out.File
+	file_api_v1_api_proto_rawDesc = nil
+	file_api_v1_api_proto_goTypes = nil
+	file_api_v1_api_proto_depIdxs = nil
 }
diff --git a/playground/backend/internal/api/playground_grpc.pb.go b/playground/backend/internal/api/v1/api_grpc.pb.go
similarity index 89%
rename from playground/backend/internal/api/playground_grpc.pb.go
rename to playground/backend/internal/api/v1/api_grpc.pb.go
index f8e11ca..63c0748 100644
--- a/playground/backend/internal/api/playground_grpc.pb.go
+++ b/playground/backend/internal/api/v1/api_grpc.pb.go
@@ -12,7 +12,6 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 
 package playground
@@ -53,7 +52,7 @@ func NewPlaygroundServiceClient(cc grpc.ClientConnInterface) PlaygroundServiceCl
 
 func (c *playgroundServiceClient) RunCode(ctx context.Context, in *RunCodeRequest, opts ...grpc.CallOption) (*RunCodeResponse, error) {
 	out := new(RunCodeResponse)
-	err := c.cc.Invoke(ctx, "/playground.v1.PlaygroundService/RunCode", in, out, opts...)
+	err := c.cc.Invoke(ctx, "/api.v1.PlaygroundService/RunCode", in, out, opts...)
 	if err != nil {
 		return nil, err
 	}
@@ -62,7 +61,7 @@ func (c *playgroundServiceClient) RunCode(ctx context.Context, in *RunCodeReques
 
 func (c *playgroundServiceClient) CheckStatus(ctx context.Context, in *CheckStatusRequest, opts ...grpc.CallOption) (*CheckStatusResponse, error) {
 	out := new(CheckStatusResponse)
-	err := c.cc.Invoke(ctx, "/playground.v1.PlaygroundService/CheckStatus", in, out, opts...)
+	err := c.cc.Invoke(ctx, "/api.v1.PlaygroundService/CheckStatus", in, out, opts...)
 	if err != nil {
 		return nil, err
 	}
@@ -71,7 +70,7 @@ func (c *playgroundServiceClient) CheckStatus(ctx context.Context, in *CheckStat
 
 func (c *playgroundServiceClient) GetRunOutput(ctx context.Context, in *GetRunOutputRequest, opts ...grpc.CallOption) (*GetRunOutputResponse, error) {
 	out := new(GetRunOutputResponse)
-	err := c.cc.Invoke(ctx, "/playground.v1.PlaygroundService/GetRunOutput", in, out, opts...)
+	err := c.cc.Invoke(ctx, "/api.v1.PlaygroundService/GetRunOutput", in, out, opts...)
 	if err != nil {
 		return nil, err
 	}
@@ -80,7 +79,7 @@ func (c *playgroundServiceClient) GetRunOutput(ctx context.Context, in *GetRunOu
 
 func (c *playgroundServiceClient) GetCompileOutput(ctx context.Context, in *GetCompileOutputRequest, opts ...grpc.CallOption) (*GetCompileOutputResponse, error) {
 	out := new(GetCompileOutputResponse)
-	err := c.cc.Invoke(ctx, "/playground.v1.PlaygroundService/GetCompileOutput", in, out, opts...)
+	err := c.cc.Invoke(ctx, "/api.v1.PlaygroundService/GetCompileOutput", in, out, opts...)
 	if err != nil {
 		return nil, err
 	}
@@ -88,7 +87,7 @@ func (c *playgroundServiceClient) GetCompileOutput(ctx context.Context, in *GetC
 }
 
 // PlaygroundServiceServer is the server API for PlaygroundService service.
-// All implementations must embed UnimplementedPlaygroundServiceServer
+// All implementations should embed UnimplementedPlaygroundServiceServer
 // for forward compatibility
 type PlaygroundServiceServer interface {
 	// Submit the job for an execution and get the pipeline uuid.
@@ -99,10 +98,9 @@ type PlaygroundServiceServer interface {
 	GetRunOutput(context.Context, *GetRunOutputRequest) (*GetRunOutputResponse, error)
 	// Get the result of pipeline compilation.
 	GetCompileOutput(context.Context, *GetCompileOutputRequest) (*GetCompileOutputResponse, error)
-	mustEmbedUnimplementedPlaygroundServiceServer()
 }
 
-// UnimplementedPlaygroundServiceServer must be embedded to have forward compatible implementations.
+// UnimplementedPlaygroundServiceServer should be embedded to have forward compatible implementations.
 type UnimplementedPlaygroundServiceServer struct {
 }
 
@@ -118,7 +116,6 @@ func (UnimplementedPlaygroundServiceServer) GetRunOutput(context.Context, *GetRu
 func (UnimplementedPlaygroundServiceServer) GetCompileOutput(context.Context, *GetCompileOutputRequest) (*GetCompileOutputResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method GetCompileOutput not implemented")
 }
-func (UnimplementedPlaygroundServiceServer) mustEmbedUnimplementedPlaygroundServiceServer() {}
 
 // UnsafePlaygroundServiceServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to PlaygroundServiceServer will
@@ -141,7 +138,7 @@ func _PlaygroundService_RunCode_Handler(srv interface{}, ctx context.Context, de
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: "/playground.v1.PlaygroundService/RunCode",
+		FullMethod: "/api.v1.PlaygroundService/RunCode",
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
 		return srv.(PlaygroundServiceServer).RunCode(ctx, req.(*RunCodeRequest))
@@ -159,7 +156,7 @@ func _PlaygroundService_CheckStatus_Handler(srv interface{}, ctx context.Context
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: "/playground.v1.PlaygroundService/CheckStatus",
+		FullMethod: "/api.v1.PlaygroundService/CheckStatus",
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
 		return srv.(PlaygroundServiceServer).CheckStatus(ctx, req.(*CheckStatusRequest))
@@ -177,7 +174,7 @@ func _PlaygroundService_GetRunOutput_Handler(srv interface{}, ctx context.Contex
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: "/playground.v1.PlaygroundService/GetRunOutput",
+		FullMethod: "/api.v1.PlaygroundService/GetRunOutput",
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
 		return srv.(PlaygroundServiceServer).GetRunOutput(ctx, req.(*GetRunOutputRequest))
@@ -195,7 +192,7 @@ func _PlaygroundService_GetCompileOutput_Handler(srv interface{}, ctx context.Co
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: "/playground.v1.PlaygroundService/GetCompileOutput",
+		FullMethod: "/api.v1.PlaygroundService/GetCompileOutput",
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
 		return srv.(PlaygroundServiceServer).GetCompileOutput(ctx, req.(*GetCompileOutputRequest))
@@ -207,7 +204,7 @@ func _PlaygroundService_GetCompileOutput_Handler(srv interface{}, ctx context.Co
 // It's only intended for direct use with grpc.RegisterService,
 // and not to be introspected or modified (even as a copy)
 var PlaygroundService_ServiceDesc = grpc.ServiceDesc{
-	ServiceName: "playground.v1.PlaygroundService",
+	ServiceName: "api.v1.PlaygroundService",
 	HandlerType: (*PlaygroundServiceServer)(nil),
 	Methods: []grpc.MethodDesc{
 		{
@@ -228,5 +225,5 @@ var PlaygroundService_ServiceDesc = grpc.ServiceDesc{
 		},
 	},
 	Streams:  []grpc.StreamDesc{},
-	Metadata: "playground.proto",
+	Metadata: "api/v1/api.proto",
 }
diff --git a/playground/backend/internal/executors/go_helper.go b/playground/backend/internal/environment/environment.go
similarity index 71%
copy from playground/backend/internal/executors/go_helper.go
copy to playground/backend/internal/environment/environment.go
index a9d72bb..910dec9 100644
--- a/playground/backend/internal/executors/go_helper.go
+++ b/playground/backend/internal/environment/environment.go
@@ -13,15 +13,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Package executors
-package executors
+// TODO: remove this code when merging https://github.com/apache/beam/pull/15654
 
-// NewGoExecutor creates an executor with Go specifics
-func NewGoExecutor(fs *fs_tool.LifeCycle, javaValidators *[]validatorWithArgs) *Executor {
-	return nil
+package environment
+
+type ServerEnvs struct {}
+
+func (envs ServerEnvs) Address() string {
+	return ""
 }
 
-// GetGoValidators return validators methods that needed for Go file
-func GetGoValidators() *[]validatorWithArgs {
-	return nil
+func NewEnvironment() ServerEnvs {
+	return ServerEnvs{}
 }
diff --git a/playground/backend/internal/executors/executor.go b/playground/backend/internal/executors/executor.go
index a5e258d..b85641b 100644
--- a/playground/backend/internal/executors/executor.go
+++ b/playground/backend/internal/executors/executor.go
@@ -17,7 +17,7 @@
 package executors
 
 import (
-	pb "beam.apache.org/playground/backend/internal/api"
+	pb "beam.apache.org/playground/backend/internal/api/v1"
 	"beam.apache.org/playground/backend/internal/fs_tool"
 	"fmt"
 	"os/exec"
diff --git a/playground/backend/internal/executors/go_helper.go b/playground/backend/internal/executors/go_helper.go
index a9d72bb..6772145 100644
--- a/playground/backend/internal/executors/go_helper.go
+++ b/playground/backend/internal/executors/go_helper.go
@@ -16,6 +16,8 @@
 // Package executors
 package executors
 
+import "beam.apache.org/playground/backend/internal/fs_tool"
+
 // NewGoExecutor creates an executor with Go specifics
 func NewGoExecutor(fs *fs_tool.LifeCycle, javaValidators *[]validatorWithArgs) *Executor {
 	return nil
diff --git a/playground/backend/internal/executors/java_helper_test.go b/playground/backend/internal/executors/java_helper_test.go
index 332717a..19b3934 100644
--- a/playground/backend/internal/executors/java_helper_test.go
+++ b/playground/backend/internal/executors/java_helper_test.go
@@ -16,7 +16,7 @@
 package executors
 
 import (
-	pb "beam.apache.org/playground/backend/internal/api"
+	pb "beam.apache.org/playground/backend/internal/api/v1"
 	"beam.apache.org/playground/backend/internal/fs_tool"
 	"github.com/google/uuid"
 	"testing"
diff --git a/playground/backend/internal/fs_tool/fs.go b/playground/backend/internal/fs_tool/fs.go
index 481fade..7b99fd9 100644
--- a/playground/backend/internal/fs_tool/fs.go
+++ b/playground/backend/internal/fs_tool/fs.go
@@ -16,7 +16,7 @@
 package fs_tool
 
 import (
-	pb "beam.apache.org/playground/backend/internal/api"
+	pb "beam.apache.org/playground/backend/internal/api/v1"
 	"errors"
 	"fmt"
 	"github.com/google/uuid"
diff --git a/playground/backend/internal/fs_tool/fs_test.go b/playground/backend/internal/fs_tool/fs_test.go
index 1b1b304..c1b00d2 100644
--- a/playground/backend/internal/fs_tool/fs_test.go
+++ b/playground/backend/internal/fs_tool/fs_test.go
@@ -16,7 +16,7 @@
 package fs_tool
 
 import (
-	pb "beam.apache.org/playground/backend/internal/api"
+	pb "beam.apache.org/playground/backend/internal/api/v1"
 	"fmt"
 	"github.com/google/uuid"
 	"io/fs"
diff --git a/playground/buf.gen.yaml b/playground/buf.gen.yaml
new file mode 100644
index 0000000..bd9b9f3d
--- /dev/null
+++ b/playground/buf.gen.yaml
@@ -0,0 +1,32 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+version: v1
+plugins:
+- name: go
+  out: backend/internal
+  opt: paths=source_relative
+- name: go-grpc
+  out: backend/internal
+  opt:
+  - paths=source_relative
+  - require_unimplemented_servers=false
+- name: dart
+  out: frontend/lib
+  opt: grpc
\ No newline at end of file
diff --git a/playground/build.gradle.kts b/playground/build.gradle.kts
new file mode 100644
index 0000000..b511c48
--- /dev/null
+++ b/playground/build.gradle.kts
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * License); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+description = "Apache Beam :: Playground"
+
+// generate protobuf client and server stubs
+task("generateProto") {
+  group = "build"
+  doLast {
+    exec {
+      executable("buf")
+      args("generate")
+    }
+  }
+}
diff --git a/playground/frontend/README.md b/playground/frontend/README.md
index acdea1d..7c3b489 100644
--- a/playground/frontend/README.md
+++ b/playground/frontend/README.md
@@ -26,11 +26,7 @@ It provides a portable API layer for building sophisticated data-parallel proces
 
 ## Getting Started
 
-Website development requires [Flutter](https://flutter.dev/docs/get-started/install) installed.
-
-Create /lib/generated folder and run the next command to generate grpc files from proto:
-
-`$ protoc playground.proto --dart_out=grpc:lib/generated --proto_path=$(pwd)/../playground/v1`
+See [playground/README.md](../README.md) for details on requirements and setup.
 
 The following command is used to build and serve the website locally:
 
diff --git a/playground/frontend/analysis_options.yaml b/playground/frontend/analysis_options.yaml
index 38742fe..41ffd37 100644
--- a/playground/frontend/analysis_options.yaml
+++ b/playground/frontend/analysis_options.yaml
@@ -26,6 +26,10 @@
 # packages, and plugins designed to encourage good coding practices.
 include: package:flutter_lints/flutter.yaml
 
+# exclude generated files
+analyzer:
+  exclude: [lib/api/**, test/**.mocks.dart, lib/**.g.dart]
+
 linter:
   # The lint rules applied to this project can be customized in the
   # section below to disable rules from the `package:flutter_lints/flutter.yaml`
diff --git a/playground/frontend/build.gradle.kts b/playground/frontend/build.gradle.kts
new file mode 100644
index 0000000..5533526
--- /dev/null
+++ b/playground/frontend/build.gradle.kts
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * License); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+description = "Apache Beam :: Playground :: Frontend"
+
+task("analyze") {
+  group = "verification"
+  description = "Analyze dart code"
+  doLast {
+    exec {
+      executable("dart")
+      args("analyze", ".")
+    }
+  }
+}
+
+task("pubGet") {
+  group = "build"
+  description = "Get packages for the playground frontend project"
+  doLast {
+    exec {
+      executable("flutter")
+      args("pub", "get")
+    }
+  }
+}
+
+task("format") {
+  group = "build"
+  description = "Idiomatically format Dart source code"
+  doLast {
+    exec {
+      executable("dart")
+      args("format", ".")
+    }
+  }
+}
+
+task("run") {
+  group = "application"
+  description = "Run application on Google Chrome"
+  doLast {
+    exec {
+      executable("flutter")
+      args("run", "-d", "chrome")
+    }
+  }
+}
+
+task("test") {
+  group = "verification"
+  description = "flutter test"
+  doLast {
+    exec {
+      executable("flutter")
+      args("test")
+    }
+  }
+}
\ No newline at end of file
diff --git a/playground/frontend/lib/api/v1/api.pb.dart b/playground/frontend/lib/api/v1/api.pb.dart
new file mode 100644
index 0000000..9c503e4
--- /dev/null
+++ b/playground/frontend/lib/api/v1/api.pb.dart
@@ -0,0 +1,450 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///
+//  Generated code. Do not modify.
+//  source: api/v1/api.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+import 'api.pbenum.dart';
+
+export 'api.pbenum.dart';
+
+class RunCodeRequest extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RunCodeRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'api.v1'), createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'code')
+    ..e<Sdk>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'sdk', $pb.PbFieldType.OE, defaultOrMaker: Sdk.SDK_UNSPECIFIED, valueOf: Sdk.valueOf, enumValues: Sdk.values)
+    ..hasRequiredFields = false
+  ;
+
+  RunCodeRequest._() : super();
+  factory RunCodeRequest({
+    $core.String? code,
+    Sdk? sdk,
+  }) {
+    final _result = create();
+    if (code != null) {
+      _result.code = code;
+    }
+    if (sdk != null) {
+      _result.sdk = sdk;
+    }
+    return _result;
+  }
+  factory RunCodeRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory RunCodeRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  RunCodeRequest clone() => RunCodeRequest()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  RunCodeRequest copyWith(void Function(RunCodeRequest) updates) => super.copyWith((message) => updates(message as RunCodeRequest)) as RunCodeRequest; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static RunCodeRequest create() => RunCodeRequest._();
+  RunCodeRequest createEmptyInstance() => create();
+  static $pb.PbList<RunCodeRequest> createRepeated() => $pb.PbList<RunCodeRequest>();
+  @$core.pragma('dart2js:noInline')
+  static RunCodeRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RunCodeRequest>(create);
+  static RunCodeRequest? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get code => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set code($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasCode() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearCode() => clearField(1);
+
+  @$pb.TagNumber(2)
+  Sdk get sdk => $_getN(1);
+  @$pb.TagNumber(2)
+  set sdk(Sdk v) { setField(2, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasSdk() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearSdk() => clearField(2);
+}
+
+class RunCodeResponse extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RunCodeResponse', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'api.v1'), createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'pipelineUuid')
+    ..hasRequiredFields = false
+  ;
+
+  RunCodeResponse._() : super();
+  factory RunCodeResponse({
+    $core.String? pipelineUuid,
+  }) {
+    final _result = create();
+    if (pipelineUuid != null) {
+      _result.pipelineUuid = pipelineUuid;
+    }
+    return _result;
+  }
+  factory RunCodeResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory RunCodeResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  RunCodeResponse clone() => RunCodeResponse()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  RunCodeResponse copyWith(void Function(RunCodeResponse) updates) => super.copyWith((message) => updates(message as RunCodeResponse)) as RunCodeResponse; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static RunCodeResponse create() => RunCodeResponse._();
+  RunCodeResponse createEmptyInstance() => create();
+  static $pb.PbList<RunCodeResponse> createRepeated() => $pb.PbList<RunCodeResponse>();
+  @$core.pragma('dart2js:noInline')
+  static RunCodeResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RunCodeResponse>(create);
+  static RunCodeResponse? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get pipelineUuid => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set pipelineUuid($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasPipelineUuid() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearPipelineUuid() => clearField(1);
+}
+
+class CheckStatusRequest extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CheckStatusRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'api.v1'), createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'pipelineUuid')
+    ..hasRequiredFields = false
+  ;
+
+  CheckStatusRequest._() : super();
+  factory CheckStatusRequest({
+    $core.String? pipelineUuid,
+  }) {
+    final _result = create();
+    if (pipelineUuid != null) {
+      _result.pipelineUuid = pipelineUuid;
+    }
+    return _result;
+  }
+  factory CheckStatusRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory CheckStatusRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  CheckStatusRequest clone() => CheckStatusRequest()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  CheckStatusRequest copyWith(void Function(CheckStatusRequest) updates) => super.copyWith((message) => updates(message as CheckStatusRequest)) as CheckStatusRequest; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static CheckStatusRequest create() => CheckStatusRequest._();
+  CheckStatusRequest createEmptyInstance() => create();
+  static $pb.PbList<CheckStatusRequest> createRepeated() => $pb.PbList<CheckStatusRequest>();
+  @$core.pragma('dart2js:noInline')
+  static CheckStatusRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CheckStatusRequest>(create);
+  static CheckStatusRequest? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get pipelineUuid => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set pipelineUuid($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasPipelineUuid() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearPipelineUuid() => clearField(1);
+}
+
+class CheckStatusResponse extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CheckStatusResponse', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'api.v1'), createEmptyInstance: create)
+    ..e<Status>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'status', $pb.PbFieldType.OE, defaultOrMaker: Status.STATUS_UNSPECIFIED, valueOf: Status.valueOf, enumValues: Status.values)
+    ..hasRequiredFields = false
+  ;
+
+  CheckStatusResponse._() : super();
+  factory CheckStatusResponse({
+    Status? status,
+  }) {
+    final _result = create();
+    if (status != null) {
+      _result.status = status;
+    }
+    return _result;
+  }
+  factory CheckStatusResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory CheckStatusResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  CheckStatusResponse clone() => CheckStatusResponse()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  CheckStatusResponse copyWith(void Function(CheckStatusResponse) updates) => super.copyWith((message) => updates(message as CheckStatusResponse)) as CheckStatusResponse; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static CheckStatusResponse create() => CheckStatusResponse._();
+  CheckStatusResponse createEmptyInstance() => create();
+  static $pb.PbList<CheckStatusResponse> createRepeated() => $pb.PbList<CheckStatusResponse>();
+  @$core.pragma('dart2js:noInline')
+  static CheckStatusResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CheckStatusResponse>(create);
+  static CheckStatusResponse? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  Status get status => $_getN(0);
+  @$pb.TagNumber(1)
+  set status(Status v) { setField(1, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasStatus() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearStatus() => clearField(1);
+}
+
+class GetCompileOutputRequest extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GetCompileOutputRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'api.v1'), createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'pipelineUuid')
+    ..hasRequiredFields = false
+  ;
+
+  GetCompileOutputRequest._() : super();
+  factory GetCompileOutputRequest({
+    $core.String? pipelineUuid,
+  }) {
+    final _result = create();
+    if (pipelineUuid != null) {
+      _result.pipelineUuid = pipelineUuid;
+    }
+    return _result;
+  }
+  factory GetCompileOutputRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory GetCompileOutputRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  GetCompileOutputRequest clone() => GetCompileOutputRequest()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  GetCompileOutputRequest copyWith(void Function(GetCompileOutputRequest) updates) => super.copyWith((message) => updates(message as GetCompileOutputRequest)) as GetCompileOutputRequest; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static GetCompileOutputRequest create() => GetCompileOutputRequest._();
+  GetCompileOutputRequest createEmptyInstance() => create();
+  static $pb.PbList<GetCompileOutputRequest> createRepeated() => $pb.PbList<GetCompileOutputRequest>();
+  @$core.pragma('dart2js:noInline')
+  static GetCompileOutputRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GetCompileOutputRequest>(create);
+  static GetCompileOutputRequest? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get pipelineUuid => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set pipelineUuid($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasPipelineUuid() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearPipelineUuid() => clearField(1);
+}
+
+class GetCompileOutputResponse extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GetCompileOutputResponse', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'api.v1'), createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'output')
+    ..e<Status>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'compilationStatus', $pb.PbFieldType.OE, defaultOrMaker: Status.STATUS_UNSPECIFIED, valueOf: Status.valueOf, enumValues: Status.values)
+    ..hasRequiredFields = false
+  ;
+
+  GetCompileOutputResponse._() : super();
+  factory GetCompileOutputResponse({
+    $core.String? output,
+    Status? compilationStatus,
+  }) {
+    final _result = create();
+    if (output != null) {
+      _result.output = output;
+    }
+    if (compilationStatus != null) {
+      _result.compilationStatus = compilationStatus;
+    }
+    return _result;
+  }
+  factory GetCompileOutputResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory GetCompileOutputResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  GetCompileOutputResponse clone() => GetCompileOutputResponse()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  GetCompileOutputResponse copyWith(void Function(GetCompileOutputResponse) updates) => super.copyWith((message) => updates(message as GetCompileOutputResponse)) as GetCompileOutputResponse; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static GetCompileOutputResponse create() => GetCompileOutputResponse._();
+  GetCompileOutputResponse createEmptyInstance() => create();
+  static $pb.PbList<GetCompileOutputResponse> createRepeated() => $pb.PbList<GetCompileOutputResponse>();
+  @$core.pragma('dart2js:noInline')
+  static GetCompileOutputResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GetCompileOutputResponse>(create);
+  static GetCompileOutputResponse? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get output => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set output($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasOutput() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearOutput() => clearField(1);
+
+  @$pb.TagNumber(2)
+  Status get compilationStatus => $_getN(1);
+  @$pb.TagNumber(2)
+  set compilationStatus(Status v) { setField(2, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasCompilationStatus() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearCompilationStatus() => clearField(2);
+}
+
+class GetRunOutputRequest extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GetRunOutputRequest', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'api.v1'), createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'pipelineUuid')
+    ..hasRequiredFields = false
+  ;
+
+  GetRunOutputRequest._() : super();
+  factory GetRunOutputRequest({
+    $core.String? pipelineUuid,
+  }) {
+    final _result = create();
+    if (pipelineUuid != null) {
+      _result.pipelineUuid = pipelineUuid;
+    }
+    return _result;
+  }
+  factory GetRunOutputRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory GetRunOutputRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  GetRunOutputRequest clone() => GetRunOutputRequest()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  GetRunOutputRequest copyWith(void Function(GetRunOutputRequest) updates) => super.copyWith((message) => updates(message as GetRunOutputRequest)) as GetRunOutputRequest; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static GetRunOutputRequest create() => GetRunOutputRequest._();
+  GetRunOutputRequest createEmptyInstance() => create();
+  static $pb.PbList<GetRunOutputRequest> createRepeated() => $pb.PbList<GetRunOutputRequest>();
+  @$core.pragma('dart2js:noInline')
+  static GetRunOutputRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GetRunOutputRequest>(create);
+  static GetRunOutputRequest? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get pipelineUuid => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set pipelineUuid($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasPipelineUuid() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearPipelineUuid() => clearField(1);
+}
+
+class GetRunOutputResponse extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GetRunOutputResponse', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'api.v1'), createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'output')
+    ..e<Status>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'compilationStatus', $pb.PbFieldType.OE, defaultOrMaker: Status.STATUS_UNSPECIFIED, valueOf: Status.valueOf, enumValues: Status.values)
+    ..hasRequiredFields = false
+  ;
+
+  GetRunOutputResponse._() : super();
+  factory GetRunOutputResponse({
+    $core.String? output,
+    Status? compilationStatus,
+  }) {
+    final _result = create();
+    if (output != null) {
+      _result.output = output;
+    }
+    if (compilationStatus != null) {
+      _result.compilationStatus = compilationStatus;
+    }
+    return _result;
+  }
+  factory GetRunOutputResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory GetRunOutputResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  GetRunOutputResponse clone() => GetRunOutputResponse()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  GetRunOutputResponse copyWith(void Function(GetRunOutputResponse) updates) => super.copyWith((message) => updates(message as GetRunOutputResponse)) as GetRunOutputResponse; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static GetRunOutputResponse create() => GetRunOutputResponse._();
+  GetRunOutputResponse createEmptyInstance() => create();
+  static $pb.PbList<GetRunOutputResponse> createRepeated() => $pb.PbList<GetRunOutputResponse>();
+  @$core.pragma('dart2js:noInline')
+  static GetRunOutputResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GetRunOutputResponse>(create);
+  static GetRunOutputResponse? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get output => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set output($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasOutput() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearOutput() => clearField(1);
+
+  @$pb.TagNumber(2)
+  Status get compilationStatus => $_getN(1);
+  @$pb.TagNumber(2)
+  set compilationStatus(Status v) { setField(2, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasCompilationStatus() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearCompilationStatus() => clearField(2);
+}
+
diff --git a/playground/frontend/lib/api/v1/api.pbenum.dart b/playground/frontend/lib/api/v1/api.pbenum.dart
new file mode 100644
index 0000000..e1cf849
--- /dev/null
+++ b/playground/frontend/lib/api/v1/api.pbenum.dart
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///
+//  Generated code. Do not modify.
+//  source: api/v1/api.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+// ignore_for_file: UNDEFINED_SHOWN_NAME
+import 'dart:core' as $core;
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class Sdk extends $pb.ProtobufEnum {
+  static const Sdk SDK_UNSPECIFIED = Sdk._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SDK_UNSPECIFIED');
+  static const Sdk SDK_JAVA = Sdk._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SDK_JAVA');
+  static const Sdk SDK_GO = Sdk._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SDK_GO');
+  static const Sdk SDK_PYTHON = Sdk._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SDK_PYTHON');
+  static const Sdk SDK_SCIO = Sdk._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SDK_SCIO');
+
+  static const $core.List<Sdk> values = <Sdk> [
+    SDK_UNSPECIFIED,
+    SDK_JAVA,
+    SDK_GO,
+    SDK_PYTHON,
+    SDK_SCIO,
+  ];
+
+  static final $core.Map<$core.int, Sdk> _byValue = $pb.ProtobufEnum.initByValue(values);
+  static Sdk? valueOf($core.int value) => _byValue[value];
+
+  const Sdk._($core.int v, $core.String n) : super(v, n);
+}
+
+class Status extends $pb.ProtobufEnum {
+  static const Status STATUS_UNSPECIFIED = Status._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'STATUS_UNSPECIFIED');
+  static const Status STATUS_EXECUTING = Status._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'STATUS_EXECUTING');
+  static const Status STATUS_FINISHED = Status._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'STATUS_FINISHED');
+  static const Status STATUS_ERROR = Status._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'STATUS_ERROR');
+
+  static const $core.List<Status> values = <Status> [
+    STATUS_UNSPECIFIED,
+    STATUS_EXECUTING,
+    STATUS_FINISHED,
+    STATUS_ERROR,
+  ];
+
+  static final $core.Map<$core.int, Status> _byValue = $pb.ProtobufEnum.initByValue(values);
+  static Status? valueOf($core.int value) => _byValue[value];
+
+  const Status._($core.int v, $core.String n) : super(v, n);
+}
+
diff --git a/playground/frontend/lib/api/v1/api.pbgrpc.dart b/playground/frontend/lib/api/v1/api.pbgrpc.dart
new file mode 100644
index 0000000..3cfd258
--- /dev/null
+++ b/playground/frontend/lib/api/v1/api.pbgrpc.dart
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///
+//  Generated code. Do not modify.
+//  source: api/v1/api.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:async' as $async;
+
+import 'dart:core' as $core;
+
+import 'package:grpc/service_api.dart' as $grpc;
+import 'api.pb.dart' as $0;
+export 'api.pb.dart';
+
+class PlaygroundServiceClient extends $grpc.Client {
+  static final _$runCode =
+      $grpc.ClientMethod<$0.RunCodeRequest, $0.RunCodeResponse>(
+          '/api.v1.PlaygroundService/RunCode',
+          ($0.RunCodeRequest value) => value.writeToBuffer(),
+          ($core.List<$core.int> value) =>
+              $0.RunCodeResponse.fromBuffer(value));
+  static final _$checkStatus =
+      $grpc.ClientMethod<$0.CheckStatusRequest, $0.CheckStatusResponse>(
+          '/api.v1.PlaygroundService/CheckStatus',
+          ($0.CheckStatusRequest value) => value.writeToBuffer(),
+          ($core.List<$core.int> value) =>
+              $0.CheckStatusResponse.fromBuffer(value));
+  static final _$getRunOutput =
+      $grpc.ClientMethod<$0.GetRunOutputRequest, $0.GetRunOutputResponse>(
+          '/api.v1.PlaygroundService/GetRunOutput',
+          ($0.GetRunOutputRequest value) => value.writeToBuffer(),
+          ($core.List<$core.int> value) =>
+              $0.GetRunOutputResponse.fromBuffer(value));
+  static final _$getCompileOutput = $grpc.ClientMethod<
+          $0.GetCompileOutputRequest, $0.GetCompileOutputResponse>(
+      '/api.v1.PlaygroundService/GetCompileOutput',
+      ($0.GetCompileOutputRequest value) => value.writeToBuffer(),
+      ($core.List<$core.int> value) =>
+          $0.GetCompileOutputResponse.fromBuffer(value));
+
+  PlaygroundServiceClient($grpc.ClientChannel channel,
+      {$grpc.CallOptions? options,
+      $core.Iterable<$grpc.ClientInterceptor>? interceptors})
+      : super(channel, options: options, interceptors: interceptors);
+
+  $grpc.ResponseFuture<$0.RunCodeResponse> runCode($0.RunCodeRequest request,
+      {$grpc.CallOptions? options}) {
+    return $createUnaryCall(_$runCode, request, options: options);
+  }
+
+  $grpc.ResponseFuture<$0.CheckStatusResponse> checkStatus(
+      $0.CheckStatusRequest request,
+      {$grpc.CallOptions? options}) {
+    return $createUnaryCall(_$checkStatus, request, options: options);
+  }
+
+  $grpc.ResponseFuture<$0.GetRunOutputResponse> getRunOutput(
+      $0.GetRunOutputRequest request,
+      {$grpc.CallOptions? options}) {
+    return $createUnaryCall(_$getRunOutput, request, options: options);
+  }
+
+  $grpc.ResponseFuture<$0.GetCompileOutputResponse> getCompileOutput(
+      $0.GetCompileOutputRequest request,
+      {$grpc.CallOptions? options}) {
+    return $createUnaryCall(_$getCompileOutput, request, options: options);
+  }
+}
+
+abstract class PlaygroundServiceBase extends $grpc.Service {
+  $core.String get $name => 'api.v1.PlaygroundService';
+
+  PlaygroundServiceBase() {
+    $addMethod($grpc.ServiceMethod<$0.RunCodeRequest, $0.RunCodeResponse>(
+        'RunCode',
+        runCode_Pre,
+        false,
+        false,
+        ($core.List<$core.int> value) => $0.RunCodeRequest.fromBuffer(value),
+        ($0.RunCodeResponse value) => value.writeToBuffer()));
+    $addMethod(
+        $grpc.ServiceMethod<$0.CheckStatusRequest, $0.CheckStatusResponse>(
+            'CheckStatus',
+            checkStatus_Pre,
+            false,
+            false,
+            ($core.List<$core.int> value) =>
+                $0.CheckStatusRequest.fromBuffer(value),
+            ($0.CheckStatusResponse value) => value.writeToBuffer()));
+    $addMethod(
+        $grpc.ServiceMethod<$0.GetRunOutputRequest, $0.GetRunOutputResponse>(
+            'GetRunOutput',
+            getRunOutput_Pre,
+            false,
+            false,
+            ($core.List<$core.int> value) =>
+                $0.GetRunOutputRequest.fromBuffer(value),
+            ($0.GetRunOutputResponse value) => value.writeToBuffer()));
+    $addMethod($grpc.ServiceMethod<$0.GetCompileOutputRequest,
+            $0.GetCompileOutputResponse>(
+        'GetCompileOutput',
+        getCompileOutput_Pre,
+        false,
+        false,
+        ($core.List<$core.int> value) =>
+            $0.GetCompileOutputRequest.fromBuffer(value),
+        ($0.GetCompileOutputResponse value) => value.writeToBuffer()));
+  }
+
+  $async.Future<$0.RunCodeResponse> runCode_Pre(
+      $grpc.ServiceCall call, $async.Future<$0.RunCodeRequest> request) async {
+    return runCode(call, await request);
+  }
+
+  $async.Future<$0.CheckStatusResponse> checkStatus_Pre($grpc.ServiceCall call,
+      $async.Future<$0.CheckStatusRequest> request) async {
+    return checkStatus(call, await request);
+  }
+
+  $async.Future<$0.GetRunOutputResponse> getRunOutput_Pre(
+      $grpc.ServiceCall call,
+      $async.Future<$0.GetRunOutputRequest> request) async {
+    return getRunOutput(call, await request);
+  }
+
+  $async.Future<$0.GetCompileOutputResponse> getCompileOutput_Pre(
+      $grpc.ServiceCall call,
+      $async.Future<$0.GetCompileOutputRequest> request) async {
+    return getCompileOutput(call, await request);
+  }
+
+  $async.Future<$0.RunCodeResponse> runCode(
+      $grpc.ServiceCall call, $0.RunCodeRequest request);
+  $async.Future<$0.CheckStatusResponse> checkStatus(
+      $grpc.ServiceCall call, $0.CheckStatusRequest request);
+  $async.Future<$0.GetRunOutputResponse> getRunOutput(
+      $grpc.ServiceCall call, $0.GetRunOutputRequest request);
+  $async.Future<$0.GetCompileOutputResponse> getCompileOutput(
+      $grpc.ServiceCall call, $0.GetCompileOutputRequest request);
+}
diff --git a/playground/frontend/lib/api/v1/api.pbjson.dart b/playground/frontend/lib/api/v1/api.pbjson.dart
new file mode 100644
index 0000000..4b29e56
--- /dev/null
+++ b/playground/frontend/lib/api/v1/api.pbjson.dart
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///
+//  Generated code. Do not modify.
+//  source: api/v1/api.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use sdkDescriptor instead')
+const Sdk$json = const {
+  '1': 'Sdk',
+  '2': const [
+    const {'1': 'SDK_UNSPECIFIED', '2': 0},
+    const {'1': 'SDK_JAVA', '2': 1},
+    const {'1': 'SDK_GO', '2': 2},
+    const {'1': 'SDK_PYTHON', '2': 3},
+    const {'1': 'SDK_SCIO', '2': 4},
+  ],
+};
+
+/// Descriptor for `Sdk`. Decode as a `google.protobuf.EnumDescriptorProto`.
+final $typed_data.Uint8List sdkDescriptor = $convert.base64Decode('CgNTZGsSEwoPU0RLX1VOU1BFQ0lGSUVEEAASDAoIU0RLX0pBVkEQARIKCgZTREtfR08QAhIOCgpTREtfUFlUSE9OEAMSDAoIU0RLX1NDSU8QBA==');
+@$core.Deprecated('Use statusDescriptor instead')
+const Status$json = const {
+  '1': 'Status',
+  '2': const [
+    const {'1': 'STATUS_UNSPECIFIED', '2': 0},
+    const {'1': 'STATUS_EXECUTING', '2': 1},
+    const {'1': 'STATUS_FINISHED', '2': 2},
+    const {'1': 'STATUS_ERROR', '2': 3},
+  ],
+};
+
+/// Descriptor for `Status`. Decode as a `google.protobuf.EnumDescriptorProto`.
+final $typed_data.Uint8List statusDescriptor = $convert.base64Decode('CgZTdGF0dXMSFgoSU1RBVFVTX1VOU1BFQ0lGSUVEEAASFAoQU1RBVFVTX0VYRUNVVElORxABEhMKD1NUQVRVU19GSU5JU0hFRBACEhAKDFNUQVRVU19FUlJPUhAD');
+@$core.Deprecated('Use runCodeRequestDescriptor instead')
+const RunCodeRequest$json = const {
+  '1': 'RunCodeRequest',
+  '2': const [
+    const {'1': 'code', '3': 1, '4': 1, '5': 9, '10': 'code'},
+    const {'1': 'sdk', '3': 2, '4': 1, '5': 14, '6': '.api.v1.Sdk', '10': 'sdk'},
+  ],
+};
+
+/// Descriptor for `RunCodeRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List runCodeRequestDescriptor = $convert.base64Decode('Cg5SdW5Db2RlUmVxdWVzdBISCgRjb2RlGAEgASgJUgRjb2RlEh0KA3NkaxgCIAEoDjILLmFwaS52MS5TZGtSA3Nkaw==');
+@$core.Deprecated('Use runCodeResponseDescriptor instead')
+const RunCodeResponse$json = const {
+  '1': 'RunCodeResponse',
+  '2': const [
+    const {'1': 'pipeline_uuid', '3': 1, '4': 1, '5': 9, '10': 'pipelineUuid'},
+  ],
+};
+
+/// Descriptor for `RunCodeResponse`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List runCodeResponseDescriptor = $convert.base64Decode('Cg9SdW5Db2RlUmVzcG9uc2USIwoNcGlwZWxpbmVfdXVpZBgBIAEoCVIMcGlwZWxpbmVVdWlk');
+@$core.Deprecated('Use checkStatusRequestDescriptor instead')
+const CheckStatusRequest$json = const {
+  '1': 'CheckStatusRequest',
+  '2': const [
+    const {'1': 'pipeline_uuid', '3': 1, '4': 1, '5': 9, '10': 'pipelineUuid'},
+  ],
+};
+
+/// Descriptor for `CheckStatusRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List checkStatusRequestDescriptor = $convert.base64Decode('ChJDaGVja1N0YXR1c1JlcXVlc3QSIwoNcGlwZWxpbmVfdXVpZBgBIAEoCVIMcGlwZWxpbmVVdWlk');
+@$core.Deprecated('Use checkStatusResponseDescriptor instead')
+const CheckStatusResponse$json = const {
+  '1': 'CheckStatusResponse',
+  '2': const [
+    const {'1': 'status', '3': 1, '4': 1, '5': 14, '6': '.api.v1.Status', '10': 'status'},
+  ],
+};
+
+/// Descriptor for `CheckStatusResponse`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List checkStatusResponseDescriptor = $convert.base64Decode('ChNDaGVja1N0YXR1c1Jlc3BvbnNlEiYKBnN0YXR1cxgBIAEoDjIOLmFwaS52MS5TdGF0dXNSBnN0YXR1cw==');
+@$core.Deprecated('Use getCompileOutputRequestDescriptor instead')
+const GetCompileOutputRequest$json = const {
+  '1': 'GetCompileOutputRequest',
+  '2': const [
+    const {'1': 'pipeline_uuid', '3': 1, '4': 1, '5': 9, '10': 'pipelineUuid'},
+  ],
+};
+
+/// Descriptor for `GetCompileOutputRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List getCompileOutputRequestDescriptor = $convert.base64Decode('ChdHZXRDb21waWxlT3V0cHV0UmVxdWVzdBIjCg1waXBlbGluZV91dWlkGAEgASgJUgxwaXBlbGluZVV1aWQ=');
+@$core.Deprecated('Use getCompileOutputResponseDescriptor instead')
+const GetCompileOutputResponse$json = const {
+  '1': 'GetCompileOutputResponse',
+  '2': const [
+    const {'1': 'output', '3': 1, '4': 1, '5': 9, '10': 'output'},
+    const {'1': 'compilation_status', '3': 2, '4': 1, '5': 14, '6': '.api.v1.Status', '10': 'compilationStatus'},
+  ],
+};
+
+/// Descriptor for `GetCompileOutputResponse`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List getCompileOutputResponseDescriptor = $convert.base64Decode('ChhHZXRDb21waWxlT3V0cHV0UmVzcG9uc2USFgoGb3V0cHV0GAEgASgJUgZvdXRwdXQSPQoSY29tcGlsYXRpb25fc3RhdHVzGAIgASgOMg4uYXBpLnYxLlN0YXR1c1IRY29tcGlsYXRpb25TdGF0dXM=');
+@$core.Deprecated('Use getRunOutputRequestDescriptor instead')
+const GetRunOutputRequest$json = const {
+  '1': 'GetRunOutputRequest',
+  '2': const [
+    const {'1': 'pipeline_uuid', '3': 1, '4': 1, '5': 9, '10': 'pipelineUuid'},
+  ],
+};
+
+/// Descriptor for `GetRunOutputRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List getRunOutputRequestDescriptor = $convert.base64Decode('ChNHZXRSdW5PdXRwdXRSZXF1ZXN0EiMKDXBpcGVsaW5lX3V1aWQYASABKAlSDHBpcGVsaW5lVXVpZA==');
+@$core.Deprecated('Use getRunOutputResponseDescriptor instead')
+const GetRunOutputResponse$json = const {
+  '1': 'GetRunOutputResponse',
+  '2': const [
+    const {'1': 'output', '3': 1, '4': 1, '5': 9, '10': 'output'},
+    const {'1': 'compilation_status', '3': 2, '4': 1, '5': 14, '6': '.api.v1.Status', '10': 'compilationStatus'},
+  ],
+};
+
+/// Descriptor for `GetRunOutputResponse`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List getRunOutputResponseDescriptor = $convert.base64Decode('ChRHZXRSdW5PdXRwdXRSZXNwb25zZRIWCgZvdXRwdXQYASABKAlSBm91dHB1dBI9ChJjb21waWxhdGlvbl9zdGF0dXMYAiABKA4yDi5hcGkudjEuU3RhdHVzUhFjb21waWxhdGlvblN0YXR1cw==');
diff --git a/playground/frontend/lib/modules/editor/repository/code_repository/code_client/grpc_code_client.dart b/playground/frontend/lib/modules/editor/repository/code_repository/code_client/grpc_code_client.dart
index 5173542..93ee0ef 100644
--- a/playground/frontend/lib/modules/editor/repository/code_repository/code_client/grpc_code_client.dart
+++ b/playground/frontend/lib/modules/editor/repository/code_repository/code_client/grpc_code_client.dart
@@ -18,7 +18,7 @@
 
 import 'package:grpc/grpc_web.dart';
 import 'package:playground/constants/api.dart';
-import 'package:playground/generated/playground.pbgrpc.dart' as grpc;
+import 'package:playground/api/v1/api.pbgrpc.dart' as grpc;
 import 'package:playground/modules/editor/repository/code_repository/code_client/check_status_response.dart';
 import 'package:playground/modules/editor/repository/code_repository/code_client/code_client.dart';
 import 'package:playground/modules/editor/repository/code_repository/code_client/output_response.dart';
diff --git a/playground/frontend/test/modules/editor/repository/code_repository/code_repository_test.mocks.dart b/playground/frontend/test/modules/editor/repository/code_repository/code_repository_test.mocks.dart
new file mode 100644
index 0000000..11c8517
--- /dev/null
+++ b/playground/frontend/test/modules/editor/repository/code_repository/code_repository_test.mocks.dart
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Mocks generated by Mockito 5.0.16 from annotations
+// in playground/test/modules/editor/repository/code_repository/code_repository_test.dart.
+// Do not manually edit this file.
+
+import 'dart:async' as _i6;
+
+import 'package:mockito/mockito.dart' as _i1;
+import 'package:playground/modules/editor/repository/code_repository/code_client/check_status_response.dart'
+    as _i3;
+import 'package:playground/modules/editor/repository/code_repository/code_client/code_client.dart'
+    as _i5;
+import 'package:playground/modules/editor/repository/code_repository/code_client/output_response.dart'
+    as _i4;
+import 'package:playground/modules/editor/repository/code_repository/code_client/run_code_response.dart'
+    as _i2;
+import 'package:playground/modules/editor/repository/code_repository/run_code_request.dart'
+    as _i7;
+
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+
+class _FakeRunCodeResponse_0 extends _i1.Fake implements _i2.RunCodeResponse {}
+
+class _FakeCheckStatusResponse_1 extends _i1.Fake
+    implements _i3.CheckStatusResponse {}
+
+class _FakeOutputResponse_2 extends _i1.Fake implements _i4.OutputResponse {}
+
+/// A class which mocks [CodeClient].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockCodeClient extends _i1.Mock implements _i5.CodeClient {
+  MockCodeClient() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  _i6.Future<_i2.RunCodeResponse> runCode(_i7.RunCodeRequestWrapper? request) =>
+      (super.noSuchMethod(Invocation.method(#runCode, [request]),
+              returnValue:
+                  Future<_i2.RunCodeResponse>.value(_FakeRunCodeResponse_0()))
+          as _i6.Future<_i2.RunCodeResponse>);
+  @override
+  _i6.Future<_i3.CheckStatusResponse> checkStatus(String? pipelineUuid) =>
+      (super.noSuchMethod(Invocation.method(#checkStatus, [pipelineUuid]),
+              returnValue: Future<_i3.CheckStatusResponse>.value(
+                  _FakeCheckStatusResponse_1()))
+          as _i6.Future<_i3.CheckStatusResponse>);
+  @override
+  _i6.Future<_i4.OutputResponse> getCompileOutput(String? pipelineUuid) =>
+      (super.noSuchMethod(Invocation.method(#getCompileOutput, [pipelineUuid]),
+              returnValue:
+                  Future<_i4.OutputResponse>.value(_FakeOutputResponse_2()))
+          as _i6.Future<_i4.OutputResponse>);
+  @override
+  _i6.Future<_i4.OutputResponse> getRunOutput(String? pipelineUuid) =>
+      (super.noSuchMethod(Invocation.method(#getRunOutput, [pipelineUuid]),
+              returnValue:
+                  Future<_i4.OutputResponse>.value(_FakeOutputResponse_2()))
+          as _i6.Future<_i4.OutputResponse>);
+  @override
+  String toString() => super.toString();
+}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index d9d4593..4c74386 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -56,6 +56,9 @@ include(":examples:kotlin")
 include(":model:fn-execution")
 include(":model:job-management")
 include(":model:pipeline")
+include(":playground")
+include(":playground:backend")
+include(":playground:frontend")
 include(":runners:core-construction-java")
 include(":runners:core-java")
 include(":runners:direct-java")