Skip to content

Commit

Permalink
APP-4171 - [DEPS] Updated deps command to add a link to lib_latest fo…
Browse files Browse the repository at this point in the history
…r current Python version for App Builder

APP-4172 - [CLI] Minor enhancement to output of multiple commands
APP-4773 - [SUBMODULE] Minor update to config submodule
  • Loading branch information
bsummers-tc committed Sep 17, 2023
1 parent b1f6f9c commit 6719d85
Show file tree
Hide file tree
Showing 14 changed files with 96 additions and 48 deletions.
10 changes: 8 additions & 2 deletions release_notes.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Release Notes

### 1.0.1
## 1.0.2

- APP-4171 - [DEPS] Updated deps command to add a link to lib_latest for current Python version for App Builder
- APP-4172 - [CLI] Minor enhancement to output of multiple commands
- APP-4773 - [SUBMODULE] Minor update to config submodule

## 1.0.1

- APP-3915 - [CONFIG] Added validation to ensure displayPath is always in the install.json for API Services
- APP-4060 - [CLI] Updated proxy inputs to use environment variables
Expand All @@ -9,7 +15,7 @@
- APP-4113 - [CONFIG] Updated App Spec model to normalize App features


### 1.0.0
## 1.0.0

- APP-3926 - Split CLI module of TcEx into tcex-cli project
- APP-3912 - [CLI] Updated `tcex` command to use "project.scripts" setting in pyproject.toml
Expand Down
2 changes: 1 addition & 1 deletion tcex_cli/__metadata__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""TcEx Framework metadata."""
__license__ = 'Apache-2.0'
__version__ = '1.0.1'
__version__ = '1.0.2'
2 changes: 1 addition & 1 deletion tcex_cli/app/config
5 changes: 4 additions & 1 deletion tcex_cli/cli/deploy/deploy_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,11 @@ def deploy_app(self):
except Exception as err:
Render.panel.failure(f'Failed Deploying App: {err}')

if not response.ok:
# TC will respond with a 200 even if the deploy fails with content of "[]"
if not response.ok or response.text in ('[]', None):
reason = response.text or response.reason
if response.text == '[]':
reason = 'TC responded with an empty array ([]), which indicates a failure.'
Render.table.key_value(
'Failed To Deploy App',
{
Expand Down
57 changes: 22 additions & 35 deletions tcex_cli/cli/deps/deps_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import subprocess # nosec
import sys
from functools import cached_property
from importlib.metadata import version as get_version
from pathlib import Path
from urllib.parse import quote

Expand Down Expand Up @@ -154,36 +155,6 @@ def create_requirements_lock(self, contents: str, requirements_file: Path):
fh.write(contents)
fh.write('')

# def create_temp_requirements(self):
# """Create a temporary requirements.txt.

# This allows testing against a git branch instead of pulling from pypi.
# """
# _requirements_fqfn = Path('requirements.txt')
# if self.has_requirements_lock:
# _requirements_fqfn = Path('requirements.lock')

# # Replace tcex version with develop branch of tcex
# with _requirements_fqfn.open(encoding='utf-8') as fh:
# current_requirements = fh.read().strip().split('\n')

# self.requirements_fqfn_branch = Path(f'temp-{_requirements_fqfn}')
# with self.requirements_fqfn_branch.open(mode='w', encoding='utf-8') as fh:
# requirements = []
# for line in current_requirements:
# if not line:
# continue
# if line.startswith('tcex'):
# line = (
# 'git+https://github.com/ThreatConnect-Inc/tcex.git@'
# f'{self.branch}#egg=tcex'
# )
# requirements.append(line)
# fh.write('\n'.join(requirements))

# # display branch setting
# self.output.append(KeyValueModel(key='Using Branch', value=self.branch))

def download_deps(self, exe_command: list[str]):
"""Download the dependencies (run pip)."""
# recommended -> https://pip.pypa.io/en/latest/user_guide/#using-pip-from-your-program
Expand Down Expand Up @@ -218,11 +189,6 @@ def install_deps(self):
# support temp (branch) requirements.txt file
exe_command = self._build_command(self.deps_dir, self.requirements_fqfn)

# display tcex version
self.output.append(
KeyValueModel(key='App TcEx Version', value=str(self.app.ij.model.sdk_version))
)

# display command setting
self.output.append(KeyValueModel(key='Pip Command', value=f'''{' '.join(exe_command)}'''))

Expand All @@ -245,6 +211,27 @@ def install_deps(self):
contents = self.requirements_lock_contents(self.deps_dir)
self.create_requirements_lock(contents, self.requirements_lock)

