Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 33 additions & 5 deletions connect/cli/plugins/project/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@

import click

from connect.cli.plugins.project.helpers import (
from connect.cli.plugins.project.extension_helpers import (
bootstrap_extension_project,
)
from connect.cli.plugins.project.report_helpers import (
add_report,
bootstrap_project,
validate_project,
bootstrap_report_project,
validate_report_project,
)


Expand All @@ -16,6 +19,7 @@ def grp_project():
pass # pragma: no cover


# REPORTS
@grp_project.group(
name='report',
short_help='Manage report projects.',
Expand All @@ -36,7 +40,7 @@ def grp_project_report():
help='Directory where the new report project will be created.',
)
def cmd_bootstrap_report_project(output_dir):
bootstrap_project(output_dir)
bootstrap_report_project(output_dir)


@grp_project_report.command(
Expand All @@ -50,7 +54,7 @@ def cmd_bootstrap_report_project(output_dir):
help='Project directory.',
)
def cmd_validate_project(project_dir):
validate_project(project_dir)
validate_report_project(project_dir)


@grp_project_report.command(
Expand All @@ -72,5 +76,29 @@ def cmd_add_report(project_dir, package_name):
add_report(project_dir, package_name)


# EXTENSION AS A SERVICE
@grp_project.group(
name='extension',
short_help='Manage extension projects.',
)
def grp_project_extension():
pass # pragma: no cover


@grp_project_extension.command(
name='bootstrap',
short_help='Bootstrap new extension project.',
)
@click.option(
'--output-dir', '-o',
default=os.getcwd(),
type=click.Path(exists=False, file_okay=False, dir_okay=True),
required=False,
help='Directory where the new extension project will be created.',
)
def cmd_bootstrap_extension_project(output_dir):
bootstrap_extension_project(output_dir)


def get_group():
return grp_project
3 changes: 2 additions & 1 deletion connect/cli/plugins/project/constants.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Copyright © 2021 CloudBlue. All rights reserved.

PROJECT_BOILERPLATE_URL = 'https://github.com/cloudblue/connect-report-python-boilerplate.git'
PROJECT_REPORT_BOILERPLATE_URL = 'https://github.com/cloudblue/connect-report-python-boilerplate.git'
PROJECT_EXTENSION_BOILERPLATE_URL = 'https://github.com/cloudblue/connect-extension-python-boilerplate.git'
43 changes: 43 additions & 0 deletions connect/cli/plugins/project/extension_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import click
from click.exceptions import ClickException
from cookiecutter.exceptions import OutputDirExistsException
from cookiecutter.main import cookiecutter

from connect.cli.plugins.project.constants import PROJECT_EXTENSION_BOILERPLATE_URL
from connect.cli.plugins.project import utils


def bootstrap_extension_project(data_dir: str):
click.secho('Bootstraping extension project...\n', fg='blue')

utils._purge_cookiecutters_dir()

index = 1
answers = {}
function_list = [
'_general_questions',
'_asset_process_capabilities',
'_asset_validation_capabilities',
'_tier_config_capabilities',
'_product_capabilities',
]
for question_function in function_list:
partial = getattr(utils, question_function)(index, len(function_list))
index += 1
answers.update(partial)

try:
project_dir = cookiecutter(
PROJECT_EXTENSION_BOILERPLATE_URL,
no_input=True,
extra_context=answers,
output_dir=data_dir,
)
click.secho(f'\nExtension Project location: {project_dir}', fg='blue')
except OutputDirExistsException as error:
project_path = str(error).split('"')[1]
raise ClickException(
f'\nThe directory "{project_path}" already exists, '
'\nif you would like to use that name, please delete '
'the directory or use another location.',
)
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from collections import OrderedDict

from cookiecutter.main import cookiecutter
from cookiecutter.config import DEFAULT_CONFIG, get_user_config
from cookiecutter.config import get_user_config
from cookiecutter.exceptions import OutputDirExistsException
from cookiecutter.generate import generate_context, generate_files
from cookiecutter.prompt import prompt_for_config
Expand All @@ -20,22 +20,23 @@
from click import ClickException

from connect.cli.plugins.project.constants import (
PROJECT_BOILERPLATE_URL,
PROJECT_REPORT_BOILERPLATE_URL,
)
from connect.cli.plugins.project.utils import _purge_cookiecutters_dir
from connect.reports.validator import (
validate,
validate_with_schema,
)
from connect.reports.parser import parse


def bootstrap_project(data_dir: str):
def bootstrap_report_project(data_dir: str):
click.secho('Bootstraping report project...\n', fg='blue')

_purge_cookiecutters_dir()

try:
project_dir = cookiecutter(PROJECT_BOILERPLATE_URL, output_dir=data_dir)
project_dir = cookiecutter(PROJECT_REPORT_BOILERPLATE_URL, output_dir=data_dir)
click.secho(f'\nReport Project location: {project_dir}', fg='blue')
except OutputDirExistsException as error:
project_path = str(error).split('"')[1]
Expand All @@ -46,7 +47,7 @@ def bootstrap_project(data_dir: str):
)


def validate_project(project_dir):
def validate_report_project(project_dir):
click.secho(f'Validating project {project_dir}...\n', fg='blue')

data = _file_descriptor_validations(project_dir)
Expand Down Expand Up @@ -86,7 +87,7 @@ def add_report(project_dir, package_name):

# Instead of using cookiecutter use the internals
report_dir, report_slug = _custom_cookiecutter(
PROJECT_BOILERPLATE_URL,
PROJECT_REPORT_BOILERPLATE_URL,
output_dir=add_report_tmpdir,
)

Expand Down Expand Up @@ -228,10 +229,3 @@ def _entrypoint_validations(project_dir, entrypoint, report_spec):
'\n>> def generate(client=None, input_data=None, progress_callback=None, '
'renderer_type=None, extra_context_callback=None) <<',
)


