You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@beam.apache.org by da...@apache.org on 2022/10/06 20:51:44 UTC
[beam] branch master updated: [Tour of Beam][Frontend] Content Tree and SDK models (#23316) (#23417)
This is an automated email from the ASF dual-hosted git repository.
damccorm 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 41ddc4a4bb2 [Tour of Beam][Frontend] Content Tree and SDK models (#23316) (#23417)
41ddc4a4bb2 is described below
commit 41ddc4a4bb2bdeb14cd4065df4b781b061f5fd18
Author: Darkhan Nausharipov <31...@users.noreply.github.com>
AuthorDate: Fri Oct 7 02:51:36 2022 +0600
[Tour of Beam][Frontend] Content Tree and SDK models (#23316) (#23417)
* Content Tree and SDK models (#23316)
models from JSON (#23316)
removed generated files (#23316)
const model constructors (#23316)
fvm config (#23316)
ignore *g.dart files (#23316)
server node, group, unit models
abstract node model (#23316)
node type in enums (#23316)
gitignore sort (#23316)
sdk model (#23316)
api calls (#23316)
getSdks in SdkDropdown (#23316)
requests in functions (#23316)
k in functions (#23316)
server folder in models (#23316)
review comments (#23316)
Move response model files (#23316)
Add CloudFunctionsTobClient (#23316)
Move a file (#23316)
Rename a file (#23316)
ContentTreeCache (#23316)
ContentTreeCache (#23316)
* Update README with building manual (#23316)
* Move hardcoded URLs to a config file (#23316)
Co-authored-by: darkhan.nausharipov <da...@kzn.akvelon.com>
Co-authored-by: Alexey Inkin <le...@inkin.ru>
---
.gitignore | 5 +-
learning/tour-of-beam/frontend/README.md | 22 ++++-
.../lib/{locator.dart => cache/content_tree.dart} | 36 +++++++-
.../lib/{locator.dart => cache/sdk_cache.dart} | 37 +++++++-
.../builders/content_tree.dart} | 29 +++++-
.../builders/sdks_builder.dart} | 25 ++++-
.../frontend/lib/components/sdk_dropdown.dart | 48 ++++++----
learning/tour-of-beam/frontend/lib/config.dart | 39 ++++++++
learning/tour-of-beam/frontend/lib/locator.dart | 12 ++-
.../frontend/lib/models/abstract_node.dart | 50 ++++++++++
.../lib/{locator.dart => models/content_tree.dart} | 21 ++++-
.../lib/{locator.dart => models/group.dart} | 17 +++-
.../lib/{locator.dart => models/module.dart} | 27 +++++-
.../frontend/lib/{locator.dart => models/sdk.dart} | 16 +++-
.../lib/{locator.dart => models/unit.dart} | 12 ++-
.../frontend/lib/pages/tour/playground_demo.dart | 13 +--
.../frontend/lib/pages/tour/screen.dart | 101 +++++++++++++--------
.../frontend/lib/pages/welcome/screen.dart | 76 ++++++++++------
.../frontend/lib/repositories/client/client.dart | 11 ++-
.../client/cloud_functions_client.dart | 51 +++++++++++
.../models/get_sdks_response.dart} | 17 +++-
.../models/group.dart} | 21 ++++-
.../models/node.dart} | 25 ++++-
.../lib/repositories/models/node_type_enum.dart | 7 +-
.../models/unit.dart} | 19 +++-
learning/tour-of-beam/frontend/pubspec.lock | 31 ++++++-
learning/tour-of-beam/frontend/pubspec.yaml | 8 +-
.../lib/src/enums/complexity.dart | 5 +
28 files changed, 605 insertions(+), 176 deletions(-)
diff --git a/.gitignore b/.gitignore
index fede9851091..62f5a20d45e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -120,12 +120,13 @@ website/www/yarn-error.log
**/node_modules
# Dart/Flutter
-**/.dart_tool
-**/.packages
**/.flutter-plugins
**/.flutter-plugins-dependencies
+**/.dart_tool
**/generated_plugin_registrant.dart
+**/*.g.dart
**/*.mocks.dart
+**/.packages
# Ignore Beam Playground Terraform
**/.terraform
diff --git a/learning/tour-of-beam/frontend/README.md b/learning/tour-of-beam/frontend/README.md
index afd892b3f8e..2e27a05ad16 100644
--- a/learning/tour-of-beam/frontend/README.md
+++ b/learning/tour-of-beam/frontend/README.md
@@ -22,9 +22,25 @@
# About
- # Getting started
- Flutter installation guide: https://docs.flutter.dev/get-started/install
- Run the app: `flutter run --web-renderer html`
+## Getting started
+Running, debugging, and testing all require this first step that fetches
+dependencies and generates code:
+
+```bash
+cd ../../../playground/frontend/playground_components
+flutter pub get
+flutter pub run build_runner build
+cd ../../../learning/tour-of-beam/frontend
+flutter pub get
+flutter pub run build_runner build
+```
+
+### Run
+
+The following command is used to build and serve the frontend app locally:
+
+`$ flutter run -d chrome`
+
# Deployment
diff --git a/learning/tour-of-beam/frontend/lib/locator.dart b/learning/tour-of-beam/frontend/lib/cache/content_tree.dart
similarity index 53%
copy from learning/tour-of-beam/frontend/lib/locator.dart
copy to learning/tour-of-beam/frontend/lib/cache/content_tree.dart
index 8ab88a830d0..b8a27c4eb7e 100644
--- a/learning/tour-of-beam/frontend/lib/locator.dart
+++ b/learning/tour-of-beam/frontend/lib/cache/content_tree.dart
@@ -16,9 +16,37 @@
* limitations under the License.
*/
-//import 'package:get_it/get_it.dart';
+import 'dart:async';
-Future<void> initializeServiceLocator() async {
- // See https://github.com/alexeyinkin/mefolio-standalone/blob/main/flutter/lib/locator.dart
- // as an example.
+import 'package:flutter/widgets.dart';
+
+import '../models/content_tree.dart';
+import '../repositories/client/client.dart';
+
+class ContentTreeCache extends ChangeNotifier {
+ final TobClient client;
+
+ ContentTreeModel? _contentTree;
+ Future<ContentTreeModel>? _future;
+
+ ContentTreeCache({
+ required this.client,
+ });
+
+ ContentTreeModel? getContentTree() {
+ if (_future == null) {
+ unawaited(_loadContentTree());
+ }
+
+ return _contentTree;
+ }
+
+ Future<ContentTreeModel?> _loadContentTree() async {
+ _future = client.getContentTree();
+ final result = await _future!;
+ _contentTree = result;
+
+ notifyListeners();
+ return _contentTree;
+ }
}
diff --git a/learning/tour-of-beam/frontend/lib/locator.dart b/learning/tour-of-beam/frontend/lib/cache/sdk_cache.dart
similarity index 54%
copy from learning/tour-of-beam/frontend/lib/locator.dart
copy to learning/tour-of-beam/frontend/lib/cache/sdk_cache.dart
index 8ab88a830d0..22045556805 100644
--- a/learning/tour-of-beam/frontend/lib/locator.dart
+++ b/learning/tour-of-beam/frontend/lib/cache/sdk_cache.dart
@@ -16,9 +16,38 @@
* limitations under the License.
*/
-//import 'package:get_it/get_it.dart';
+import 'dart:async';
-Future<void> initializeServiceLocator() async {
- // See https://github.com/alexeyinkin/mefolio-standalone/blob/main/flutter/lib/locator.dart
- // as an example.
+import 'package:flutter/widgets.dart';
+
+import '../models/sdk.dart';
+import '../repositories/client/client.dart';
+import '../repositories/models/get_sdks_response.dart';
+
+class SdkCache extends ChangeNotifier {
+ final TobClient client;
+
+ final _sdks = <SdkModel>[];
+ Future<GetSdksResponse>? _future;
+
+ SdkCache({
+ required this.client,
+ });
+
+ List<SdkModel> getSdks() {
+ if (_future == null) {
+ unawaited(_loadSdks());
+ }
+
+ return _sdks;
+ }
+
+ Future<List<SdkModel>> _loadSdks() async {
+ _future = client.getSdks();
+ final result = await _future!;
+
+ _sdks.addAll(result.sdks);
+ notifyListeners();
+ return _sdks;
+ }
}
diff --git a/learning/tour-of-beam/frontend/lib/locator.dart b/learning/tour-of-beam/frontend/lib/components/builders/content_tree.dart
similarity index 56%
copy from learning/tour-of-beam/frontend/lib/locator.dart
copy to learning/tour-of-beam/frontend/lib/components/builders/content_tree.dart
index 8ab88a830d0..4aec6ba6ef0 100644
--- a/learning/tour-of-beam/frontend/lib/locator.dart
+++ b/learning/tour-of-beam/frontend/lib/components/builders/content_tree.dart
@@ -16,9 +16,30 @@
* limitations under the License.
*/
-//import 'package:get_it/get_it.dart';
+import 'package:flutter/widgets.dart';
+import 'package:get_it/get_it.dart';
-Future<void> initializeServiceLocator() async {
- // See https://github.com/alexeyinkin/mefolio-standalone/blob/main/flutter/lib/locator.dart
- // as an example.
+import '../../cache/content_tree.dart';
+import '../../models/content_tree.dart';
+
+class ContentTreeBuilder extends StatelessWidget {
+ final ValueWidgetBuilder<ContentTreeModel?> builder;
+
+ const ContentTreeBuilder({
+ required this.builder,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final cache = GetIt.instance.get<ContentTreeCache>();
+
+ return AnimatedBuilder(
+ animation: cache,
+ builder: (context, child) => builder(
+ context,
+ cache.getContentTree(),
+ child,
+ ),
+ );
+ }
}
diff --git a/learning/tour-of-beam/frontend/lib/locator.dart b/learning/tour-of-beam/frontend/lib/components/builders/sdks_builder.dart
similarity index 59%
copy from learning/tour-of-beam/frontend/lib/locator.dart
copy to learning/tour-of-beam/frontend/lib/components/builders/sdks_builder.dart
index 8ab88a830d0..2a21185c084 100644
--- a/learning/tour-of-beam/frontend/lib/locator.dart
+++ b/learning/tour-of-beam/frontend/lib/components/builders/sdks_builder.dart
@@ -16,9 +16,26 @@
* limitations under the License.
*/
-//import 'package:get_it/get_it.dart';
+import 'package:flutter/widgets.dart';
+import 'package:get_it/get_it.dart';
-Future<void> initializeServiceLocator() async {
- // See https://github.com/alexeyinkin/mefolio-standalone/blob/main/flutter/lib/locator.dart
- // as an example.
+import '../../cache/sdk_cache.dart';
+import '../../models/sdk.dart';
+
+class SdksBuilder extends StatelessWidget {
+ final ValueWidgetBuilder<List<SdkModel>> builder;
+
+ const SdksBuilder({
+ required this.builder,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final cache = GetIt.instance.get<SdkCache>();
+
+ return AnimatedBuilder(
+ animation: cache,
+ builder: (context, child) => builder(context, cache.getSdks(), child),
+ );
+ }
}
diff --git a/learning/tour-of-beam/frontend/lib/components/sdk_dropdown.dart b/learning/tour-of-beam/frontend/lib/components/sdk_dropdown.dart
index 47f1a728b8e..7c4fc9b3006 100644
--- a/learning/tour-of-beam/frontend/lib/components/sdk_dropdown.dart
+++ b/learning/tour-of-beam/frontend/lib/components/sdk_dropdown.dart
@@ -19,30 +19,40 @@
import 'package:flutter/material.dart';
import 'package:playground_components/playground_components.dart';
+import 'builders/sdks_builder.dart';
+
class SdkDropdown extends StatelessWidget {
const SdkDropdown();
@override
Widget build(BuildContext context) {
- return _DropdownWrapper(
- child: DropdownButton(
- value: 'Java',
- onChanged: (sdk) {
- // TODO(nausharipov): change SDK
- },
- items: const ['Java', 'Python', 'Go']
- .map(
- (sdk) => DropdownMenuItem(
- value: sdk,
- child: Text(sdk),
- ),
- )
- .toList(growable: false),
- isDense: true,
- alignment: Alignment.center,
- focusColor: BeamColors.transparent,
- borderRadius: BorderRadius.circular(BeamSizes.size6),
- ),
+ return SdksBuilder(
+ builder: (context, sdks, _) {
+ if (sdks.isEmpty) {
+ return Container();
+ }
+
+ return _DropdownWrapper(
+ child: DropdownButton(
+ value: sdks.first.id,
+ onChanged: (sdk) {
+ // TODO(nausharipov): change SDK
+ },
+ items: sdks
+ .map(
+ (sdk) => DropdownMenuItem(
+ value: sdk.id,
+ child: Text(sdk.title),
+ ),
+ )
+ .toList(growable: false),
+ isDense: true,
+ alignment: Alignment.center,
+ focusColor: BeamColors.transparent,
+ borderRadius: BorderRadius.circular(BeamSizes.size6),
+ ),
+ );
+ },
);
}
}
diff --git a/learning/tour-of-beam/frontend/lib/config.dart b/learning/tour-of-beam/frontend/lib/config.dart
new file mode 100644
index 00000000000..b7ed542e1b0
--- /dev/null
+++ b/learning/tour-of-beam/frontend/lib/config.dart
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+// TODO(alexeyinkin): Generate this file on deployment.
+
+const _cloudFunctionsProjectRegion = 'us-central1';
+const _cloudFunctionsProjectId = 'tour-of-beam-2';
+const cloudFunctionsBaseUrl = 'https://'
+ '$_cloudFunctionsProjectRegion-$_cloudFunctionsProjectId'
+ '.cloudfunctions.net';
+
+// Copied from Playground's config.g.dart
+
+const String kAnalyticsUA = 'UA-73650088-2';
+const String kApiClientURL =
+ 'https://backend-router-beta-dot-apache-beam-testing.appspot.com';
+const String kApiJavaClientURL =
+ 'https://backend-java-beta-dot-apache-beam-testing.appspot.com';
+const String kApiGoClientURL =
+ 'https://backend-go-beta-dot-apache-beam-testing.appspot.com';
+const String kApiPythonClientURL =
+ 'https://backend-python-beta-dot-apache-beam-testing.appspot.com';
+const String kApiScioClientURL =
+ 'https://backend-scio-beta-dot-apache-beam-testing.appspot.com';
diff --git a/learning/tour-of-beam/frontend/lib/locator.dart b/learning/tour-of-beam/frontend/lib/locator.dart
index 8ab88a830d0..4ac8c4c4d35 100644
--- a/learning/tour-of-beam/frontend/lib/locator.dart
+++ b/learning/tour-of-beam/frontend/lib/locator.dart
@@ -16,9 +16,15 @@
* limitations under the License.
*/
-//import 'package:get_it/get_it.dart';
+import 'package:get_it/get_it.dart';
+
+import 'cache/content_tree.dart';
+import 'cache/sdk_cache.dart';
+import 'repositories/client/cloud_functions_client.dart';
Future<void> initializeServiceLocator() async {
- // See https://github.com/alexeyinkin/mefolio-standalone/blob/main/flutter/lib/locator.dart
- // as an example.
+ final client = CloudFunctionsTobClient();
+
+ GetIt.instance.registerSingleton(ContentTreeCache(client: client));
+ GetIt.instance.registerSingleton(SdkCache(client: client));
}
diff --git a/learning/tour-of-beam/frontend/lib/models/abstract_node.dart b/learning/tour-of-beam/frontend/lib/models/abstract_node.dart
new file mode 100644
index 00000000000..00769490e28
--- /dev/null
+++ b/learning/tour-of-beam/frontend/lib/models/abstract_node.dart
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+import '../repositories/models/node.dart';
+import '../repositories/models/node_type_enum.dart';
+import 'group.dart';
+import 'unit.dart';
+
+abstract class NodeModel {
+ final String title;
+ const NodeModel({required this.title});
+
+ /// Constructs nodes from the response data.
+ ///
+ /// Models from the response are inconvenient for a direct use in the app
+ /// because they come from a golang backend which does not
+ /// support inheritance, and so they use an extra layer of composition
+ /// which is inconvenient in Flutter.
+ static List<NodeModel> fromMaps(List json) {
+ return json
+ .cast<Map<String, dynamic>>()
+ .map<NodeResponseModel>(NodeResponseModel.fromJson)
+ .map(fromResponse)
+ .toList();
+ }
+
+ static NodeModel fromResponse(NodeResponseModel node) {
+ switch (node.type) {
+ case NodeType.group:
+ return GroupModel.fromResponse(node.group!);
+ case NodeType.unit:
+ return UnitModel.fromResponse(node.unit!);
+ }
+ }
+}
diff --git a/learning/tour-of-beam/frontend/lib/locator.dart b/learning/tour-of-beam/frontend/lib/models/content_tree.dart
similarity index 65%
copy from learning/tour-of-beam/frontend/lib/locator.dart
copy to learning/tour-of-beam/frontend/lib/models/content_tree.dart
index 8ab88a830d0..7e3aa74f8c2 100644
--- a/learning/tour-of-beam/frontend/lib/locator.dart
+++ b/learning/tour-of-beam/frontend/lib/models/content_tree.dart
@@ -16,9 +16,22 @@
* limitations under the License.
*/
-//import 'package:get_it/get_it.dart';
+import 'package:json_annotation/json_annotation.dart';
-Future<void> initializeServiceLocator() async {
- // See https://github.com/alexeyinkin/mefolio-standalone/blob/main/flutter/lib/locator.dart
- // as an example.
+import 'module.dart';
+
+part 'content_tree.g.dart';
+
+@JsonSerializable(createToJson: false)
+class ContentTreeModel {
+ final String sdkId;
+ final List<ModuleModel> modules;
+
+ const ContentTreeModel({
+ required this.sdkId,
+ required this.modules,
+ });
+
+ factory ContentTreeModel.fromJson(Map<String, dynamic> json) =>
+ _$ContentTreeModelFromJson(json);
}
diff --git a/learning/tour-of-beam/frontend/lib/locator.dart b/learning/tour-of-beam/frontend/lib/models/group.dart
similarity index 67%
copy from learning/tour-of-beam/frontend/lib/locator.dart
copy to learning/tour-of-beam/frontend/lib/models/group.dart
index 8ab88a830d0..64a3d79c200 100644
--- a/learning/tour-of-beam/frontend/lib/locator.dart
+++ b/learning/tour-of-beam/frontend/lib/models/group.dart
@@ -16,9 +16,18 @@
* limitations under the License.
*/
-//import 'package:get_it/get_it.dart';
+import '../repositories/models/group.dart';
+import 'abstract_node.dart';
-Future<void> initializeServiceLocator() async {
- // See https://github.com/alexeyinkin/mefolio-standalone/blob/main/flutter/lib/locator.dart
- // as an example.
+class GroupModel extends NodeModel {
+ final List<NodeModel> nodes;
+
+ const GroupModel({
+ required super.title,
+ required this.nodes,
+ });
+
+ GroupModel.fromResponse(GroupResponseModel group)
+ : nodes = group.nodes.map(NodeModel.fromResponse).toList(growable: false),
+ super(title: group.title);
}
diff --git a/learning/tour-of-beam/frontend/lib/locator.dart b/learning/tour-of-beam/frontend/lib/models/module.dart
similarity index 57%
copy from learning/tour-of-beam/frontend/lib/locator.dart
copy to learning/tour-of-beam/frontend/lib/models/module.dart
index 8ab88a830d0..fc057fd9397 100644
--- a/learning/tour-of-beam/frontend/lib/locator.dart
+++ b/learning/tour-of-beam/frontend/lib/models/module.dart
@@ -16,9 +16,28 @@
* limitations under the License.
*/
-//import 'package:get_it/get_it.dart';
+import 'package:json_annotation/json_annotation.dart';
+import 'package:playground_components/playground_components.dart';
-Future<void> initializeServiceLocator() async {
- // See https://github.com/alexeyinkin/mefolio-standalone/blob/main/flutter/lib/locator.dart
- // as an example.
+import 'abstract_node.dart';
+
+part 'module.g.dart';
+
+@JsonSerializable(createToJson: false)
+class ModuleModel {
+ final String id;
+ final String title;
+ final Complexity complexity;
+ @JsonKey(fromJson: NodeModel.fromMaps)
+ final List<NodeModel> nodes;
+
+ const ModuleModel({
+ required this.id,
+ required this.title,
+ required this.complexity,
+ required this.nodes,
+ });
+
+ factory ModuleModel.fromJson(Map<String, dynamic> json) =>
+ _$ModuleModelFromJson(json);
}
diff --git a/learning/tour-of-beam/frontend/lib/locator.dart b/learning/tour-of-beam/frontend/lib/models/sdk.dart
similarity index 71%
copy from learning/tour-of-beam/frontend/lib/locator.dart
copy to learning/tour-of-beam/frontend/lib/models/sdk.dart
index 8ab88a830d0..64ef531a501 100644
--- a/learning/tour-of-beam/frontend/lib/locator.dart
+++ b/learning/tour-of-beam/frontend/lib/models/sdk.dart
@@ -16,9 +16,17 @@
* limitations under the License.
*/
-//import 'package:get_it/get_it.dart';
+import 'package:json_annotation/json_annotation.dart';
-Future<void> initializeServiceLocator() async {
- // See https://github.com/alexeyinkin/mefolio-standalone/blob/main/flutter/lib/locator.dart
- // as an example.
+part 'sdk.g.dart';
+
+@JsonSerializable(createToJson: false)
+class SdkModel {
+ final String id;
+ final String title;
+
+ const SdkModel({required this.id, required this.title});
+
+ factory SdkModel.fromJson(Map<String, dynamic> json) =>
+ _$SdkModelFromJson(json);
}
diff --git a/learning/tour-of-beam/frontend/lib/locator.dart b/learning/tour-of-beam/frontend/lib/models/unit.dart
similarity index 77%
copy from learning/tour-of-beam/frontend/lib/locator.dart
copy to learning/tour-of-beam/frontend/lib/models/unit.dart
index 8ab88a830d0..9d37cbab058 100644
--- a/learning/tour-of-beam/frontend/lib/locator.dart
+++ b/learning/tour-of-beam/frontend/lib/models/unit.dart
@@ -16,9 +16,13 @@
* limitations under the License.
*/
-//import 'package:get_it/get_it.dart';
+import '../repositories/models/unit.dart';
+import 'abstract_node.dart';
-Future<void> initializeServiceLocator() async {
- // See https://github.com/alexeyinkin/mefolio-standalone/blob/main/flutter/lib/locator.dart
- // as an example.
+class UnitModel extends NodeModel {
+ final String id;
+
+ UnitModel.fromResponse(UnitResponseModel unit)
+ : id = unit.id,
+ super(title: unit.title);
}
diff --git a/learning/tour-of-beam/frontend/lib/pages/tour/playground_demo.dart b/learning/tour-of-beam/frontend/lib/pages/tour/playground_demo.dart
index 384d46b0fc2..6f44223f189 100644
--- a/learning/tour-of-beam/frontend/lib/pages/tour/playground_demo.dart
+++ b/learning/tour-of-beam/frontend/lib/pages/tour/playground_demo.dart
@@ -20,18 +20,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:playground_components/playground_components.dart';
-// This is for demo only. Need a thought-through import in production.
-
-const String kApiClientURL =
- 'https://backend-router-beta-dot-apache-beam-testing.appspot.com';
-const String kApiJavaClientURL =
- 'https://backend-java-beta-dot-apache-beam-testing.appspot.com';
-const String kApiGoClientURL =
- 'https://backend-go-beta-dot-apache-beam-testing.appspot.com';
-const String kApiPythonClientURL =
- 'https://backend-python-beta-dot-apache-beam-testing.appspot.com';
-const String kApiScioClientURL =
- 'https://backend-scio-beta-dot-apache-beam-testing.appspot.com';
+import '../../config.dart';
class PlaygroundDemoWidget extends StatefulWidget {
const PlaygroundDemoWidget({super.key});
diff --git a/learning/tour-of-beam/frontend/lib/pages/tour/screen.dart b/learning/tour-of-beam/frontend/lib/pages/tour/screen.dart
index 7d708ff2508..3a20b5a6786 100644
--- a/learning/tour-of-beam/frontend/lib/pages/tour/screen.dart
+++ b/learning/tour-of-beam/frontend/lib/pages/tour/screen.dart
@@ -21,11 +21,16 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:playground_components/playground_components.dart';
+import '../../components/builders/content_tree.dart';
import '../../components/expansion_tile_wrapper.dart';
import '../../components/filler_text.dart';
import '../../components/scaffold.dart';
import '../../constants/sizes.dart';
import '../../generated/assets.gen.dart';
+import '../../models/abstract_node.dart';
+import '../../models/group.dart';
+import '../../models/module.dart';
+import '../../models/unit.dart';
import 'playground_demo.dart';
class TourScreen extends StatelessWidget {
@@ -99,33 +104,40 @@ class _ContentTree extends StatelessWidget {
return Container(
width: 250,
padding: const EdgeInsets.symmetric(horizontal: BeamSizes.size12),
- child: SingleChildScrollView(
- child: Column(
- children: [
- const _ContentTreeTitle(),
- ...[
- 'Core Transforms',
- 'Common Transforms',
- ].map((e) => _Module(module: e)).toList(growable: false),
- const SizedBox(height: BeamSizes.size12),
- ],
- ),
+ child: ContentTreeBuilder(
+ builder: (context, contentTree, child) {
+ if (contentTree == null) {
+ return Container();
+ }
+
+ return SingleChildScrollView(
+ child: Column(
+ children: [
+ const _ContentTreeTitle(),
+ ...contentTree.modules
+ .map((module) => _Module(module: module))
+ .toList(growable: false),
+ const SizedBox(height: BeamSizes.size12),
+ ],
+ ),
+ );
+ },
),
);
}
}
class _Module extends StatelessWidget {
- final String module;
+ final ModuleModel module;
const _Module({required this.module});
@override
Widget build(BuildContext context) {
return Column(
children: [
- _ModuleTitle(title: module),
- ...['Map', 'Combine']
- .map((group) => _Group(group: group))
+ _ModuleTitle(module: module),
+ ...module.nodes
+ .map((node) => _Node(node: node))
.toList(growable: false),
const BeamDivider(
margin: EdgeInsets.symmetric(vertical: BeamSizes.size10),
@@ -156,8 +168,8 @@ class _ContentTreeTitle extends StatelessWidget {
}
class _ModuleTitle extends StatelessWidget {
- final String title;
- const _ModuleTitle({required this.title});
+ final ModuleModel module;
+ const _ModuleTitle({required this.module});
@override
Widget build(BuildContext context) {
@@ -167,12 +179,12 @@ class _ModuleTitle extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
- title,
+ module.title,
style: Theme.of(context).textTheme.headlineMedium,
),
- const Padding(
- padding: EdgeInsets.only(right: BeamSizes.size4),
- child: ComplexityWidget(complexity: Complexity.basic),
+ Padding(
+ padding: const EdgeInsets.only(right: BeamSizes.size4),
+ child: ComplexityWidget(complexity: module.complexity),
),
],
),
@@ -180,8 +192,23 @@ class _ModuleTitle extends StatelessWidget {
}
}
+class _Node extends StatelessWidget {
+ final NodeModel node;
+ const _Node({required this.node});
+
+ @override
+ Widget build(BuildContext context) {
+ if (node is GroupModel) {
+ return _Group(group: node as GroupModel);
+ } else if (node is UnitModel) {
+ return _Unit(unit: node as UnitModel);
+ }
+ throw Exception('A node with an unknown type');
+ }
+}
+
class _Group extends StatelessWidget {
- final String group;
+ final GroupModel group;
const _Group({required this.group});
@override
@@ -189,44 +216,40 @@ class _Group extends StatelessWidget {
return ExpansionTileWrapper(
ExpansionTile(
tilePadding: EdgeInsets.zero,
- title: _GroupTitle(title: group),
+ title: _GroupTitle(title: group.title),
childrenPadding: const EdgeInsets.only(
left: BeamSizes.size24,
- top: BeamSizes.size10,
),
- children: const [_Units()],
+ children: [_GroupNodes(nodes: group.nodes)],
),
);
}
}
-class _Units extends StatelessWidget {
- const _Units();
+class _GroupNodes extends StatelessWidget {
+ final List<NodeModel> nodes;
+ const _GroupNodes({required this.nodes});
@override
Widget build(BuildContext context) {
return Column(
- children: ['ParDo one-to-one', 'ParDo one-to-many']
- .map((e) => _Unit(title: e))
- .toList(growable: false),
+ children: nodes.map((node) => _Node(node: node)).toList(growable: false),
);
}
}
class _Unit extends StatelessWidget {
- final String title;
- const _Unit({required this.title});
+ final UnitModel unit;
+ const _Unit({required this.unit});
@override
Widget build(BuildContext context) {
return Padding(
- padding: const EdgeInsets.only(bottom: BeamSizes.size18),
+ padding: const EdgeInsets.symmetric(vertical: BeamSizes.size10),
child: Row(
children: [
- _ProgressIndicator(
- assetPath: Assets.svg.unitProgress100,
- ),
- Text(title),
+ _ProgressIndicator(assetPath: Assets.svg.unitProgress0),
+ Expanded(child: Text(unit.title)),
],
),
);
@@ -241,9 +264,7 @@ class _GroupTitle extends StatelessWidget {
Widget build(BuildContext context) {
return Row(
children: [
- _ProgressIndicator(
- assetPath: Assets.svg.unitProgress100,
- ),
+ _ProgressIndicator(assetPath: Assets.svg.unitProgress0),
Text(
title,
style: Theme.of(context).textTheme.headlineMedium,
diff --git a/learning/tour-of-beam/frontend/lib/pages/welcome/screen.dart b/learning/tour-of-beam/frontend/lib/pages/welcome/screen.dart
index ae799b7e77c..25e2fbb65cb 100644
--- a/learning/tour-of-beam/frontend/lib/pages/welcome/screen.dart
+++ b/learning/tour-of-beam/frontend/lib/pages/welcome/screen.dart
@@ -22,10 +22,14 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:playground_components/playground_components.dart';
+import '../../components/builders/content_tree.dart';
+import '../../components/builders/sdks_builder.dart';
import '../../components/filler_text.dart';
import '../../components/scaffold.dart';
import '../../constants/sizes.dart';
import '../../generated/assets.gen.dart';
+import '../../models/module.dart';
+import '../../models/sdk.dart';
class WelcomeScreen extends StatelessWidget {
const WelcomeScreen();
@@ -106,10 +110,18 @@ class _SdkSelection extends StatelessWidget {
padding: const EdgeInsets.fromLTRB(50, 60, 50, 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
- children: const [
- _IntroText(),
- SizedBox(height: BeamSizes.size32),
- _Buttons(),
+ children: [
+ const _IntroText(),
+ const SizedBox(height: BeamSizes.size32),
+ SdksBuilder(
+ builder: (context, sdks, child) {
+ if (sdks.isEmpty) {
+ return Container();
+ }
+
+ return _Buttons(sdks: sdks);
+ },
+ ),
],
),
),
@@ -129,26 +141,26 @@ class _TourSummary extends StatelessWidget {
vertical: BeamSizes.size20,
horizontal: 27,
),
- child: Column(
- children: _modules
- .map(
- (module) => _Module(
- title: module,
- isLast: module == _modules.last,
- ),
- )
- .toList(growable: false),
+ child: ContentTreeBuilder(
+ builder: (context, contentTree, child) {
+ if (contentTree == null) {
+ return Container();
+ }
+
+ return Column(
+ children: contentTree.modules
+ .map(
+ (module) => _Module(
+ module: module,
+ isLast: module == contentTree.modules.last,
+ ),
+ )
+ .toList(growable: false),
+ );
+ },
),
);
}
-
- static const List<String> _modules = [
- 'Core Transforms',
- 'Common Transforms',
- 'IO',
- 'Windowing',
- 'Triggers',
- ];
}
class _IntroText extends StatelessWidget {
@@ -199,7 +211,8 @@ class _IntroText extends StatelessWidget {
}
class _Buttons extends StatelessWidget {
- const _Buttons();
+ final List<SdkModel> sdks;
+ const _Buttons({required this.sdks});
void _onSdkChanged(String value) {
// TODO(nausharipov): change sdk
@@ -210,10 +223,11 @@ class _Buttons extends StatelessWidget {
return Wrap(
children: [
Wrap(
- children: ['Java', 'Python', 'Go']
+ children: sdks
.map(
- (e) => _SdkButton(
- value: e,
+ (sdk) => _SdkButton(
+ title: sdk.title,
+ value: sdk.id,
groupValue: _sdk,
onChanged: _onSdkChanged,
),
@@ -230,15 +244,17 @@ class _Buttons extends StatelessWidget {
);
}
- static const String _sdk = 'Java';
+ static const String _sdk = 'java';
}
class _SdkButton extends StatelessWidget {
+ final String title;
final String value;
final String groupValue;
final ValueChanged<String> onChanged;
const _SdkButton({
+ required this.title,
required this.value,
required this.groupValue,
required this.onChanged,
@@ -258,18 +274,18 @@ class _SdkButton extends StatelessWidget {
onPressed: () {
onChanged(value);
},
- child: Text(value),
+ child: Text(title),
),
);
}
}
class _Module extends StatelessWidget {
- final String title;
+ final ModuleModel module;
final bool isLast;
const _Module({
- required this.title,
+ required this.module,
required this.isLast,
});
@@ -277,7 +293,7 @@ class _Module extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
children: [
- _ModuleHeader(title: title),
+ _ModuleHeader(title: module.title),
if (isLast) const _LastModuleBody() else const _ModuleBody(),
],
);
diff --git a/playground/frontend/playground_components/lib/src/enums/complexity.dart b/learning/tour-of-beam/frontend/lib/repositories/client/client.dart
similarity index 80%
copy from playground/frontend/playground_components/lib/src/enums/complexity.dart
copy to learning/tour-of-beam/frontend/lib/repositories/client/client.dart
index 79767efa9b9..455488abbc2 100644
--- a/playground/frontend/playground_components/lib/src/enums/complexity.dart
+++ b/learning/tour-of-beam/frontend/lib/repositories/client/client.dart
@@ -16,8 +16,11 @@
* limitations under the License.
*/
-enum Complexity {
- basic,
- medium,
- advanced,
+import '../../models/content_tree.dart';
+import '../models/get_sdks_response.dart';
+
+abstract class TobClient {
+ Future<ContentTreeModel> getContentTree();
+
+ Future<GetSdksResponse> getSdks();
}
diff --git a/learning/tour-of-beam/frontend/lib/repositories/client/cloud_functions_client.dart b/learning/tour-of-beam/frontend/lib/repositories/client/cloud_functions_client.dart
new file mode 100644
index 00000000000..d062dfd97e2
--- /dev/null
+++ b/learning/tour-of-beam/frontend/lib/repositories/client/cloud_functions_client.dart
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+import 'dart:convert';
+
+import 'package:http/http.dart' as http;
+
+import '../../config.dart';
+import '../../models/content_tree.dart';
+import '../models/get_sdks_response.dart';
+import 'client.dart';
+
+class CloudFunctionsTobClient extends TobClient {
+ @override
+ Future<GetSdksResponse> getSdks() async {
+ final json = await http.get(
+ Uri.parse(
+ '$cloudFunctionsBaseUrl/getSdkList',
+ ),
+ );
+
+ final map = jsonDecode(utf8.decode(json.bodyBytes)) as Map<String, dynamic>;
+ return GetSdksResponse.fromJson(map);
+ }
+
+ @override
+ Future<ContentTreeModel> getContentTree() async {
+ final json = await http.get(
+ Uri.parse(
+ '$cloudFunctionsBaseUrl/getContentTree?sdk=Python',
+ ),
+ );
+ final map = jsonDecode(utf8.decode(json.bodyBytes)) as Map<String, dynamic>;
+ return ContentTreeModel.fromJson(map);
+ }
+}
diff --git a/learning/tour-of-beam/frontend/lib/locator.dart b/learning/tour-of-beam/frontend/lib/repositories/models/get_sdks_response.dart
similarity index 68%
copy from learning/tour-of-beam/frontend/lib/locator.dart
copy to learning/tour-of-beam/frontend/lib/repositories/models/get_sdks_response.dart
index 8ab88a830d0..c7b5af64657 100644
--- a/learning/tour-of-beam/frontend/lib/locator.dart
+++ b/learning/tour-of-beam/frontend/lib/repositories/models/get_sdks_response.dart
@@ -16,9 +16,18 @@
* limitations under the License.
*/
-//import 'package:get_it/get_it.dart';
+import 'package:json_annotation/json_annotation.dart';
-Future<void> initializeServiceLocator() async {
- // See https://github.com/alexeyinkin/mefolio-standalone/blob/main/flutter/lib/locator.dart
- // as an example.
+import '../../models/sdk.dart';
+
+part 'get_sdks_response.g.dart';
+
+@JsonSerializable(createToJson: false)
+class GetSdksResponse {
+ final List<SdkModel> sdks;
+
+ const GetSdksResponse({required this.sdks});
+
+ factory GetSdksResponse.fromJson(Map<String, dynamic> json) =>
+ _$GetSdksResponseFromJson(json);
}
diff --git a/learning/tour-of-beam/frontend/lib/locator.dart b/learning/tour-of-beam/frontend/lib/repositories/models/group.dart
similarity index 65%
copy from learning/tour-of-beam/frontend/lib/locator.dart
copy to learning/tour-of-beam/frontend/lib/repositories/models/group.dart
index 8ab88a830d0..58705112a8b 100644
--- a/learning/tour-of-beam/frontend/lib/locator.dart
+++ b/learning/tour-of-beam/frontend/lib/repositories/models/group.dart
@@ -16,9 +16,22 @@
* limitations under the License.
*/
-//import 'package:get_it/get_it.dart';
+import 'package:json_annotation/json_annotation.dart';
-Future<void> initializeServiceLocator() async {
- // See https://github.com/alexeyinkin/mefolio-standalone/blob/main/flutter/lib/locator.dart
- // as an example.
+import 'node.dart';
+
+part 'group.g.dart';
+
+@JsonSerializable(createToJson: false)
+class GroupResponseModel {
+ final String title;
+ final List<NodeResponseModel> nodes;
+
+ const GroupResponseModel({
+ required this.title,
+ required this.nodes,
+ });
+
+ factory GroupResponseModel.fromJson(Map<String, dynamic> json) =>
+ _$GroupResponseModelFromJson(json);
}
diff --git a/learning/tour-of-beam/frontend/lib/locator.dart b/learning/tour-of-beam/frontend/lib/repositories/models/node.dart
similarity index 60%
copy from learning/tour-of-beam/frontend/lib/locator.dart
copy to learning/tour-of-beam/frontend/lib/repositories/models/node.dart
index 8ab88a830d0..38f23f819b4 100644
--- a/learning/tour-of-beam/frontend/lib/locator.dart
+++ b/learning/tour-of-beam/frontend/lib/repositories/models/node.dart
@@ -16,9 +16,26 @@
* limitations under the License.
*/
-//import 'package:get_it/get_it.dart';
+import 'package:json_annotation/json_annotation.dart';
-Future<void> initializeServiceLocator() async {
- // See https://github.com/alexeyinkin/mefolio-standalone/blob/main/flutter/lib/locator.dart
- // as an example.
+import 'group.dart';
+import 'node_type_enum.dart';
+import 'unit.dart';
+
+part 'node.g.dart';
+
+@JsonSerializable(createToJson: false)
+class NodeResponseModel {
+ final NodeType type;
+ final UnitResponseModel? unit;
+ final GroupResponseModel? group;
+
+ const NodeResponseModel({
+ required this.type,
+ required this.unit,
+ required this.group,
+ });
+
+ factory NodeResponseModel.fromJson(Map<String, dynamic> json) =>
+ _$NodeResponseModelFromJson(json);
}
diff --git a/playground/frontend/playground_components/lib/src/enums/complexity.dart b/learning/tour-of-beam/frontend/lib/repositories/models/node_type_enum.dart
similarity index 94%
copy from playground/frontend/playground_components/lib/src/enums/complexity.dart
copy to learning/tour-of-beam/frontend/lib/repositories/models/node_type_enum.dart
index 79767efa9b9..602635d560b 100644
--- a/playground/frontend/playground_components/lib/src/enums/complexity.dart
+++ b/learning/tour-of-beam/frontend/lib/repositories/models/node_type_enum.dart
@@ -16,8 +16,7 @@
* limitations under the License.
*/
-enum Complexity {
- basic,
- medium,
- advanced,
+enum NodeType {
+ group,
+ unit,
}
diff --git a/learning/tour-of-beam/frontend/lib/locator.dart b/learning/tour-of-beam/frontend/lib/repositories/models/unit.dart
similarity index 68%
copy from learning/tour-of-beam/frontend/lib/locator.dart
copy to learning/tour-of-beam/frontend/lib/repositories/models/unit.dart
index 8ab88a830d0..eeb3c1ffce0 100644
--- a/learning/tour-of-beam/frontend/lib/locator.dart
+++ b/learning/tour-of-beam/frontend/lib/repositories/models/unit.dart
@@ -16,9 +16,20 @@
* limitations under the License.
*/
-//import 'package:get_it/get_it.dart';
+import 'package:json_annotation/json_annotation.dart';
-Future<void> initializeServiceLocator() async {
- // See https://github.com/alexeyinkin/mefolio-standalone/blob/main/flutter/lib/locator.dart
- // as an example.
+part 'unit.g.dart';
+
+@JsonSerializable(createToJson: false)
+class UnitResponseModel {
+ final String id;
+ final String title;
+
+ const UnitResponseModel({
+ required this.id,
+ required this.title,
+ });
+
+ factory UnitResponseModel.fromJson(Map<String, dynamic> json) =>
+ _$UnitResponseModelFromJson(json);
}
diff --git a/learning/tour-of-beam/frontend/pubspec.lock b/learning/tour-of-beam/frontend/pubspec.lock
index c59bb0affd6..b132dbc7564 100644
--- a/learning/tour-of-beam/frontend/pubspec.lock
+++ b/learning/tour-of-beam/frontend/pubspec.lock
@@ -221,7 +221,7 @@ packages:
source: hosted
version: "0.0.2"
equatable:
- dependency: transitive
+ dependency: "direct dev"
description:
name: equatable
url: "https://pub.dartlang.org"
@@ -370,12 +370,12 @@ packages:
source: hosted
version: "0.7.0"
http:
- dependency: transitive
+ dependency: "direct main"
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
- version: "0.13.4"
+ version: "0.13.5"
http2:
dependency: transitive
description:
@@ -424,12 +424,19 @@ packages:
source: hosted
version: "0.6.4"
json_annotation:
- dependency: transitive
+ dependency: "direct main"
description:
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
- version: "4.6.0"
+ version: "4.7.0"
+ json_serializable:
+ dependency: "direct dev"
+ description:
+ name: json_serializable
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "6.4.0"
linked_scroll_controller:
dependency: transitive
description:
@@ -701,6 +708,20 @@ packages:
description: flutter
source: sdk
version: "0.0.99"
+ source_gen:
+ dependency: transitive
+ description:
+ name: source_gen
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.2.5"
+ source_helper:
+ dependency: transitive
+ description:
+ name: source_helper
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.3.3"
source_span:
dependency: transitive
description:
diff --git a/learning/tour-of-beam/frontend/pubspec.yaml b/learning/tour-of-beam/frontend/pubspec.yaml
index fb9ee46307d..ea86db0e0c0 100644
--- a/learning/tour-of-beam/frontend/pubspec.yaml
+++ b/learning/tour-of-beam/frontend/pubspec.yaml
@@ -23,8 +23,8 @@ publish_to: 'none'
version: 0.1.0
environment:
- sdk: ">=2.18.1 <3.0.0"
- flutter: ">=3.3.2"
+ sdk: '>=2.18.1 <3.0.0'
+ flutter: '>=3.3.2'
dependencies:
code_text_field:
@@ -38,6 +38,8 @@ dependencies:
flutter_svg: ^1.0.3
get_it: ^7.2.0
google_fonts: ^3.0.1
+ http: ^0.13.5
+ json_annotation: ^4.7.0
playground_components: { path: ../../../playground/frontend/playground_components }
provider: ^6.0.3
shared_preferences: ^2.0.15
@@ -46,9 +48,11 @@ dependencies:
dev_dependencies:
build_runner: ^2.2.0
+ equatable: ^2.0.5
flutter_gen_runner: ^4.3.0
flutter_test: { sdk: flutter }
integration_test: { sdk: flutter }
+ json_serializable: ^6.4.0
total_lints: ^2.17.0
flutter:
diff --git a/playground/frontend/playground_components/lib/src/enums/complexity.dart b/playground/frontend/playground_components/lib/src/enums/complexity.dart
index 79767efa9b9..f43ffae19fe 100644
--- a/playground/frontend/playground_components/lib/src/enums/complexity.dart
+++ b/playground/frontend/playground_components/lib/src/enums/complexity.dart
@@ -16,8 +16,13 @@
* limitations under the License.
*/
+import 'package:json_annotation/json_annotation.dart';
+
enum Complexity {
+ @JsonValue('BASIC')
basic,
+ @JsonValue('MEDIUM')
medium,
+ @JsonValue('ADVANCED')
advanced,
}