From d23440a92e700ad6e816a4b795ef3f14f1128582 Mon Sep 17 00:00:00 2001 From: alafanechere Date: Thu, 6 Jan 2022 17:28:37 +0100 Subject: [PATCH 01/10] Implement list connectors --- octavia-cli/octavia_cli/entrypoint.py | 24 ++- octavia-cli/octavia_cli/list/__init__.py | 3 + octavia-cli/octavia_cli/list/commands.py | 48 ++++++ octavia-cli/octavia_cli/list/definitions.py | 130 +++++++++++++++ octavia-cli/unit_tests/test_entrypoint.py | 12 +- .../unit_tests/test_list/test_definitions.py | 149 ++++++++++++++++++ 6 files changed, 358 insertions(+), 8 deletions(-) create mode 100644 octavia-cli/octavia_cli/list/__init__.py create mode 100644 octavia-cli/octavia_cli/list/commands.py create mode 100644 octavia-cli/octavia_cli/list/definitions.py create mode 100644 octavia-cli/unit_tests/test_list/test_definitions.py diff --git a/octavia-cli/octavia_cli/entrypoint.py b/octavia-cli/octavia_cli/entrypoint.py index 3d82bc32f5ea3..82734ecf980ab 100644 --- a/octavia-cli/octavia_cli/entrypoint.py +++ b/octavia-cli/octavia_cli/entrypoint.py @@ -2,8 +2,15 @@ # Copyright (c) 2021 Airbyte, Inc., all rights reserved. # +from typing import List + +import airbyte_api_client import click +from .list import commands as list_commands + +AVAILABLE_COMMANDS: List[click.Command] = [list_commands._list] + @click.group() @click.option("--airbyte-url", envvar="AIRBYTE_URL", default="http://localhost:8000", help="The URL of your Airbyte instance.") @@ -12,13 +19,13 @@ def octavia(airbyte_url): click.secho(f"🐙 - Octavia is targetting your Airbyte instance running at {airbyte_url}") -@octavia.command(help="Scaffolds a local project directories.") -def init(): - raise click.ClickException("The init command is not yet implemented.") +def add_commands_to_octavia(): + for command in AVAILABLE_COMMANDS: + octavia.add_command(command) -@octavia.command(name="list", help="List existing resources on the Airbyte instance.") -def _list(): +@octavia.command(help="Scaffolds a local project directories.") +def init(): raise click.ClickException("The init command is not yet implemented.") @@ -38,5 +45,8 @@ def apply(): @octavia.command(help="Delete resources") -def delete(): - raise click.ClickException("The init command is not yet implemented.") +def delete() -> None: + raise click.ClickException("The delete command is not yet implemented.") + + +add_commands_to_octavia() diff --git a/octavia-cli/octavia_cli/list/__init__.py b/octavia-cli/octavia_cli/list/__init__.py new file mode 100644 index 0000000000000..46b7376756ec6 --- /dev/null +++ b/octavia-cli/octavia_cli/list/__init__.py @@ -0,0 +1,3 @@ +# +# Copyright (c) 2021 Airbyte, Inc., all rights reserved. +# diff --git a/octavia-cli/octavia_cli/list/commands.py b/octavia-cli/octavia_cli/list/commands.py new file mode 100644 index 0000000000000..1c79cee47feee --- /dev/null +++ b/octavia-cli/octavia_cli/list/commands.py @@ -0,0 +1,48 @@ +# +# Copyright (c) 2021 Airbyte, Inc., all rights reserved. +# + +from typing import List + +import click + +from .definitions import DestinationDefinitions, SourceDefinitions + + +@click.group("list", help="List existing Airbyte resources.") +@click.pass_context +def _list(ctx: click.Context): # pragma: no cover + pass + + +@click.group("connectors", help="Latest information on supported sources and destinations connectors.") +@click.pass_context +def connectors(ctx: click.Context): # pragma: no cover + pass + + +@connectors.command(help="Latest information on supported sources.") +@click.pass_context +def sources(ctx: click.Context): + api_client = ctx.obj["API_CLIENT"] + definitions = SourceDefinitions(api_client) + click.echo(definitions) + + +@connectors.command(help="Latest information on supported destinations.") +@click.pass_context +def destinations(ctx: click.Context): + api_client = ctx.obj["API_CLIENT"] + definitions = DestinationDefinitions(api_client) + click.echo(definitions) + + +AVAILABLE_COMMANDS: List[click.Command] = [connectors] + + +def add_commands_to_list(): + for command in AVAILABLE_COMMANDS: + _list.add_command(command) + + +add_commands_to_list() diff --git a/octavia-cli/octavia_cli/list/definitions.py b/octavia-cli/octavia_cli/list/definitions.py new file mode 100644 index 0000000000000..2c7be89791616 --- /dev/null +++ b/octavia-cli/octavia_cli/list/definitions.py @@ -0,0 +1,130 @@ +# +# Copyright (c) 2021 Airbyte, Inc., all rights reserved. +# + +import abc +from enum import Enum +from typing import List, Union + +import airbyte_api_client +from airbyte_api_client.api import destination_definition_api, source_definition_api + + +class DefinitionType(Enum): + SOURCE = "source" + DESTINATION = "destination" + + +class Definitions(abc.ABC): + LIST_LATEST_DEFINITIONS_KWARGS = {"_check_return_type": False} + + @property + @abc.abstractmethod + def api( + self, + ) -> Union[source_definition_api.SourceDefinitionApi, destination_definition_api.DestinationDefinitionApi]: # pragma: no cover + pass + + def __init__(self, definition_type: DefinitionType, api_client: airbyte_api_client.ApiClient): + self.definition_type = definition_type + self.api_instance = self.api(api_client) + + @property + def fields_to_display(self) -> List[str]: + return ["name", "dockerRepository", "dockerImageTag", f"{self.definition_type.value}DefinitionId"] + + @property + def response_definition_list_field(self) -> str: + return f"{self.definition_type.value}_definitions" + + @property + @abc.abstractmethod + def latest_definitions(self) -> List[List[str]]: # pragma: no cover + pass + + def _parse_response(self, api_response) -> List[List[str]]: + definitions = [ + [definition[field] for field in self.fields_to_display] for definition in api_response[self.response_definition_list_field] + ] + return definitions + + # TODO alafanechere: declare in a specific formatting module because it will probably be reused + @staticmethod + def _compute_col_width(data: List[List[str]], padding: int = 2) -> int: + """Compute column width for display purposes: + Find largest column size, add a padding of two characters. + Returns: + data (List[List[str]]): Tabular data containing rows and columns. + padding (int): Number of character to adds to create space between columns. + Returns: + col_width (int): The computed column width according to input data. + """ + col_width = max(len(col) for row in data for col in row) + padding + return col_width + + # TODO alafanechere: declare in a specific formatting module because it will probably be reused + @staticmethod + def camelcased_to_uppercased_spaced(camelcased: str): + """Util function to transform a camelCase string to a UPPERCASED SPACED string + e.g: dockerImageName -> DOCKER IMAGE NAME + Args: + camelcased (str): The camel cased string to convert. + + Returns: + (str): The converted UPPERCASED SPACED string + """ + return "".join(map(lambda x: x if x.islower() else " " + x, camelcased)).upper() + + # TODO alafanechere: declare in a specific formatting module because it will probably be reused + @staticmethod + def _display_as_table(data: List[List[str]]) -> str: + """Formats tabular input data into a displayable table with columns. + Args: + data (List[List[str]]): Tabular data containing rows and columns. + Returns: + table (str): String representation of input tabular data. + """ + col_width = Definitions._compute_col_width(data) + table = "\n".join(["".join(col.ljust(col_width) for col in row) for row in data]) + return table + + # TODO alafanechere: declare in a specific formatting module because it will probably be reused + @staticmethod + def _format_column_names(camelcased_column_names: List[str]) -> List[str]: + """Form camel cased column names to uppercased spaced column names + + Args: + camelcased_column_names (List[str]): Column names in camel case. + + Returns: + (List[str]): Column names in uppercase with spaces. + """ + return [Definitions.camelcased_to_uppercased_spaced(column_name) for column_name in camelcased_column_names] + + def __repr__(self): + definitions = [self._format_column_names(self.fields_to_display)] + self.latest_definitions + return self._display_as_table(definitions) + + +class SourceDefinitions(Definitions): + api = source_definition_api.SourceDefinitionApi + + def __init__(self, api_client: airbyte_api_client.ApiClient): + super().__init__(DefinitionType.SOURCE, api_client) + + @property + def latest_definitions(self) -> List[List[str]]: + api_response = self.api.list_latest_source_definitions(self.api_instance, **self.LIST_LATEST_DEFINITIONS_KWARGS) + return self._parse_response(api_response) + + +class DestinationDefinitions(Definitions): + api = destination_definition_api.DestinationDefinitionApi + + def __init__(self, api_client: airbyte_api_client.ApiClient): + super().__init__(DefinitionType.DESTINATION, api_client) + + @property + def latest_definitions(self) -> List[List[str]]: + api_response = self.api.list_latest_destination_definitions(self.api_instance, **self.LIST_LATEST_DEFINITIONS_KWARGS) + return self._parse_response(api_response) diff --git a/octavia-cli/unit_tests/test_entrypoint.py b/octavia-cli/unit_tests/test_entrypoint.py index c8effc674c65f..cf033ecab99fe 100644 --- a/octavia-cli/unit_tests/test_entrypoint.py +++ b/octavia-cli/unit_tests/test_entrypoint.py @@ -14,12 +14,22 @@ def test_octavia(): assert result.output.startswith("Usage: octavia [OPTIONS] COMMAND [ARGS]...") +def test_commands_in_octavia_group(): + octavia_commands = entrypoint.octavia.commands.values() + for command in entrypoint.AVAILABLE_COMMANDS: + assert command in octavia_commands + + @pytest.mark.parametrize( "command", - [entrypoint.init, entrypoint.apply, entrypoint.create, entrypoint.delete, entrypoint._list, entrypoint._import], + [entrypoint.init, entrypoint.apply, entrypoint.create, entrypoint.delete, entrypoint._import], ) def test_not_implemented_commands(command): runner = CliRunner() result = runner.invoke(command) assert result.exit_code == 1 assert result.output.endswith("not yet implemented.\n") + + +def test_available_commands(): + assert entrypoint.AVAILABLE_COMMANDS == [entrypoint.list_commands._list] diff --git a/octavia-cli/unit_tests/test_list/test_definitions.py b/octavia-cli/unit_tests/test_list/test_definitions.py new file mode 100644 index 0000000000000..fbe429cb51424 --- /dev/null +++ b/octavia-cli/unit_tests/test_list/test_definitions.py @@ -0,0 +1,149 @@ +# +# Copyright (c) 2021 Airbyte, Inc., all rights reserved. +# + +import pytest +from airbyte_api_client.api import destination_definition_api, source_definition_api +from octavia_cli.list.definitions import ( + Definitions, + DefinitionType, + DestinationDefinitions, + SourceDefinitions, +) + + +def test_definition_type(): + assert [definition_type.value for definition_type in DefinitionType] == ["source", "destination"] + + +class TestDefinitions: + @pytest.fixture + def mock_api(self, mocker): + return mocker.Mock() + + @pytest.fixture + def patch_base_class(self, mocker, mock_api): + # Mock abstract methods to enable instantiating abstract class + mocker.patch.object(Definitions, "api", mock_api) + mocker.patch.object(Definitions, "__abstractmethods__", set()) + + @pytest.fixture + def mock_definition_type(self, mocker): + return mocker.Mock(value="my_definition_type") + + @pytest.fixture + def mock_api_client(self, mocker): + return mocker.Mock() + + def test_init(self, patch_base_class, mock_api, mock_definition_type, mock_api_client): + definitions = Definitions(mock_definition_type, mock_api_client) + assert definitions.definition_type == mock_definition_type + mock_api.assert_called_with(mock_api_client) + assert definitions.api_instance == mock_api.return_value + + def test_abstract_methods(self, mock_definition_type, mock_api_client): + assert Definitions.__abstractmethods__ == {"api", "latest_definitions"} + with pytest.raises(TypeError): + Definitions(mock_definition_type, mock_api_client) + + def test_fields_to_display(self, patch_base_class, mock_definition_type, mock_api_client): + definitions = Definitions(mock_definition_type, mock_api_client) + expected_field_to_display = ["name", "dockerRepository", "dockerImageTag", "my_definition_typeDefinitionId"] + assert definitions.fields_to_display == expected_field_to_display + + def test_response_definition_list_field(self, patch_base_class, mock_definition_type, mock_api_client): + definitions = Definitions(mock_definition_type, mock_api_client) + expected_response_definition_list_field = "my_definition_type_definitions" + assert definitions.response_definition_list_field == expected_response_definition_list_field + + def test_parse_response(self, patch_base_class, mock_definition_type, mock_api_client): + definitions = Definitions(mock_definition_type, mock_api_client) + api_response = {definitions.response_definition_list_field: []} + for i in range(5): + definition = {field: f"{field}_value_{i}" for field in definitions.fields_to_display} + definition["discarded_field"] = "discarded_value" + api_response[definitions.response_definition_list_field].append(definition) + parsed_definitions = definitions._parse_response(api_response) + assert len(parsed_definitions) == 5 + for i in range(5): + assert parsed_definitions[i] == [f"{field}_value_{i}" for field in definitions.fields_to_display] + assert "discarded_value" not in parsed_definitions[i] + + def test_repr(self, patch_base_class, mocker, mock_definition_type, mock_api_client): + headers = ["fieldA", "fieldB", "fieldC"] + latest_definitions = [["a", "b", "c"]] + mocker.patch.object(Definitions, "fields_to_display", headers) + mocker.patch.object(Definitions, "latest_definitions", latest_definitions) + mocker.patch.object(Definitions, "_display_as_table") + mocker.patch.object(Definitions, "_format_column_names") + + definitions = Definitions(mock_definition_type, mock_api_client) + representation = definitions.__repr__() + definitions._display_as_table.assert_called_with([definitions._format_column_names.return_value] + latest_definitions) + assert representation == definitions._display_as_table.return_value + + @pytest.mark.parametrize( + "test_data,padding,expected_col_width", + [([["a", "___10chars"], ["e", "f"]], 2, 2 + 10), ([["a", "___10chars"], ["e", "____11chars"]], 2, 2 + 11), ([[""]], 2, 2)], + ) + def test_compute_col_width(self, test_data, padding, expected_col_width): + col_width = Definitions._compute_col_width(test_data, padding) + assert col_width == expected_col_width + + @pytest.mark.parametrize( + "test_data,col_width,expected_output", + [ + ([["a", "___10chars"], ["e", "____11chars"]], 13, "a ___10chars \ne ____11chars "), + ], + ) + def test_display_as_table(self, mocker, test_data, col_width, expected_output, mock_definition_type, mock_api_client): + mocker.patch.object(Definitions, "_compute_col_width", mocker.Mock(return_value=col_width)) + assert Definitions._display_as_table(test_data) == expected_output + + +class TestSubDefinitions: + @pytest.fixture + def mock_api_client(self, mocker): + return mocker.Mock() + + @pytest.mark.parametrize( + "definition_type,SubDefinitionClass", + [ + (DefinitionType.SOURCE, SourceDefinitions), + (DefinitionType.DESTINATION, DestinationDefinitions), + ], + ) + def test_init(self, mocker, mock_api_client, definition_type, SubDefinitionClass): + definitions_init = mocker.Mock() + mocker.patch.object(Definitions, "__init__", definitions_init) + SubDefinitionClass(mock_api_client) + definitions_init.assert_called_with(definition_type, mock_api_client) + + @pytest.mark.parametrize( + "SubDefinitionClass,expected_api", + [ + (SourceDefinitions, source_definition_api.SourceDefinitionApi), + (DestinationDefinitions, destination_definition_api.DestinationDefinitionApi), + ], + ) + def test_class_attributes(self, SubDefinitionClass, expected_api): + assert SubDefinitionClass.api == expected_api + + @pytest.mark.parametrize( + "SubDefinitionClass,list_latest_fn", + [ + (SourceDefinitions, "list_latest_source_definitions"), + (DestinationDefinitions, "list_latest_destination_definitions"), + ], + ) + def test_latest_definitions(self, mocker, mock_api_client, SubDefinitionClass, list_latest_fn): + mocker.patch.object(SubDefinitionClass, "api") + mocker.patch.object(SubDefinitionClass, "_parse_response") + + definitions = SubDefinitionClass(mock_api_client) + definitions.api_instance = mocker.Mock() + + assert definitions.latest_definitions == definitions._parse_response.return_value + definitions.api.__getattr__(list_latest_fn).assert_called_with( + definitions.api_instance, **SubDefinitionClass.LIST_LATEST_DEFINITIONS_KWARGS + ) From ab3a00acf8a1e6d8f19160fa65b2e114b5836919 Mon Sep 17 00:00:00 2001 From: alafanechere Date: Mon, 17 Jan 2022 10:07:52 +0100 Subject: [PATCH 02/10] test Definitions --- octavia-cli/octavia_cli/list/definitions.py | 6 +++--- octavia-cli/unit_tests/test_list/test_definitions.py | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/octavia-cli/octavia_cli/list/definitions.py b/octavia-cli/octavia_cli/list/definitions.py index 2c7be89791616..648202fd4b5ab 100644 --- a/octavia-cli/octavia_cli/list/definitions.py +++ b/octavia-cli/octavia_cli/list/definitions.py @@ -64,7 +64,7 @@ def _compute_col_width(data: List[List[str]], padding: int = 2) -> int: # TODO alafanechere: declare in a specific formatting module because it will probably be reused @staticmethod - def camelcased_to_uppercased_spaced(camelcased: str): + def _camelcased_to_uppercased_spaced(camelcased: str): """Util function to transform a camelCase string to a UPPERCASED SPACED string e.g: dockerImageName -> DOCKER IMAGE NAME Args: @@ -91,7 +91,7 @@ def _display_as_table(data: List[List[str]]) -> str: # TODO alafanechere: declare in a specific formatting module because it will probably be reused @staticmethod def _format_column_names(camelcased_column_names: List[str]) -> List[str]: - """Form camel cased column names to uppercased spaced column names + """Format camel cased column names to uppercased spaced column names Args: camelcased_column_names (List[str]): Column names in camel case. @@ -99,7 +99,7 @@ def _format_column_names(camelcased_column_names: List[str]) -> List[str]: Returns: (List[str]): Column names in uppercase with spaces. """ - return [Definitions.camelcased_to_uppercased_spaced(column_name) for column_name in camelcased_column_names] + return [Definitions._camelcased_to_uppercased_spaced(column_name) for column_name in camelcased_column_names] def __repr__(self): definitions = [self._format_column_names(self.fields_to_display)] + self.latest_definitions diff --git a/octavia-cli/unit_tests/test_list/test_definitions.py b/octavia-cli/unit_tests/test_list/test_definitions.py index fbe429cb51424..4bbcd12834621 100644 --- a/octavia-cli/unit_tests/test_list/test_definitions.py +++ b/octavia-cli/unit_tests/test_list/test_definitions.py @@ -100,6 +100,17 @@ def test_display_as_table(self, mocker, test_data, col_width, expected_output, m mocker.patch.object(Definitions, "_compute_col_width", mocker.Mock(return_value=col_width)) assert Definitions._display_as_table(test_data) == expected_output + @pytest.mark.parametrize("input_camelcased,expected_output", [("camelCased", "CAMEL CASED"), ("notcamelcased", "NOTCAMELCASED")]) + def test_camelcased_to_uppercased_spaced(self, input_camelcased, expected_output): + assert Definitions._camelcased_to_uppercased_spaced(input_camelcased) == expected_output + + def test_format_column_names(self, mocker): + columns_to_format = ["camelCased"] + formatted_columns = Definitions._format_column_names(columns_to_format) + assert len(formatted_columns) == 1 + for i, c in enumerate(formatted_columns): + assert c == Definitions._camelcased_to_uppercased_spaced(columns_to_format[i]) + class TestSubDefinitions: @pytest.fixture From 60ef2912f40f4d3b2677c1e04f14e2975c609ecc Mon Sep 17 00:00:00 2001 From: alafanechere Date: Mon, 17 Jan 2022 10:23:29 +0100 Subject: [PATCH 03/10] test list commands --- .../unit_tests/test_list/test_commands.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 octavia-cli/unit_tests/test_list/test_commands.py diff --git a/octavia-cli/unit_tests/test_list/test_commands.py b/octavia-cli/unit_tests/test_list/test_commands.py new file mode 100644 index 0000000000000..c9576ad34c7ae --- /dev/null +++ b/octavia-cli/unit_tests/test_list/test_commands.py @@ -0,0 +1,37 @@ +# +# Copyright (c) 2021 Airbyte, Inc., all rights reserved. +# +from unittest import mock + +from click.testing import CliRunner +from octavia_cli.list import commands + + +def test_available_commands(): + assert commands.AVAILABLE_COMMANDS == [commands.connectors] + + +def test_commands_in_list_group(): + list_commands = commands._list.commands.values() + for command in commands.AVAILABLE_COMMANDS: + assert command in list_commands + + +@mock.patch("octavia_cli.list.commands.SourceDefinitions") +def test_connectors_sources(mock_source_definitions, mocker): + mock_source_definitions.return_value = "SourceDefinitionsRepr" + context_object = {"API_CLIENT": mocker.Mock()} + runner = CliRunner() + result = runner.invoke((commands.sources), obj=context_object) + mock_source_definitions.assert_called_with(context_object["API_CLIENT"]) + assert result.output == "SourceDefinitionsRepr\n" + + +@mock.patch("octavia_cli.list.commands.DestinationDefinitions") +def test_connectors_destinations(mock_destination_definitions, mocker): + mock_destination_definitions.return_value = "DestinationDefinitionsRepr" + context_object = {"API_CLIENT": mocker.Mock()} + runner = CliRunner() + result = runner.invoke((commands.destinations), obj=context_object) + mock_destination_definitions.assert_called_with(context_object["API_CLIENT"]) + assert result.output == "DestinationDefinitionsRepr\n" From c4c8ac0b5f92535e7f3ac9b7977635100258777c Mon Sep 17 00:00:00 2001 From: alafanechere Date: Mon, 17 Jan 2022 10:33:00 +0100 Subject: [PATCH 04/10] add copyright --- octavia-cli/unit_tests/test_list/test_commands.py | 1 + 1 file changed, 1 insertion(+) diff --git a/octavia-cli/unit_tests/test_list/test_commands.py b/octavia-cli/unit_tests/test_list/test_commands.py index c9576ad34c7ae..5e8b39953873d 100644 --- a/octavia-cli/unit_tests/test_list/test_commands.py +++ b/octavia-cli/unit_tests/test_list/test_commands.py @@ -1,6 +1,7 @@ # # Copyright (c) 2021 Airbyte, Inc., all rights reserved. # + from unittest import mock from click.testing import CliRunner From b33dabdc8208432275043c5ac3316e9940df7434 Mon Sep 17 00:00:00 2001 From: alafanechere Date: Mon, 17 Jan 2022 10:33:17 +0100 Subject: [PATCH 05/10] update readme --- octavia-cli/README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/octavia-cli/README.md b/octavia-cli/README.md index 3589ea1512f85..83ba6fd278284 100644 --- a/octavia-cli/README.md +++ b/octavia-cli/README.md @@ -24,7 +24,7 @@ docker run octavia-cli:dev ```` 3. Create an `octavia` alias in your `.bashrc` or `.zshrc`: ````bash -echo 'alias octavia="docker run octavia-cli:dev"' >> ~/.zshrc +echo 'alias octavia="docker run airbyte/octavia-cli:dev"' >> ~/.zshrc source ~/.zshrc octavia ```` @@ -38,7 +38,8 @@ We welcome community contributions! | Date | Milestone | |------------|-------------------------------------| -| 2022-01-06 | Generate an API Python client from our Open API spec | +| 2022-01-17 | Implement `octavia list connectors source` and `octavia list connectors destinations`| +| 2022-01-17 | Generate an API Python client from our Open API spec | | 2021-12-22 | Bootstrapping the project's code base | # Developing locally @@ -48,7 +49,7 @@ We welcome community contributions! 4. Install dev dependencies: `pip install -e .\[dev\]` 5. Install `pre-commit` hooks: `pre-commit install` 6. Run the test suite: `pytest --cov=octavia_cli unit_tests` -7. Iterate; please check the [Contributing](#contributing) for instructions on contributing. +7. Iterate: please check the [Contributing](#contributing) for instructions on contributing. # Contributing 1. Please sign up to [Airbyte's Slack workspace](https://slack.airbyte.io/) and join the `#octavia-cli`. We'll sync up community efforts in this channel. From 63a060048b86b7cecae9d31c32bf141b47ca3bcb Mon Sep 17 00:00:00 2001 From: alafanechere Date: Mon, 17 Jan 2022 10:33:51 +0100 Subject: [PATCH 06/10] update readme --- octavia-cli/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octavia-cli/README.md b/octavia-cli/README.md index 83ba6fd278284..701d9a73f07ae 100644 --- a/octavia-cli/README.md +++ b/octavia-cli/README.md @@ -20,7 +20,7 @@ SUB_BUILD=OCTAVIA_CLI ./gradlew build #from the root of the repo ``` 2. Run the CLI from docker: ```bash -docker run octavia-cli:dev +docker run airbyte/octavia-cli:dev ```` 3. Create an `octavia` alias in your `.bashrc` or `.zshrc`: ````bash From d515b94de77351b977b14b19776118018d2790e8 Mon Sep 17 00:00:00 2001 From: alafanechere Date: Tue, 18 Jan 2022 10:18:46 +0100 Subject: [PATCH 07/10] refacto to set latest_definitions prop on Definitions abstract class --- octavia-cli/octavia_cli/list/definitions.py | 31 +++----- .../unit_tests/test_list/test_definitions.py | 79 ++++++++----------- 2 files changed, 46 insertions(+), 64 deletions(-) diff --git a/octavia-cli/octavia_cli/list/definitions.py b/octavia-cli/octavia_cli/list/definitions.py index 648202fd4b5ab..a85fe7585e89f 100644 --- a/octavia-cli/octavia_cli/list/definitions.py +++ b/octavia-cli/octavia_cli/list/definitions.py @@ -4,7 +4,7 @@ import abc from enum import Enum -from typing import List, Union +from typing import Callable, List, Union import airbyte_api_client from airbyte_api_client.api import destination_definition_api, source_definition_api @@ -25,9 +25,10 @@ def api( ) -> Union[source_definition_api.SourceDefinitionApi, destination_definition_api.DestinationDefinitionApi]: # pragma: no cover pass - def __init__(self, definition_type: DefinitionType, api_client: airbyte_api_client.ApiClient): + def __init__(self, definition_type: DefinitionType, api_client: airbyte_api_client.ApiClient, list_latest_definitions: Callable): self.definition_type = definition_type self.api_instance = self.api(api_client) + self.list_latest_definitions = list_latest_definitions @property def fields_to_display(self) -> List[str]: @@ -37,17 +38,17 @@ def fields_to_display(self) -> List[str]: def response_definition_list_field(self) -> str: return f"{self.definition_type.value}_definitions" - @property - @abc.abstractmethod - def latest_definitions(self) -> List[List[str]]: # pragma: no cover - pass - def _parse_response(self, api_response) -> List[List[str]]: definitions = [ [definition[field] for field in self.fields_to_display] for definition in api_response[self.response_definition_list_field] ] return definitions + @property + def latest_definitions(self) -> List[List[str]]: + api_response = self.list_latest_definitions(self.api_instance, **self.LIST_LATEST_DEFINITIONS_KWARGS) + return self._parse_response(api_response) + # TODO alafanechere: declare in a specific formatting module because it will probably be reused @staticmethod def _compute_col_width(data: List[List[str]], padding: int = 2) -> int: @@ -64,7 +65,7 @@ def _compute_col_width(data: List[List[str]], padding: int = 2) -> int: # TODO alafanechere: declare in a specific formatting module because it will probably be reused @staticmethod - def _camelcased_to_uppercased_spaced(camelcased: str): + def _camelcased_to_uppercased_spaced(camelcased: str) -> str: """Util function to transform a camelCase string to a UPPERCASED SPACED string e.g: dockerImageName -> DOCKER IMAGE NAME Args: @@ -110,21 +111,11 @@ class SourceDefinitions(Definitions): api = source_definition_api.SourceDefinitionApi def __init__(self, api_client: airbyte_api_client.ApiClient): - super().__init__(DefinitionType.SOURCE, api_client) - - @property - def latest_definitions(self) -> List[List[str]]: - api_response = self.api.list_latest_source_definitions(self.api_instance, **self.LIST_LATEST_DEFINITIONS_KWARGS) - return self._parse_response(api_response) + super().__init__(DefinitionType.SOURCE, api_client, self.api.list_latest_source_definitions) class DestinationDefinitions(Definitions): api = destination_definition_api.DestinationDefinitionApi def __init__(self, api_client: airbyte_api_client.ApiClient): - super().__init__(DefinitionType.DESTINATION, api_client) - - @property - def latest_definitions(self) -> List[List[str]]: - api_response = self.api.list_latest_destination_definitions(self.api_instance, **self.LIST_LATEST_DEFINITIONS_KWARGS) - return self._parse_response(api_response) + super().__init__(DefinitionType.DESTINATION, api_client, self.api.list_latest_destination_definitions) diff --git a/octavia-cli/unit_tests/test_list/test_definitions.py b/octavia-cli/unit_tests/test_list/test_definitions.py index 4bbcd12834621..4a32e8596fa77 100644 --- a/octavia-cli/unit_tests/test_list/test_definitions.py +++ b/octavia-cli/unit_tests/test_list/test_definitions.py @@ -28,36 +28,34 @@ def patch_base_class(self, mocker, mock_api): mocker.patch.object(Definitions, "__abstractmethods__", set()) @pytest.fixture - def mock_definition_type(self, mocker): - return mocker.Mock(value="my_definition_type") + def definitions_mock_args(self, mocker): + return (mocker.Mock(value="my_definition_type"), mocker.Mock(), mocker.Mock()) - @pytest.fixture - def mock_api_client(self, mocker): - return mocker.Mock() - - def test_init(self, patch_base_class, mock_api, mock_definition_type, mock_api_client): - definitions = Definitions(mock_definition_type, mock_api_client) + def test_init(self, patch_base_class, mock_api, definitions_mock_args): + mock_definition_type, mock_api_client, mock_list_latest_definitions = definitions_mock_args + definitions = Definitions(*definitions_mock_args) assert definitions.definition_type == mock_definition_type mock_api.assert_called_with(mock_api_client) assert definitions.api_instance == mock_api.return_value + assert definitions.list_latest_definitions == mock_list_latest_definitions - def test_abstract_methods(self, mock_definition_type, mock_api_client): - assert Definitions.__abstractmethods__ == {"api", "latest_definitions"} + def test_abstract_methods(self, definitions_mock_args): + assert Definitions.__abstractmethods__ == {"api"} with pytest.raises(TypeError): - Definitions(mock_definition_type, mock_api_client) + Definitions(*definitions_mock_args) - def test_fields_to_display(self, patch_base_class, mock_definition_type, mock_api_client): - definitions = Definitions(mock_definition_type, mock_api_client) + def test_fields_to_display(self, patch_base_class, definitions_mock_args): + definitions = Definitions(*definitions_mock_args) expected_field_to_display = ["name", "dockerRepository", "dockerImageTag", "my_definition_typeDefinitionId"] assert definitions.fields_to_display == expected_field_to_display - def test_response_definition_list_field(self, patch_base_class, mock_definition_type, mock_api_client): - definitions = Definitions(mock_definition_type, mock_api_client) + def test_response_definition_list_field(self, patch_base_class, definitions_mock_args): + definitions = Definitions(*definitions_mock_args) expected_response_definition_list_field = "my_definition_type_definitions" assert definitions.response_definition_list_field == expected_response_definition_list_field - def test_parse_response(self, patch_base_class, mock_definition_type, mock_api_client): - definitions = Definitions(mock_definition_type, mock_api_client) + def test_parse_response(self, patch_base_class, definitions_mock_args): + definitions = Definitions(*definitions_mock_args) api_response = {definitions.response_definition_list_field: []} for i in range(5): definition = {field: f"{field}_value_{i}" for field in definitions.fields_to_display} @@ -69,7 +67,15 @@ def test_parse_response(self, patch_base_class, mock_definition_type, mock_api_c assert parsed_definitions[i] == [f"{field}_value_{i}" for field in definitions.fields_to_display] assert "discarded_value" not in parsed_definitions[i] - def test_repr(self, patch_base_class, mocker, mock_definition_type, mock_api_client): + def test_latest_definitions(self, patch_base_class, mocker, definitions_mock_args): + mock_list_latest_definitions = definitions_mock_args[-1] + mocker.patch.object(Definitions, "_parse_response") + definitions = Definitions(*definitions_mock_args) + assert definitions.latest_definitions == definitions._parse_response.return_value + mock_list_latest_definitions.assert_called_with(definitions.api_instance, **definitions.LIST_LATEST_DEFINITIONS_KWARGS) + definitions._parse_response.assert_called_with(mock_list_latest_definitions.return_value) + + def test_repr(self, patch_base_class, mocker, definitions_mock_args): headers = ["fieldA", "fieldB", "fieldC"] latest_definitions = [["a", "b", "c"]] mocker.patch.object(Definitions, "fields_to_display", headers) @@ -77,7 +83,7 @@ def test_repr(self, patch_base_class, mocker, mock_definition_type, mock_api_cli mocker.patch.object(Definitions, "_display_as_table") mocker.patch.object(Definitions, "_format_column_names") - definitions = Definitions(mock_definition_type, mock_api_client) + definitions = Definitions(*definitions_mock_args) representation = definitions.__repr__() definitions._display_as_table.assert_called_with([definitions._format_column_names.return_value] + latest_definitions) assert representation == definitions._display_as_table.return_value @@ -96,7 +102,7 @@ def test_compute_col_width(self, test_data, padding, expected_col_width): ([["a", "___10chars"], ["e", "____11chars"]], 13, "a ___10chars \ne ____11chars "), ], ) - def test_display_as_table(self, mocker, test_data, col_width, expected_output, mock_definition_type, mock_api_client): + def test_display_as_table(self, mocker, test_data, col_width, expected_output): mocker.patch.object(Definitions, "_compute_col_width", mocker.Mock(return_value=col_width)) assert Definitions._display_as_table(test_data) == expected_output @@ -118,17 +124,21 @@ def mock_api_client(self, mocker): return mocker.Mock() @pytest.mark.parametrize( - "definition_type,SubDefinitionClass", + "definition_type,SubDefinitionClass,list_latest_definitions", [ - (DefinitionType.SOURCE, SourceDefinitions), - (DefinitionType.DESTINATION, DestinationDefinitions), + (DefinitionType.SOURCE, SourceDefinitions, source_definition_api.SourceDefinitionApi.list_latest_source_definitions), + ( + DefinitionType.DESTINATION, + DestinationDefinitions, + destination_definition_api.DestinationDefinitionApi.list_latest_destination_definitions, + ), ], ) - def test_init(self, mocker, mock_api_client, definition_type, SubDefinitionClass): + def test_init(self, mocker, mock_api_client, definition_type, SubDefinitionClass, list_latest_definitions): definitions_init = mocker.Mock() mocker.patch.object(Definitions, "__init__", definitions_init) SubDefinitionClass(mock_api_client) - definitions_init.assert_called_with(definition_type, mock_api_client) + definitions_init.assert_called_with(definition_type, mock_api_client, list_latest_definitions) @pytest.mark.parametrize( "SubDefinitionClass,expected_api", @@ -139,22 +149,3 @@ def test_init(self, mocker, mock_api_client, definition_type, SubDefinitionClass ) def test_class_attributes(self, SubDefinitionClass, expected_api): assert SubDefinitionClass.api == expected_api - - @pytest.mark.parametrize( - "SubDefinitionClass,list_latest_fn", - [ - (SourceDefinitions, "list_latest_source_definitions"), - (DestinationDefinitions, "list_latest_destination_definitions"), - ], - ) - def test_latest_definitions(self, mocker, mock_api_client, SubDefinitionClass, list_latest_fn): - mocker.patch.object(SubDefinitionClass, "api") - mocker.patch.object(SubDefinitionClass, "_parse_response") - - definitions = SubDefinitionClass(mock_api_client) - definitions.api_instance = mocker.Mock() - - assert definitions.latest_definitions == definitions._parse_response.return_value - definitions.api.__getattr__(list_latest_fn).assert_called_with( - definitions.api_instance, **SubDefinitionClass.LIST_LATEST_DEFINITIONS_KWARGS - ) From c295f679ecb783528c2ec44498155b8aba8d73b1 Mon Sep 17 00:00:00 2001 From: alafanechere Date: Tue, 18 Jan 2022 10:32:08 +0100 Subject: [PATCH 08/10] rename Definitions to ConnectorsDefinitions --- octavia-cli/octavia_cli/list/commands.py | 6 +- octavia-cli/octavia_cli/list/definitions.py | 10 +-- .../unit_tests/test_list/test_commands.py | 20 ++--- .../unit_tests/test_list/test_definitions.py | 82 +++++++++---------- 4 files changed, 57 insertions(+), 61 deletions(-) diff --git a/octavia-cli/octavia_cli/list/commands.py b/octavia-cli/octavia_cli/list/commands.py index 1c79cee47feee..b965ee03a7d7a 100644 --- a/octavia-cli/octavia_cli/list/commands.py +++ b/octavia-cli/octavia_cli/list/commands.py @@ -6,7 +6,7 @@ import click -from .definitions import DestinationDefinitions, SourceDefinitions +from .definitions import DestinationConnectorsDefinitions, SourceConnectorsDefinitions @click.group("list", help="List existing Airbyte resources.") @@ -25,7 +25,7 @@ def connectors(ctx: click.Context): # pragma: no cover @click.pass_context def sources(ctx: click.Context): api_client = ctx.obj["API_CLIENT"] - definitions = SourceDefinitions(api_client) + definitions = SourceConnectorsDefinitions(api_client) click.echo(definitions) @@ -33,7 +33,7 @@ def sources(ctx: click.Context): @click.pass_context def destinations(ctx: click.Context): api_client = ctx.obj["API_CLIENT"] - definitions = DestinationDefinitions(api_client) + definitions = DestinationConnectorsDefinitions(api_client) click.echo(definitions) diff --git a/octavia-cli/octavia_cli/list/definitions.py b/octavia-cli/octavia_cli/list/definitions.py index a85fe7585e89f..4820adf905e3a 100644 --- a/octavia-cli/octavia_cli/list/definitions.py +++ b/octavia-cli/octavia_cli/list/definitions.py @@ -15,7 +15,7 @@ class DefinitionType(Enum): DESTINATION = "destination" -class Definitions(abc.ABC): +class ConnectorsDefinitions(abc.ABC): LIST_LATEST_DEFINITIONS_KWARGS = {"_check_return_type": False} @property @@ -85,7 +85,7 @@ def _display_as_table(data: List[List[str]]) -> str: Returns: table (str): String representation of input tabular data. """ - col_width = Definitions._compute_col_width(data) + col_width = ConnectorsDefinitions._compute_col_width(data) table = "\n".join(["".join(col.ljust(col_width) for col in row) for row in data]) return table @@ -100,21 +100,21 @@ def _format_column_names(camelcased_column_names: List[str]) -> List[str]: Returns: (List[str]): Column names in uppercase with spaces. """ - return [Definitions._camelcased_to_uppercased_spaced(column_name) for column_name in camelcased_column_names] + return [ConnectorsDefinitions._camelcased_to_uppercased_spaced(column_name) for column_name in camelcased_column_names] def __repr__(self): definitions = [self._format_column_names(self.fields_to_display)] + self.latest_definitions return self._display_as_table(definitions) -class SourceDefinitions(Definitions): +class SourceConnectorsDefinitions(ConnectorsDefinitions): api = source_definition_api.SourceDefinitionApi def __init__(self, api_client: airbyte_api_client.ApiClient): super().__init__(DefinitionType.SOURCE, api_client, self.api.list_latest_source_definitions) -class DestinationDefinitions(Definitions): +class DestinationConnectorsDefinitions(ConnectorsDefinitions): api = destination_definition_api.DestinationDefinitionApi def __init__(self, api_client: airbyte_api_client.ApiClient): diff --git a/octavia-cli/unit_tests/test_list/test_commands.py b/octavia-cli/unit_tests/test_list/test_commands.py index 5e8b39953873d..9547ae2914a03 100644 --- a/octavia-cli/unit_tests/test_list/test_commands.py +++ b/octavia-cli/unit_tests/test_list/test_commands.py @@ -2,8 +2,6 @@ # Copyright (c) 2021 Airbyte, Inc., all rights reserved. # -from unittest import mock - from click.testing import CliRunner from octavia_cli.list import commands @@ -18,21 +16,19 @@ def test_commands_in_list_group(): assert command in list_commands -@mock.patch("octavia_cli.list.commands.SourceDefinitions") -def test_connectors_sources(mock_source_definitions, mocker): - mock_source_definitions.return_value = "SourceDefinitionsRepr" +def test_connectors_sources(mocker): + mocker.patch.object(commands, "SourceConnectorsDefinitions", mocker.Mock(return_value="SourceConnectorsDefinitionsRepr")) context_object = {"API_CLIENT": mocker.Mock()} runner = CliRunner() result = runner.invoke((commands.sources), obj=context_object) - mock_source_definitions.assert_called_with(context_object["API_CLIENT"]) - assert result.output == "SourceDefinitionsRepr\n" + commands.SourceConnectorsDefinitions.assert_called_with(context_object["API_CLIENT"]) + assert result.output == "SourceConnectorsDefinitionsRepr\n" -@mock.patch("octavia_cli.list.commands.DestinationDefinitions") -def test_connectors_destinations(mock_destination_definitions, mocker): - mock_destination_definitions.return_value = "DestinationDefinitionsRepr" +def test_connectors_destinations(mocker): + mocker.patch.object(commands, "DestinationConnectorsDefinitions", mocker.Mock(return_value="DestinationConnectorsDefinitionsRepr")) context_object = {"API_CLIENT": mocker.Mock()} runner = CliRunner() result = runner.invoke((commands.destinations), obj=context_object) - mock_destination_definitions.assert_called_with(context_object["API_CLIENT"]) - assert result.output == "DestinationDefinitionsRepr\n" + commands.DestinationConnectorsDefinitions.assert_called_with(context_object["API_CLIENT"]) + assert result.output == "DestinationConnectorsDefinitionsRepr\n" diff --git a/octavia-cli/unit_tests/test_list/test_definitions.py b/octavia-cli/unit_tests/test_list/test_definitions.py index 4a32e8596fa77..606ede737309b 100644 --- a/octavia-cli/unit_tests/test_list/test_definitions.py +++ b/octavia-cli/unit_tests/test_list/test_definitions.py @@ -5,10 +5,10 @@ import pytest from airbyte_api_client.api import destination_definition_api, source_definition_api from octavia_cli.list.definitions import ( - Definitions, + ConnectorsDefinitions, DefinitionType, - DestinationDefinitions, - SourceDefinitions, + DestinationConnectorsDefinitions, + SourceConnectorsDefinitions, ) @@ -16,7 +16,7 @@ def test_definition_type(): assert [definition_type.value for definition_type in DefinitionType] == ["source", "destination"] -class TestDefinitions: +class TestConnectorsDefinitions: @pytest.fixture def mock_api(self, mocker): return mocker.Mock() @@ -24,38 +24,38 @@ def mock_api(self, mocker): @pytest.fixture def patch_base_class(self, mocker, mock_api): # Mock abstract methods to enable instantiating abstract class - mocker.patch.object(Definitions, "api", mock_api) - mocker.patch.object(Definitions, "__abstractmethods__", set()) + mocker.patch.object(ConnectorsDefinitions, "api", mock_api) + mocker.patch.object(ConnectorsDefinitions, "__abstractmethods__", set()) @pytest.fixture - def definitions_mock_args(self, mocker): + def connectors_definitions_mock_args(self, mocker): return (mocker.Mock(value="my_definition_type"), mocker.Mock(), mocker.Mock()) - def test_init(self, patch_base_class, mock_api, definitions_mock_args): - mock_definition_type, mock_api_client, mock_list_latest_definitions = definitions_mock_args - definitions = Definitions(*definitions_mock_args) + def test_init(self, patch_base_class, mock_api, connectors_definitions_mock_args): + mock_definition_type, mock_api_client, mock_list_latest_definitions = connectors_definitions_mock_args + definitions = ConnectorsDefinitions(*connectors_definitions_mock_args) assert definitions.definition_type == mock_definition_type mock_api.assert_called_with(mock_api_client) assert definitions.api_instance == mock_api.return_value assert definitions.list_latest_definitions == mock_list_latest_definitions - def test_abstract_methods(self, definitions_mock_args): - assert Definitions.__abstractmethods__ == {"api"} + def test_abstract_methods(self, connectors_definitions_mock_args): + assert ConnectorsDefinitions.__abstractmethods__ == {"api"} with pytest.raises(TypeError): - Definitions(*definitions_mock_args) + ConnectorsDefinitions(*connectors_definitions_mock_args) - def test_fields_to_display(self, patch_base_class, definitions_mock_args): - definitions = Definitions(*definitions_mock_args) + def test_fields_to_display(self, patch_base_class, connectors_definitions_mock_args): + definitions = ConnectorsDefinitions(*connectors_definitions_mock_args) expected_field_to_display = ["name", "dockerRepository", "dockerImageTag", "my_definition_typeDefinitionId"] assert definitions.fields_to_display == expected_field_to_display - def test_response_definition_list_field(self, patch_base_class, definitions_mock_args): - definitions = Definitions(*definitions_mock_args) + def test_response_definition_list_field(self, patch_base_class, connectors_definitions_mock_args): + definitions = ConnectorsDefinitions(*connectors_definitions_mock_args) expected_response_definition_list_field = "my_definition_type_definitions" assert definitions.response_definition_list_field == expected_response_definition_list_field - def test_parse_response(self, patch_base_class, definitions_mock_args): - definitions = Definitions(*definitions_mock_args) + def test_parse_response(self, patch_base_class, connectors_definitions_mock_args): + definitions = ConnectorsDefinitions(*connectors_definitions_mock_args) api_response = {definitions.response_definition_list_field: []} for i in range(5): definition = {field: f"{field}_value_{i}" for field in definitions.fields_to_display} @@ -67,23 +67,23 @@ def test_parse_response(self, patch_base_class, definitions_mock_args): assert parsed_definitions[i] == [f"{field}_value_{i}" for field in definitions.fields_to_display] assert "discarded_value" not in parsed_definitions[i] - def test_latest_definitions(self, patch_base_class, mocker, definitions_mock_args): - mock_list_latest_definitions = definitions_mock_args[-1] - mocker.patch.object(Definitions, "_parse_response") - definitions = Definitions(*definitions_mock_args) + def test_latest_definitions(self, patch_base_class, mocker, connectors_definitions_mock_args): + mock_list_latest_definitions = connectors_definitions_mock_args[-1] + mocker.patch.object(ConnectorsDefinitions, "_parse_response") + definitions = ConnectorsDefinitions(*connectors_definitions_mock_args) assert definitions.latest_definitions == definitions._parse_response.return_value mock_list_latest_definitions.assert_called_with(definitions.api_instance, **definitions.LIST_LATEST_DEFINITIONS_KWARGS) definitions._parse_response.assert_called_with(mock_list_latest_definitions.return_value) - def test_repr(self, patch_base_class, mocker, definitions_mock_args): + def test_repr(self, patch_base_class, mocker, connectors_definitions_mock_args): headers = ["fieldA", "fieldB", "fieldC"] latest_definitions = [["a", "b", "c"]] - mocker.patch.object(Definitions, "fields_to_display", headers) - mocker.patch.object(Definitions, "latest_definitions", latest_definitions) - mocker.patch.object(Definitions, "_display_as_table") - mocker.patch.object(Definitions, "_format_column_names") + mocker.patch.object(ConnectorsDefinitions, "fields_to_display", headers) + mocker.patch.object(ConnectorsDefinitions, "latest_definitions", latest_definitions) + mocker.patch.object(ConnectorsDefinitions, "_display_as_table") + mocker.patch.object(ConnectorsDefinitions, "_format_column_names") - definitions = Definitions(*definitions_mock_args) + definitions = ConnectorsDefinitions(*connectors_definitions_mock_args) representation = definitions.__repr__() definitions._display_as_table.assert_called_with([definitions._format_column_names.return_value] + latest_definitions) assert representation == definitions._display_as_table.return_value @@ -93,7 +93,7 @@ def test_repr(self, patch_base_class, mocker, definitions_mock_args): [([["a", "___10chars"], ["e", "f"]], 2, 2 + 10), ([["a", "___10chars"], ["e", "____11chars"]], 2, 2 + 11), ([[""]], 2, 2)], ) def test_compute_col_width(self, test_data, padding, expected_col_width): - col_width = Definitions._compute_col_width(test_data, padding) + col_width = ConnectorsDefinitions._compute_col_width(test_data, padding) assert col_width == expected_col_width @pytest.mark.parametrize( @@ -103,22 +103,22 @@ def test_compute_col_width(self, test_data, padding, expected_col_width): ], ) def test_display_as_table(self, mocker, test_data, col_width, expected_output): - mocker.patch.object(Definitions, "_compute_col_width", mocker.Mock(return_value=col_width)) - assert Definitions._display_as_table(test_data) == expected_output + mocker.patch.object(ConnectorsDefinitions, "_compute_col_width", mocker.Mock(return_value=col_width)) + assert ConnectorsDefinitions._display_as_table(test_data) == expected_output @pytest.mark.parametrize("input_camelcased,expected_output", [("camelCased", "CAMEL CASED"), ("notcamelcased", "NOTCAMELCASED")]) def test_camelcased_to_uppercased_spaced(self, input_camelcased, expected_output): - assert Definitions._camelcased_to_uppercased_spaced(input_camelcased) == expected_output + assert ConnectorsDefinitions._camelcased_to_uppercased_spaced(input_camelcased) == expected_output def test_format_column_names(self, mocker): columns_to_format = ["camelCased"] - formatted_columns = Definitions._format_column_names(columns_to_format) + formatted_columns = ConnectorsDefinitions._format_column_names(columns_to_format) assert len(formatted_columns) == 1 for i, c in enumerate(formatted_columns): - assert c == Definitions._camelcased_to_uppercased_spaced(columns_to_format[i]) + assert c == ConnectorsDefinitions._camelcased_to_uppercased_spaced(columns_to_format[i]) -class TestSubDefinitions: +class TestSubConnectorsDefinitions: @pytest.fixture def mock_api_client(self, mocker): return mocker.Mock() @@ -126,25 +126,25 @@ def mock_api_client(self, mocker): @pytest.mark.parametrize( "definition_type,SubDefinitionClass,list_latest_definitions", [ - (DefinitionType.SOURCE, SourceDefinitions, source_definition_api.SourceDefinitionApi.list_latest_source_definitions), + (DefinitionType.SOURCE, SourceConnectorsDefinitions, source_definition_api.SourceDefinitionApi.list_latest_source_definitions), ( DefinitionType.DESTINATION, - DestinationDefinitions, + DestinationConnectorsDefinitions, destination_definition_api.DestinationDefinitionApi.list_latest_destination_definitions, ), ], ) def test_init(self, mocker, mock_api_client, definition_type, SubDefinitionClass, list_latest_definitions): definitions_init = mocker.Mock() - mocker.patch.object(Definitions, "__init__", definitions_init) + mocker.patch.object(ConnectorsDefinitions, "__init__", definitions_init) SubDefinitionClass(mock_api_client) definitions_init.assert_called_with(definition_type, mock_api_client, list_latest_definitions) @pytest.mark.parametrize( "SubDefinitionClass,expected_api", [ - (SourceDefinitions, source_definition_api.SourceDefinitionApi), - (DestinationDefinitions, destination_definition_api.DestinationDefinitionApi), + (SourceConnectorsDefinitions, source_definition_api.SourceDefinitionApi), + (DestinationConnectorsDefinitions, destination_definition_api.DestinationDefinitionApi), ], ) def test_class_attributes(self, SubDefinitionClass, expected_api): From 9d86981f0dabe501a93361a8c0977ff41d9a6d3c Mon Sep 17 00:00:00 2001 From: alafanechere Date: Tue, 18 Jan 2022 10:44:53 +0100 Subject: [PATCH 09/10] rename definitions.py to connectors_definitions.py --- octavia-cli/octavia_cli/list/commands.py | 5 ++++- .../list/{definitions.py => connectors_definitions.py} | 0 .../{test_definitions.py => test_connectors_definitions.py} | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) rename octavia-cli/octavia_cli/list/{definitions.py => connectors_definitions.py} (100%) rename octavia-cli/unit_tests/test_list/{test_definitions.py => test_connectors_definitions.py} (99%) diff --git a/octavia-cli/octavia_cli/list/commands.py b/octavia-cli/octavia_cli/list/commands.py index b965ee03a7d7a..0976b5708df45 100644 --- a/octavia-cli/octavia_cli/list/commands.py +++ b/octavia-cli/octavia_cli/list/commands.py @@ -6,7 +6,10 @@ import click -from .definitions import DestinationConnectorsDefinitions, SourceConnectorsDefinitions +from .connectors_definitions import ( + DestinationConnectorsDefinitions, + SourceConnectorsDefinitions, +) @click.group("list", help="List existing Airbyte resources.") diff --git a/octavia-cli/octavia_cli/list/definitions.py b/octavia-cli/octavia_cli/list/connectors_definitions.py similarity index 100% rename from octavia-cli/octavia_cli/list/definitions.py rename to octavia-cli/octavia_cli/list/connectors_definitions.py diff --git a/octavia-cli/unit_tests/test_list/test_definitions.py b/octavia-cli/unit_tests/test_list/test_connectors_definitions.py similarity index 99% rename from octavia-cli/unit_tests/test_list/test_definitions.py rename to octavia-cli/unit_tests/test_list/test_connectors_definitions.py index 606ede737309b..0a7687b2b72e0 100644 --- a/octavia-cli/unit_tests/test_list/test_definitions.py +++ b/octavia-cli/unit_tests/test_list/test_connectors_definitions.py @@ -4,7 +4,7 @@ import pytest from airbyte_api_client.api import destination_definition_api, source_definition_api -from octavia_cli.list.definitions import ( +from octavia_cli.list.connectors_definitions import ( ConnectorsDefinitions, DefinitionType, DestinationConnectorsDefinitions, From dc02a57b7018ba6eb1fedc0688ebabfe0793d4f6 Mon Sep 17 00:00:00 2001 From: alafanechere Date: Fri, 21 Jan 2022 08:49:37 +0100 Subject: [PATCH 10/10] format with gradlew format and not pre-commit --- octavia-cli/octavia_cli/list/commands.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/octavia-cli/octavia_cli/list/commands.py b/octavia-cli/octavia_cli/list/commands.py index 0976b5708df45..1eac6a5303086 100644 --- a/octavia-cli/octavia_cli/list/commands.py +++ b/octavia-cli/octavia_cli/list/commands.py @@ -6,10 +6,7 @@ import click -from .connectors_definitions import ( - DestinationConnectorsDefinitions, - SourceConnectorsDefinitions, -) +from .connectors_definitions import DestinationConnectorsDefinitions, SourceConnectorsDefinitions @click.group("list", help="List existing Airbyte resources.")