Backend provides the learning content tree for a given SDK, and currently logged-in user's snippets and progress. Currently it supports Java, Python, and Go Beam SDK.
It is comprised of several Cloud Functions, with Firerstore in Datastore mode as a storage. Public endpoints:
Authorized endpoints also consume Authorization: Bearer <id_token> header
We use Playground GRPC to save/get user snippets, so we keep the generated stubs in playground_api To re-generate, refer to Playground Readme, section “Re-generate protobuf”.
To update mocks for tests, run:
$ go generate -x ./...
Note:
moqtool to be installed
The storage is shared with Beam Playground backend, so that Tour Of Beam could access its entities in pg_examples and pg_snippets.
Conceptually, the learning tree is a tree with a root node in tb_learning_path, having several children of tb_learning_module, and each module has its descendant nodes. Node is either a group or a unit.
Every module or unit has SDK-wide unique ID, which is provided by a content maintainer. User's progress on a unit is tied to its ID, and if ID changes, the progress is lost.
Kinds
tb_learning_path
key: (SDK_JAVA|SDK_PYTHON|SDK_GO)
tb_learning_module
key: <SDK>_<moduleID>
parentKey: Learning Path key SDK
tb_learning_node
key: <SDK>_<persistentID>
parentKey: parent module/group key
tb_user
key: uid from IDToken
tb_user_progress
key: <SDK>_<unitID>
parentKey: tb_user entity key
Prerequisites:
playground by default)us-east1-bTo discover Router host:
# setup kubectl credentials
gcloud container clusters get-credentials $GKE_CLUSTER_NAME --zone $GKE_ZONE --project $PROJECT_ID
# get external host:port of a backend-router-grpc service
PLAYGROUND_ROUTER_HOST=$(kubectl get svc -l "app=backend-router-grpc" \
-o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}:{.items[0].spec.ports[0].port}'\
)
gcloud datastore indexes create ./internal/storage/index.yaml
for endpoint in getSdkList getContentTree getUnitContent getUserProgress postUnitComplete postUserCode; do gcloud functions deploy $endpoint --entry-point $endpoint \ --region $REGION --runtime go116 --allow-unauthenticated \ --trigger-http \ --set-env-vars="DATASTORE_PROJECT_ID=$PROJECT_ID,DATASTORE_NAMESPACE=$DATASTORE_NAMESPACE,GOOGLE_PROJECT_ID=$PROJECT_ID,PLAYGROUND_ROUTER_HOST=$PLAYGROUND_ROUTER_HOST" done
$ go run cmd/ci_cd/ci_cd.go
Entry point: list sdk names
$ curl -X GET "https://$REGION-$PROJECT_ID.cloudfunctions.net/getSdkList" | json_pp
$ curl -X GET "https://$REGION-$PROJECT_ID.cloudfunctions.net/getContentTree?sdk=python"
$ curl -X GET "https://$REGION-$PROJECT_ID.cloudfunctions.net/getUnitContent?sdk=python&id=challenge1"
$ curl -X GET -H "Authorization: Bearer $token" \ "https://$REGION-$PROJECT_ID.cloudfunctions.net/getUserProgress?sdk=python"
$ curl -X POST -H "Authorization: Bearer $token" \
"https://$REGION-$PROJECT_ID.cloudfunctions.net/postUnitComplete?sdk=python&id=challenge1" -d '{}'
request body:
{ "pipelineOptions": "some pipeline opts", "files": [ {"name": "main.py", "content": "import sys; sys.exit(0)", "isMain": true} ] }
$ curl -X POST -H "Authorization: Bearer $token" \ "https://$REGION-$PROJECT_ID.cloudfunctions.net/postUserCode?sdk=python&id=challenge1" -d @request.json
$ curl -X POST -H "Authorization: Bearer $token" \
"https://$REGION-$PROJECT_ID.cloudfunctions.net/postDeleteProgress" -d '{}'