def _purge_cookiecutters_dir():
# Avoid asking rewrite clone boilerplate project
cookie_dir = DEFAULT_CONFIG['cookiecutters_dir']
if os.path.isdir(cookie_dir):
rmtree(cookie_dir)
174 changes: 174 additions & 0 deletions connect/cli/plugins/project/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import os

from click.exceptions import ClickException
from cookiecutter.config import DEFAULT_CONFIG
from cookiecutter.utils import rmtree
from interrogatio import dialogus
from interrogatio.validators.builtins import RequiredValidator


def _purge_cookiecutters_dir():
# Avoid asking rewrite clone boilerplate project
cookie_dir = DEFAULT_CONFIG['cookiecutters_dir']
if os.path.isdir(cookie_dir):
rmtree(cookie_dir)


def _general_questions(idx, total):
questions = [
{
'name': 'project_name',
'type': 'input',
'message': 'Extension project name: ',
'default': 'My Awesome Project',
'validators': (RequiredValidator(message='Please, provide a project name.'),),
},
{
'name': 'description',
'type': 'input',
'message': 'Extension project description: ',
'default': 'Project description',
'validators': (RequiredValidator(message='Please, provide a description.'),),
},
{
'name': 'package_name',
'type': 'input',
'message': 'Extension project package name: ',
'default': 'connect_ext',
'validators': (RequiredValidator(message='Please, provide a package name.'),),
},
{
'name': 'author',
'type': 'input',
'message': 'Extension project author: ',
'default': 'Globex Corporation',
'validators': (RequiredValidator(message='Please, provide an author name.'),),
},
{
'name': 'version',
'type': 'input',
'message': 'Extension project version: ',
'default': '0.1.0',
'validators': (RequiredValidator(message='Please, provide a version.'),),
},
{
'name': 'license',
'type': 'selectone',
'description': 'Extension project license: ',
'values': [
('Apache Software License 2.0', 'Apache Software License 2.0'),
('MIT', 'MIT'),
('BSD', 'BSD'),
],
},
{
'name': 'use_github_actions',
'type': 'selectone',
'description': 'Do you want to use Github actions? ',
'values': [
('n', 'No'),
('y', 'Yes'),
],
},
{
'name': 'use_asyncio',
'type': 'selectone',
'description': 'Do you want to use asynchronous libraries? ',
'values': [
('n', 'No'),
('y', 'Yes'),
],
},
]
answers = _show_dialog(questions, idx, total)
return answers


def _asset_process_capabilities(idx, total):
questions = [
{
'name': 'asset_processing',
'type': 'selectmany',
'description': 'Asset processing capabilities',
'values': [
('subscription_process_capabilities_1of6', 'Purchase Request'),
('subscription_process_capabilities_2of6', 'Change Request'),
('subscription_process_capabilities_3of6', 'Suspend Request'),
('subscription_process_capabilities_4of6', 'Resume Request'),
('subscription_process_capabilities_5of6', 'Cancel Request'),
('subscription_process_capabilities_6of6', 'Adjustment Request'),
],
},
]
answers = _show_dialog(questions, idx, total)
return _gen_cookie_capabilities(answers['asset_processing'])


def _asset_validation_capabilities(idx, total):
questions = [
{
'name': 'asset_validation',
'type': 'selectmany',
'description': 'Asset validation capabilities',
'values': [
('subscription_validation_capabilities_1of2', 'Purchase Request'),
('subscription_validation_capabilities_2of2', 'Change Request'),
],
},
]
answers = _show_dialog(questions, idx, total)
return _gen_cookie_capabilities(answers['asset_validation'])


def _tier_config_capabilities(idx, total):
questions = [
{
'name': 'tierconfig',
'type': 'selectmany',
'description': 'Tier configuration capabilities',
'values': [
('tier_config_process_capabilities_1of2', 'Setup Request Processing'),
('tier_config_process_capabilities_2of2', 'Change Request Processing'),
('tier_config_validation_capabilities_1of2', 'Setup Request Validation'),
('tier_config_validation_capabilities_1of2', 'Change Request Validation'),
],
},
]
answers = _show_dialog(questions, idx, total)
return _gen_cookie_capabilities(answers['tierconfig'])


def _product_capabilities(idx, total):
questions = [
{
'name': 'product',
'type': 'selectmany',
'description': 'Product capabilities',
'values': [
('product_capabilities_1of2', 'Product Action Execution'),
('product_capabilities_2of2', 'Product Custom Event'),
],
},
]
answers = _show_dialog(questions, idx, total)
return _gen_cookie_capabilities(answers['product'])


def _show_dialog(questions, idx, total):
answers = dialogus(
questions,
title=f'Extension Project Configuration ({idx}/{total})',
confirm='Next',
)
if not answers:
raise ClickException('Aborted by user input')
return answers


def _gen_cookie_capabilities(answers):
cookicutter_answers = {}
if answers:
for capability in answers:
cookicutter_answers[capability] = 'y'

return cookicutter_answers
3 changes: 0 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@
from shutil import copy2

import pytest

import responses

from fs.tempfs import TempFS

from openpyxl import load_workbook

from connect.cli.core.base import cli
Expand Down
4 changes: 2 additions & 2 deletions tests/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
'name': 'Account 1',
'api_key': 'ApiKey ZZZZ:SSSS',
'endpoint': 'https://localhost/public/v1',
}
},
],
}
}
Loading