def get_summary(config, definitions): common = """<b><blue>Project</blue></b> <b>Project Name:</b> ${project_name} - <b>Package Name:</b> ${package_name} <b>Description:</b> ${description} <b>Author:</b> ${author} <b>Version:</b> ${version} - <b>License:</b> ${license} <b>Use Github Actions:</b> ${use_github_actions} - <b>Use Asyncio:</b> ${use_asyncio} <b><blue>Config</blue></b> <b>API Key:</b> ${api_key} <b>Environment ID:</b> ${environment_id} <b>Server Address:</b> ${server_address} """ event_answers = '\n'.join( (f'<b><blue>{group}</blue></b>\n' f' <b>{group} types:</b> ' '${' + f'{category}_' + slugify(group.lower()) + '}') for category, category_events in definitions.items() for group in category_events.keys()) + '\n' examples = """<b><blue>Examples</blue></b> <b>Schedulables:</b> ${include_schedules_example} - <b>Variables:</b> ${include_variables_example} """ return common + event_answers + examples
def test_validate_extension_project_error_exit(mocker, faker, fs, mocked_responses, config_vendor, capsys): runner_version = f'{faker.random_number()}.{faker.random_number()}' mocker.patch('connect.cli.plugins.project.extension.helpers.click.echo') mocker.patch( 'connect.cli.plugins.project.extension.helpers.get_pypi_runner_version', side_effect=runner_version, ) config_vendor.load(config_dir='/tmp') mocked_responses.add( method='GET', url=f'{config_vendor.active.endpoint}/devops/event-definitions', headers={ 'Content-Range': 'items 0-0/1', }, json=[ { 'type': 'sample_background_event', 'group': 'Group', 'name': 'Sample Event', 'category': 'background', 'object_statuses': ['status1', 'status2'], }, ], ) data = { 'project_name': faker.name(), 'project_slug': slugify(faker.name()), 'description': 'desc', 'package_name': slugify(faker.name()), 'author': 'connect', 'version': '1.0', 'license': 'Apache', 'use_github_actions': 'n', 'use_asyncio': 'n', 'include_schedules_example': 'y', 'include_variables_example': 'y', 'api_key': faker.pystr(), 'environment_id': f'ENV-{faker.random_number()}', 'server_address': faker.domain_name(2), 'background_group': ['sample_background_event'], } mocker.patch( 'connect.cli.plugins.project.extension.helpers.dialogus', return_value=data, ) output_dir = f'{fs.root_path}/projects' os.mkdir(output_dir) bootstrap_extension_project(config_vendor, output_dir, False) project_dir = os.path.join(output_dir, data['project_slug']) os.unlink(os.path.join(project_dir, 'pyproject.toml')) validate_extension_project(config_vendor, project_dir) captured = capsys.readouterr() assert 'Warning/errors have been found' in captured.out
def test_bootstrap_extension_project_background( fs, faker, mocker, mocked_responses, config_vendor, extension_class_declaration, extension_imports, extension_bg_event, extension_schedulable_event, test_bg_event, test_schedulable_event, async_impl, with_variables, with_schedulable, with_github_actions, ): runner_version = f'{faker.random_number()}.{faker.random_number()}' mocker.patch('connect.cli.plugins.project.extension.helpers.click.echo') mocker.patch( 'connect.cli.plugins.project.extension.helpers.get_pypi_runner_version', return_value=runner_version, ) config_vendor.load(config_dir='/tmp') mocked_responses.add( method='GET', url=f'{config_vendor.active.endpoint}/devops/event-definitions', headers={ 'Content-Range': 'items 0-0/1', }, json=[ { 'type': 'sample_background_event', 'group': 'Group', 'name': 'Sample Event', 'category': 'background', 'object_statuses': ['status1', 'status2'], }, ], ) data = { 'project_name': faker.name(), 'project_slug': slugify(faker.name()), 'description': 'desc', 'package_name': slugify(faker.name()), 'author': 'connect', 'version': '1.0', 'license': 'Apache', 'use_github_actions': 'y' if with_github_actions else 'n', 'use_asyncio': 'y' if async_impl else 'n', 'include_schedules_example': 'y' if with_schedulable else 'n', 'include_variables_example': 'y' if with_variables else 'n', 'api_key': faker.pystr(), 'environment_id': f'ENV-{faker.random_number()}', 'server_address': faker.domain_name(2), 'background_group': ['sample_background_event'], } mocker.patch( 'connect.cli.plugins.project.extension.helpers.dialogus', return_value=data, ) output_dir = f'{fs.root_path}/projects' os.mkdir(output_dir) bootstrap_extension_project(config_vendor, output_dir, False) classname_prefix = data['project_slug'].replace('_', ' ').title().replace(' ', '') env_file_name = f".{data['project_slug']}_dev.env" env_file = open(os.path.join(output_dir, data['project_slug'], env_file_name)).read() assert f'export API_KEY="{data["api_key"]}"' in env_file assert f'export ENVIRONMENT_ID="{data["environment_id"]}"' in env_file assert f'export SERVER_ADDRESS="{data["server_address"]}"' in env_file docker_compose_yml = yaml.safe_load( open(os.path.join(output_dir, data['project_slug'], 'docker-compose.yml')), ) for service_suffix in ('dev', 'bash', 'test'): service = docker_compose_yml['services'][f"{data['project_slug']}_{service_suffix}"] assert service['container_name'] == f"{data['project_slug']}_{service_suffix}" assert service['image'] == f'cloudblueconnect/connect-extension-runner:{runner_version}' assert service['env_file'] == [env_file_name] pyproject_toml = toml.load(os.path.join(output_dir, data['project_slug'], 'pyproject.toml')) ext_entrypoint = pyproject_toml['tool']['poetry']['plugins']['connect.eaas.ext'] assert ext_entrypoint == {'extension': f"{data['package_name']}.extension:{classname_prefix}Extension"} if with_github_actions: assert os.path.exists( os.path.join(output_dir, data['project_slug'], '.github', 'workflows', 'build.yml'), ) is True parser = configparser.ConfigParser() parser.read(os.path.join(output_dir, data['project_slug'], '.flake8')) assert parser['flake8']['application-import-names'] == data['package_name'] flake8_style_guide = flake8.get_style_guide( show_source=parser['flake8']['show-source'], max_line_length=int(parser['flake8']['max-line-length']), application_import_names=parser['flake8']['application-import-names'], import_order_style=parser['flake8']['import-order-style'], max_cognitive_complexity=parser['flake8']['max-cognitive-complexity'], ignore=parser['flake8']['ignore'], exclude=parser['flake8']['exclude'], ) report = flake8_style_guide.check_files([ os.path.join(output_dir, data['project_slug'], data['package_name'], 'extension.py'), os.path.join(output_dir, data['project_slug'], 'tests', f'test_{data["project_slug"]}.py'), ]) assert report.total_errors == 0 extension_py = open( os.path.join(output_dir, data['project_slug'], data['package_name'], 'extension.py'), ).read() expected_imports = extension_imports( with_schedulable=with_schedulable, with_variables=with_variables, ) assert expected_imports in extension_py assert extension_class_declaration( classname_prefix, with_variables=with_variables, ) in extension_py assert extension_bg_event(async_impl=async_impl) in extension_py test_py = open( os.path.join(output_dir, data['project_slug'], 'tests', f'test_{data["project_slug"]}.py'), ).read() assert test_bg_event(classname_prefix, async_impl=async_impl) in test_py if with_schedulable: assert extension_schedulable_event(async_impl=async_impl) in extension_py assert test_schedulable_event(classname_prefix, async_impl=async_impl) in test_py
'My Awesome Project', 'validators': (RequiredValidator(message='Please, provide a project name.'), ), }, { 'name': 'project_slug', 'label': 'Project: root', 'type': 'input', 'description': ('Choose a name for the root folder of your Reports module.' ' This must be a valid python\nidentifier:'), 'default': lambda x: slugify(x['project_name']), 'validators': ( RequiredValidator( message='Please, provide a project root folder.'), PythonIdentifierValidator(), ), }, { 'name': 'author', 'label': 'Project: author', 'type': 'input', 'description': 'Please enter the author for this project:',
def get_questions(config, definitions): project = [ { 'name': 'project_name', 'label': 'Project: name', 'type': 'input', 'description': 'Enter a friendly name for your Extension:', 'default': 'My Awesome Project', 'validators': (RequiredValidator(message='Please, provide a project name.'), ), }, { 'name': 'project_slug', 'label': 'Project: root', 'type': 'input', 'description': 'Choose a name for the root folder of your Extension module.', 'default': lambda ctx: slugify(ctx['project_name']), 'validators': (RequiredValidator( message='Please, provide a project root folder.'), ), }, { 'name': 'description', 'label': 'Project: description', 'type': 'input', 'description': 'Briefly describe your Extension:', 'default': 'Project description', 'validators': (RequiredValidator(message='Please, provide a description.'), ), }, { 'name': 'package_name', 'label': 'Project: package', 'type': 'input', 'description': ('Choose a name for the root python package of your extension module.' ' This must be a valid python\nidentifier:'), 'default': 'connect_ext', 'validators': ( RequiredValidator(message='Please, provide a package name.'), PythonIdentifierValidator(), ), }, { 'name': 'use_asyncio', 'label': 'Project: asyncio', 'type': 'selectone', 'description': 'Do you want to develop your Extension using the Python asyncio library ?', 'values': [ ('n', 'No'), ('y', 'Yes'), ], 'formatting_template': '${label}', }, { 'name': 'author', 'label': 'Project: author', 'type': 'input', 'description': 'Enter the name of the Extension author:', 'default': 'Globex Corporation', 'validators': (RequiredValidator(message='Please, provide an author name.'), ), }, { 'name': 'version', 'label': 'Project: version', 'type': 'input', 'description': 'Introduce a version identifier for your Extension:', 'default': '0.1.0', 'validators': (RequiredValidator(message='Please, provide a version.'), ), }, { 'name': 'license', 'label': 'Project: license', 'type': 'selectone', 'description': 'Choose an Open Source license for your Extension:', 'values': [ ('Apache Software License 2.0', 'Apache Software License 2.0'), ('MIT', 'MIT'), ('BSD', 'BSD'), ], 'formatting_template': '${label}', }, { 'name': 'use_github_actions', 'label': 'Project: CI', 'type': 'selectone', 'description': ('If you plan to host the Extension code on GitHub, are you also planning\n' 'to use GitHub actions as your continuous integration system ?'), 'values': [ ('n', 'No'), ('y', 'Yes'), ], 'formatting_template': '${label}', }, ] environment = [ { 'name': 'api_key', 'label': 'Config: API key', 'type': 'input', 'description': ('Enter the API key that will be used to authenticate API calls to Connect:\n' 'The API key can be created within the integration module of Connect.\n' 'It defaults to the current active CLI account.'), 'default': config.active.api_key, 'validators': (RequiredValidator(message='Please, provide an API Key.'), ), }, { 'name': 'environment_id', 'label': 'Config: environment ID', 'type': 'input', 'description': ('Enter the DevOps environment identifier to which your Extension will connect:\n' 'It can be found in the DevOps module of Connect within the Local Access widget.\n' 'See: https://connect.cloudblue.com/community/modules/devops/user-interface/#Service_Details' ), 'validators': (RequiredValidator( message='Please, provide a DevOps environment ID.'), ), }, { 'name': 'server_address', 'label': 'Config: API hostname', 'type': 'input', 'description': 'Connect API hostname: ', 'default': urlparse(config.active.endpoint).netloc, 'validators': (RequiredValidator(message='Please, provide the API hostname.'), ), }, ] event_questions = [] for category, cagegory_events in definitions.items(): for group, events in cagegory_events.items(): event_questions.append( { 'name': f'{category}_{slugify(group.lower())}', 'label': f'{category.title()}: {group.lower()}', 'type': 'selectmany', 'description': (f'What types of {group.title()} {category} ' 'events do you want your Extension to process ?'), 'values': [(event['type'], event['name']) for event in events], 'formatting_template': '${label}', }, ) examples = [ { 'name': 'include_schedules_example', 'label': 'Examples: schedulables', 'type': 'selectone', 'description': 'Do you want to include an example of schedulable method ? ', 'values': [ ('n', 'No'), ('y', 'Yes'), ], 'formatting_template': '${label}', }, { 'name': 'include_variables_example', 'label': 'Examples: variables', 'type': 'selectone', 'description': 'Do you want to include an example of environment variables ? ', 'values': [ ('n', 'No'), ('y', 'Yes'), ], 'formatting_template': '${label}', }, ] return project + environment + event_questions + examples