if self.app_builder is True and self.app.ij.model.sdk_version < Version('4.0.0'):
# the lib_version directory
python_version = self.target_python_version or '3.6.15'
lib_version = Path(f'lib_{python_version}')

# remove previous build director
if lib_version.is_symlink():
lib_version.unlink()
elif lib_version.is_dir():
shutil.rmtree(lib_version)

# create symlink: lib_latest -> lib_version
lib_version.symlink_to(self.deps_dir, target_is_directory=True)

# display tcex version
try:
self.output.append(KeyValueModel(key='App TcEx Version', value=get_version('tcex')))
except Exception: # nosec
# best effort to log tcex version
pass

def install_deps_tests(self):
"""Install tests dependencies."""
if self.requirements_txt_tests.exists():
Expand Down
6 changes: 5 additions & 1 deletion tcex_cli/cli/run/launch_service_common_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ def keyboard_listener_get_key(self):

def keyboard_listener_reset(self):
"""Reset the keyboard"""
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.stored_keyboard_settings)
try:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.stored_keyboard_settings)
except Exception: # nosec
# ignore errors for now
pass

def live_data_commands(self):
"""Display live data."""
Expand Down
2 changes: 1 addition & 1 deletion tcex_cli/cli/run/launch_service_webhook_trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ def live_data_display(self):

def live_data_header(self) -> Panel:
"""Display live header."""
panel_data = []
if self.model.trigger_inputs:
panel_data = []
for trigger_id, _ in enumerate(self.model.trigger_inputs):
panel_data.append(
f'Running server: [{self.accent}]http://{self.model.inputs.api_service_host}'
Expand Down
18 changes: 18 additions & 0 deletions tcex_cli/cli/run/run_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from tcex_cli.cli.run.launch_service_api import LaunchServiceApi
from tcex_cli.cli.run.launch_service_custom_trigger import LaunchServiceCustomTrigger
from tcex_cli.cli.run.launch_service_webhook_trigger import LaunchServiceWebhookTrigger
from tcex_cli.cli.run.model.app_api_service_model import AppApiServiceModel
from tcex_cli.cli.run.model.app_webhook_trigger_service_model import AppWebhookTriggerServiceModel
from tcex_cli.render.render import Render


Expand All @@ -28,6 +30,20 @@ def __init__(self):
# validate in App directory
self._validate_in_app_directory()

def _display_api_settings(self, api_inputs: AppApiServiceModel | AppWebhookTriggerServiceModel):
"""Display API settings."""
Render.panel.info(
(
'Current API Service Settings:\n'
f'host: [{self.accent}]{api_inputs.api_service_host}[/{self.accent}]\n'
f'port: [{self.accent}]{api_inputs.api_service_port}[/{self.accent}]\n\n'
'API default settings can be overridden with these environment variables:\n'
f' - [{self.accent}]API_SERVICE_HOST[/{self.accent}]\n'
f' - [{self.accent}]API_SERVICE_PORT[/{self.accent}]'
),
'API Settings',
)

def _validate_in_app_directory(self):
"""Return True if in App directory."""
if not Path('app.py').is_file() or not Path('run.py').is_file():
Expand Down Expand Up @@ -58,6 +74,7 @@ def run(self, config_json: Path):
case 'apiservice':
Render.panel.info('Launching API Service', f'[{self.panel_title}]Running App[/]')
launch_app = LaunchServiceApi(config_json)
self._display_api_settings(launch_app.model.inputs)
launch_app.setup()
exit_code = launch_app.launch()
self.exit_cli(exit_code)
Expand Down Expand Up @@ -101,6 +118,7 @@ def run(self, config_json: Path):
'Launching Webhook Trigger Service', f'[{self.panel_title}]Running App[/]'
)
launch_app = LaunchServiceWebhookTrigger(config_json)
self._display_api_settings(launch_app.model.inputs)
launch_app.setup()
exit_code = launch_app.launch()
self.exit_cli(exit_code)
2 changes: 1 addition & 1 deletion tcex_cli/cli/spec_tool/gen_app_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def __init__(self):
self.input_static = GenAppInputStatic()
self.log = _logger
self.typing_modules = set()
self.pydantic_modules = {'BaseModel'}
self.pydantic_modules = set()
self.report_mismatch = []

