Skip to content

Commit

Permalink
Merge pull request #482 from DataRecce/feature/drc-872-refine-show-hu…
Browse files Browse the repository at this point in the history
…man-readable-message-when-recce-server-cant

[Feature] Add required artifects verification
  • Loading branch information
wcchang1115 authored Nov 13, 2024
2 parents a276929 + 8055749 commit a466787
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 9 deletions.
9 changes: 3 additions & 6 deletions recce/adapter/dbt_adapter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import uuid
from contextlib import contextmanager
from dataclasses import dataclass, fields
from errno import ENOENT
from pathlib import Path
from typing import Callable, Dict, List, Optional, Tuple, Iterator, Any, Set, Union

Expand Down Expand Up @@ -245,10 +246,6 @@ def load(cls,
# Load the artifacts from the state file or dbt target and dbt base directory
if not no_artifacts and not review:
dbt_adapter.load_artifacts()
if not dbt_adapter.curr_manifest:
raise Exception(f'Cannot load "{runtime_config.target_path}/manifest.json"')
if not dbt_adapter.base_manifest:
raise Exception(f'Cannot load "{target_base_path}/manifest.json"')
return dbt_adapter

def print_lineage_info(self):
Expand Down Expand Up @@ -340,11 +337,11 @@ def load_artifacts(self):
path = os.path.join(project_root, target_path, 'manifest.json')
curr_manifest = load_manifest(path=path)
if curr_manifest is None:
raise Exception(f'Cannot load the current manifest: {path}')
raise FileNotFoundError(ENOENT, os.strerror(ENOENT), path)
path = os.path.join(project_root, target_base_path, 'manifest.json')
base_manifest = load_manifest(path=path)
if base_manifest is None:
raise Exception(f'Cannot load the base manifest: {path}')
raise FileNotFoundError(ENOENT, os.strerror(ENOENT), path)

curr_catalog = load_catalog(path=os.path.join(project_root, target_path, 'catalog.json'))
base_catalog = load_catalog(path=os.path.join(project_root, target_base_path, 'catalog.json'))
Expand Down
10 changes: 10 additions & 0 deletions recce/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,11 @@ def server(host, port, state_file=None, **kwargs):
else:
console.rule("Recce Server")

result, message = RecceContext.verify_required_artifacts(**kwargs)
if not result:
console.print(f"[[red]Error[/red]] {message}")
exit(1)

state = AppState(state_loader=state_loader, kwargs=kwargs, flag=flag)
app.state = state

Expand Down Expand Up @@ -327,6 +332,11 @@ def run(output, **kwargs):
console.print(f"{hint}")
exit(1)

result, message = RecceContext.verify_required_artifacts(**kwargs)
if not result:
console.print(f"[[red]Error[/red]] {message}")
exit(1)

# Verify the output state file path
try:
if os.path.isdir(output) or output.endswith('/'):
Expand Down
15 changes: 14 additions & 1 deletion recce/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import logging
import os
from dataclasses import dataclass, field
from typing import Callable, Dict, Optional, List
from typing import Callable, Dict, Optional, List, Tuple

from recce.adapter.base import BaseAdapter
from recce.models import Check, Run
Expand Down Expand Up @@ -239,6 +239,19 @@ def mark_onboarding_completed(self):
# Skip the onboarding state for non-cloud mode
pass

@staticmethod
def verify_required_artifacts(**kwargs) -> Tuple[bool, Optional[str]]:
if kwargs.get('sqlmesh', False):
pass
else:
from recce.adapter.dbt_adapter import DbtAdapter
try:
DbtAdapter.load(**kwargs)
except FileNotFoundError as e:
return False, f"Cannot load the manifest: '{e.filename}'"

return True, None


recce_context: Optional[RecceContext] = None

Expand Down
10 changes: 8 additions & 2 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from click.testing import CliRunner

from recce.cli import server as cli_command_server
from recce.core import RecceContext
from recce.state import RecceStateLoader


Expand All @@ -21,9 +22,11 @@ def setUp(self):
self.runner = CliRunner()
pass

@patch.object(RecceContext, 'verify_required_artifacts')
@patch('recce.cli.uvicorn.run')
def test_cmd_server(self, mock_run):
def test_cmd_server(self, mock_run, mock_verify_required_artifacts):
from recce.server import app
mock_verify_required_artifacts.return_value = True, None
self.runner.invoke(cli_command_server, ['--host', 'unittest', '--port', 5566])
mock_run.assert_called_once_with(app, host='unittest', port=5566, lifespan='on')

Expand All @@ -39,14 +42,17 @@ def test_cmd_server_with_cloud_without_token(self, mock_run):
result = self.runner.invoke(cli_command_server, ['--cloud', '--password', 'unittest'])
assert result.exit_code == 1

@patch.object(RecceContext, 'verify_required_artifacts')
@patch('recce.util.recce_cloud.get_recce_cloud_onboarding_state')
@patch('recce.cli.uvicorn.run')
@patch('recce.cli.RecceStateLoader')
def test_cmd_server_with_cloud(self, mock_state_loader_class, mock_run, mock_get_recce_cloud_onboarding_state):
def test_cmd_server_with_cloud(self, mock_state_loader_class, mock_run, mock_get_recce_cloud_onboarding_state,
mock_verify_required_artifacts):
mock_state_loader = MagicMock(spec=RecceStateLoader)
mock_state_loader.verify.return_value = True
mock_state_loader.review_mode = True
mock_get_recce_cloud_onboarding_state.return_value = 'completed'
mock_verify_required_artifacts.return_value = True, None

mock_state_loader_class.return_value = mock_state_loader
self.runner.invoke(cli_command_server, ['--cloud', '--password', 'unittest', '--cloud-token', 'unittest'])
Expand Down

0 comments on commit a466787

Please sign in to comment.