Skip to content

Commit 44ca9f5

Browse files
authored
feat: Allow Feast UI to be spun up with CLI command: feast ui (#2667)
Signed-off-by: Danny Chiao <[email protected]>
1 parent d4b0b1a commit 44ca9f5

29 files changed

+38843
-16
lines changed

.github/workflows/publish.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ jobs:
166166
os: [ ubuntu-latest, macos-10.15 ]
167167
steps:
168168
- uses: actions/checkout@v2
169+
- name: Setup Node
170+
uses: actions/setup-node@v2
171+
with:
172+
node-version: '17.x'
173+
registry-url: 'https://registry.npmjs.org'
169174
- name: Build wheels
170175
uses: pypa/[email protected]
171176
env:
@@ -183,6 +188,7 @@ jobs:
183188
make install-protoc-dependencies
184189
make install-go-proto-dependencies
185190
make install-go-ci-dependencies
191+
make build-ui
186192
187193
- uses: actions/upload-artifact@v2
188194
with:
@@ -202,12 +208,18 @@ jobs:
202208
with:
203209
python-version: "3.10"
204210
architecture: x64
205-
- name: Install dependencies
211+
- name: Setup Node
212+
uses: actions/setup-node@v2
213+
with:
214+
node-version: '17.x'
215+
registry-url: 'https://registry.npmjs.org'
216+
- name: Build and install dependencies
206217
run: |
207218
pip install -U pip setuptools wheel twine
208219
make install-protoc-dependencies
209220
make install-go-proto-dependencies
210221
make install-go-ci-dependencies
222+
make build-ui
211223
- name: Build
212224
run: |
213225
python3 setup.py sdist bdist_wheel

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ go/protos/
200200

201201

202202
# Feast UI dependencies
203+
sdk/python/feast/ui/node_modules
204+
sdk/python/feast/ui/build
203205
ui/node_modules
204206
ui/.pnp
205207
ui/.pnp.js

CONTRIBUTING.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,12 @@ source venv/bin/activate
8989
pip install --upgrade pip
9090
```
9191

92-
4. Install development dependencies for Feast Python SDK / CLI
92+
4. (Optional): Install Node & Yarn. Then run the following to build Feast UI artifacts for use in `feast ui`
93+
```
94+
make build-ui
95+
```
96+
97+
5Install development dependencies for Feast Python SDK / CLI
9398
```sh
9499
pip install -e ".[dev]"
95100
```

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,9 @@ build-sphinx: compile-protos-python
261261

262262
build-templates:
263263
python infra/scripts/compile-templates.py
264+
265+
# Web UI
266+
267+
# Note: requires node and yarn to be installed
268+
build-ui:
269+
cd $(ROOT_DIR)/sdk/python/feast/ui && yarn install && npm run build --omit=dev

sdk/python/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,4 @@ dmypy.json
115115
.pyre/
116116

117117
.vscode/*
118-
playground
118+
playground

sdk/python/feast/cli.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,19 @@ def version():
109109
print(f'Feast SDK Version: "{pkg_resources.get_distribution("feast")}"')
110110

111111

112+
@cli.command()
113+
@click.pass_context
114+
def ui(ctx: click.Context):
115+
"""
116+
Shows the Feast UI over the current directory
117+
"""
118+
repo = ctx.obj["CHDIR"]
119+
cli_check_repo(repo)
120+
store = FeatureStore(repo_path=str(repo))
121+
repo_config = load_repo_config(repo)
122+
store.serve_ui(registry_dump(repo_config, repo_path=repo))
123+
124+
112125
@cli.command()
113126
@click.pass_context
114127
def endpoint(ctx: click.Context):
@@ -475,7 +488,7 @@ def registry_dump_command(ctx: click.Context):
475488
cli_check_repo(repo)
476489
repo_config = load_repo_config(repo)
477490

478-
registry_dump(repo_config, repo_path=repo)
491+
click.echo(registry_dump(repo_config, repo_path=repo))
479492

480493

481494
@cli.command("materialize")

sdk/python/feast/feature_store.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
from google.protobuf.timestamp_pb2 import Timestamp
4040
from tqdm import tqdm
4141

42-
from feast import feature_server, flags, flags_helper, utils
42+
from feast import feature_server, flags, flags_helper, ui_server, utils
4343
from feast.base_feature_view import BaseFeatureView
4444
from feast.data_source import DataSource
4545
from feast.diff.infra_diff import InfraDiff, diff_infra_protos
@@ -1985,6 +1985,16 @@ def get_feature_server_endpoint(self) -> Optional[str]:
19851985
"""Returns endpoint for the feature server, if it exists."""
19861986
return self._provider.get_feature_server_endpoint()
19871987

1988+
@log_exceptions_and_usage
1989+
def serve_ui(self, registry_dump: str) -> None:
1990+
"""Start the UI server locally"""
1991+
warnings.warn(
1992+
"The Feast UI is an experimental feature. "
1993+
"We do not guarantee that future changes will maintain backward compatibility.",
1994+
RuntimeWarning,
1995+
)
1996+
ui_server.start_server(self, registry_dump, self.config.project)
1997+
19881998
@log_exceptions_and_usage
19891999
def serve_transformations(self, port: int) -> None:
19902000
"""Start the feature transformation server locally on a given port."""

sdk/python/feast/repo_operations.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,14 +281,13 @@ def teardown(repo_config: RepoConfig, repo_path: Path):
281281

282282

283283
@log_exceptions_and_usage
284-
def registry_dump(repo_config: RepoConfig, repo_path: Path):
284+
def registry_dump(repo_config: RepoConfig, repo_path: Path) -> str:
285285
"""For debugging only: output contents of the metadata registry"""
286286
registry_config = repo_config.get_registry_config()
287287
project = repo_config.project
288288
registry = Registry(registry_config=registry_config, repo_path=repo_path)
289289
registry_dict = registry.to_dict(project=project)
290-
291-
click.echo(json.dumps(registry_dict, indent=2, sort_keys=True))
290+
return json.dumps(registry_dict, indent=2, sort_keys=True)
292291

293292

294293
def cli_check_repo(repo_path: Path):

sdk/python/feast/ui/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Example Feast UI App
2+
3+
This is an example React App that imports the Feast UI module and relies on a "/projects-list" endpoint to get projects.
4+
5+
See the module import in `src/index.js`. The main change this implements on top of a vanilla create-react-app is adding:
6+
7+
```tsx
8+
import ReactDOM from "react-dom";
9+
import FeastUI from "@feast-dev/feast-ui";
10+
import "@feast-dev/feast-ui/dist/feast-ui.css";
11+
12+
ReactDOM.render(
13+
<React.StrictMode>
14+
<FeastUI
15+
feastUIConfigs={{
16+
projectListPromise: fetch("http://0.0.0.0:8888/projects-list", {
17+
headers: {
18+
"Content-Type": "application/json",
19+
},
20+
}).then((res) => {
21+
return res.json();
22+
})
23+
}}
24+
/>
25+
</React.StrictMode>,
26+
document.getElementById("root")
27+
);
28+
```
29+
30+
It is used by the `feast ui` command to scaffold a local UI server. The feast python package bundles in resources produced from `npm run build --omit=dev
31+
32+
33+
**Note**: yarn start will not work on this because of the above dependency.

0 commit comments

Comments
 (0)