def _add_action_classes(self):
Expand Down
2 changes: 1 addition & 1 deletion tcex_cli/cli/spec_tool/gen_app_input_static.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def template_app_imports(

# add import for service trigger Apps
if self.ij.model.is_trigger_app:
_imports.append('from tcex.input.model import CreateConfigModel')
_imports.append('from tcex.input.model.create_config_model import CreateConfigModel')

# add new lines
_imports.extend(
Expand Down
1 change: 1 addition & 0 deletions tcex_cli/cli/spec_tool/gen_app_spec_yml.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def _add_standard_fields(self, app_spec_yml_data: dict):
'programMain': self.app.ij.model.program_main,
'programVersion': str(self.app.ij.model.program_version),
'runtimeLevel': self.app.ij.model.runtime_level,
'schemaVersion': '1.1.0',
'sdkVersion': self.app.ij.model.sdk_version,
}
)
Expand Down
3 changes: 3 additions & 0 deletions tcex_cli/cli/spec_tool/spec_tool_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ def generate_app_spec(self):

self.write_app_file(gen.filename, f'{config}\n')

# for reload/rewrite/fix of app_spec.yml
self.asy.contents # pylint: disable=pointless-statement

def generate_install_json(self):
"""Generate the install.json file."""
gen = GenInstallJson(self.asy)
Expand Down
5 changes: 4 additions & 1 deletion tcex_cli/cli/template/template_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,11 @@ def download_template_file(self, item: FileMetadataModel):
if item.download_url is None:
return

# neither of the following options seem to work, but leaving here for future reference:
# - headers={'Cache-Control': 'no-cache'}
# - headers={'Cache-Control': 'max-age=0'}
r = self.session.get(
item.download_url, allow_redirects=True, headers={'Cache-Control': 'no-cache'}
item.download_url, allow_redirects=True, headers={'Cache-Control': 'max-age=0'}
)
if not r.ok:
self.log.error(
Expand Down
29 changes: 26 additions & 3 deletions tests/deps/test_tcex_deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ def teardown_method(self):
deps_dir = Path('deps')
shutil.rmtree(deps_dir, ignore_errors=True)

def _remove_proxy_env_vars(self):
"""Remove proxy env vars"""
os.environ.pop('TC_PROXY_HOST', None)
os.environ.pop('TC_PROXY_PORT', None)
os.environ.pop('TC_PROXY_USER', None)
os.environ.pop('TC_PROXY_USERNAME', None)
os.environ.pop('TC_PROXY_PASS', None)
os.environ.pop('TC_PROXY_PASSWORD', None)

def _run_command(
self,
args: list[str],
Expand All @@ -52,11 +61,17 @@ def _run_command(

def test_tcex_deps_std(self, monkeypatch: pytest.MonkeyPatch, request: FixtureRequest):
"""Test Case"""
# remove proxy env vars
self._remove_proxy_env_vars()

result = self._run_command(['deps'], 'app_std', monkeypatch, request)
assert result.exit_code == 0

def test_tcex_deps_branch(self, monkeypatch: pytest.MonkeyPatch, request: FixtureRequest):
"""Test Case"""
# remove proxy env vars
self._remove_proxy_env_vars()

branch = 'develop'
result = self._run_command(['deps', '--branch', branch], 'app_branch', monkeypatch, request)
assert result.exit_code == 0
Expand All @@ -71,12 +86,20 @@ def test_tcex_deps_branch(self, monkeypatch: pytest.MonkeyPatch, request: Fixtur
if 'Running' in line:
assert 'temp-requirements.txt' in line

def test_tcex_deps_proxy(self, monkeypatch: pytest.MonkeyPatch, request: FixtureRequest):
def test_tcex_deps_proxy_env(self, monkeypatch: pytest.MonkeyPatch, request: FixtureRequest):
"""Test Case"""
# proxy settings will be pulled from env vars
result = self._run_command(['deps'], 'app_std', monkeypatch, request)
assert result.exit_code == 0

def test_tcex_deps_proxy_explicit(
self, monkeypatch: pytest.MonkeyPatch, request: FixtureRequest
):
"""Test Case"""
proxy_host = os.getenv('TC_PROXY_HOST')
proxy_port = os.getenv('TC_PROXY_PORT')
proxy_user = os.getenv('TC_PROXY_USERNAME')
proxy_pass = os.getenv('TC_PROXY_PASSWORD')
proxy_user = os.getenv('TC_PROXY_USERNAME') or os.getenv('TC_PROXY_USER')
proxy_pass = os.getenv('TC_PROXY_PASSWORD') or os.getenv('TC_PROXY_PASS')

command = ['deps', '--proxy-host', proxy_host, '--proxy-port', proxy_port]
if proxy_user and proxy_pass:
Expand Down

0 comments on commit 6719d85

Please sign in to comment.