Skip to content

Commit 1f4f5e4

Browse files
authored
Merge pull request #54 from cloudblue/feature/LITE-18766_cli_extension_bootstrap_cmd
LITE-18766: Add bootstrap command for extension projects
2 parents ee45238 + 410fe44 commit 1f4f5e4

File tree

12 files changed

+420
-64
lines changed

12 files changed

+420
-64
lines changed

connect/cli/plugins/project/commands.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55
import click
66

7-
from connect.cli.plugins.project.helpers import (
7+
from connect.cli.plugins.project.extension_helpers import (
8+
bootstrap_extension_project,
9+
)
10+
from connect.cli.plugins.project.report_helpers import (
811
add_report,
9-
bootstrap_project,
10-
validate_project,
12+
bootstrap_report_project,
13+
validate_report_project,
1114
)
1215

1316

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

1821

22+
# REPORTS
1923
@grp_project.group(
2024
name='report',
2125
short_help='Manage report projects.',
@@ -36,7 +40,7 @@ def grp_project_report():
3640
help='Directory where the new report project will be created.',
3741
)
3842
def cmd_bootstrap_report_project(output_dir):
39-
bootstrap_project(output_dir)
43+
bootstrap_report_project(output_dir)
4044

4145

4246
@grp_project_report.command(
@@ -50,7 +54,7 @@ def cmd_bootstrap_report_project(output_dir):
5054
help='Project directory.',
5155
)
5256
def cmd_validate_project(project_dir):
53-
validate_project(project_dir)
57+
validate_report_project(project_dir)
5458

5559

5660
@grp_project_report.command(
@@ -72,5 +76,29 @@ def cmd_add_report(project_dir, package_name):
7276
add_report(project_dir, package_name)
7377

7478

79+
# EXTENSION AS A SERVICE
80+
@grp_project.group(
81+
name='extension',
82+
short_help='Manage extension projects.',
83+
)
84+
def grp_project_extension():
85+
pass # pragma: no cover
86+
87+
88+
@grp_project_extension.command(
89+
name='bootstrap',
90+
short_help='Bootstrap new extension project.',
91+
)
92+
@click.option(
93+
'--output-dir', '-o',
94+
default=os.getcwd(),
95+
type=click.Path(exists=False, file_okay=False, dir_okay=True),
96+
required=False,
97+
help='Directory where the new extension project will be created.',
98+
)
99+
def cmd_bootstrap_extension_project(output_dir):
100+
bootstrap_extension_project(output_dir)
101+
102+
75103
def get_group():
76104
return grp_project
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# Copyright © 2021 CloudBlue. All rights reserved.
22

3-
PROJECT_BOILERPLATE_URL = 'https://github.com/cloudblue/connect-report-python-boilerplate.git'
3+
PROJECT_REPORT_BOILERPLATE_URL = 'https://github.com/cloudblue/connect-report-python-boilerplate.git'
4+
PROJECT_EXTENSION_BOILERPLATE_URL = 'https://github.com/cloudblue/connect-extension-python-boilerplate.git'
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import click
2+
from click.exceptions import ClickException
3+
from cookiecutter.exceptions import OutputDirExistsException
4+
from cookiecutter.main import cookiecutter
5+
6+
from connect.cli.plugins.project.constants import PROJECT_EXTENSION_BOILERPLATE_URL
7+
from connect.cli.plugins.project import utils
8+
9+
10+
def bootstrap_extension_project(data_dir: str):
11+
click.secho('Bootstraping extension project...\n', fg='blue')
12+
13+
utils._purge_cookiecutters_dir()
14+
15+
index = 1
16+
answers = {}
17+
function_list = [
18+
'_general_questions',
19+
'_asset_process_capabilities',
20+
'_asset_validation_capabilities',
21+
'_tier_config_capabilities',
22+
'_product_capabilities',
23+
]
24+
for question_function in function_list:
25+
partial = getattr(utils, question_function)(index, len(function_list))
26+
index += 1
27+
answers.update(partial)
28+
29+
try:
30+
project_dir = cookiecutter(
31+
PROJECT_EXTENSION_BOILERPLATE_URL,
32+
no_input=True,
33+
extra_context=answers,
34+
output_dir=data_dir,
35+
)
36+
click.secho(f'\nExtension Project location: {project_dir}', fg='blue')
37+
except OutputDirExistsException as error:
38+
project_path = str(error).split('"')[1]
39+
raise ClickException(
40+
f'\nThe directory "{project_path}" already exists, '
41+
'\nif you would like to use that name, please delete '
42+
'the directory or use another location.',
43+
)

connect/cli/plugins/project/helpers.py renamed to connect/cli/plugins/project/report_helpers.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from collections import OrderedDict
1111

1212
from cookiecutter.main import cookiecutter
13-
from cookiecutter.config import DEFAULT_CONFIG, get_user_config
13+
from cookiecutter.config import get_user_config
1414
from cookiecutter.exceptions import OutputDirExistsException
1515
from cookiecutter.generate import generate_context, generate_files
1616
from cookiecutter.prompt import prompt_for_config
@@ -20,22 +20,23 @@
2020
from click import ClickException
2121

2222
from connect.cli.plugins.project.constants import (
23-
PROJECT_BOILERPLATE_URL,
23+
PROJECT_REPORT_BOILERPLATE_URL,
2424
)
25+
from connect.cli.plugins.project.utils import _purge_cookiecutters_dir
2526
from connect.reports.validator import (
2627
validate,
2728
validate_with_schema,
2829
)
2930
from connect.reports.parser import parse
3031

3132

32-
def bootstrap_project(data_dir: str):
33+
def bootstrap_report_project(data_dir: str):
3334
click.secho('Bootstraping report project...\n', fg='blue')
3435

3536
_purge_cookiecutters_dir()
3637

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

4849

49-
def validate_project(project_dir):
50+
def validate_report_project(project_dir):
5051
click.secho(f'Validating project {project_dir}...\n', fg='blue')
5152

5253
data = _file_descriptor_validations(project_dir)
@@ -86,7 +87,7 @@ def add_report(project_dir, package_name):
8687

8788
# Instead of using cookiecutter use the internals
8889
report_dir, report_slug = _custom_cookiecutter(
89-
PROJECT_BOILERPLATE_URL,
90+
PROJECT_REPORT_BOILERPLATE_URL,
9091
output_dir=add_report_tmpdir,
9192
)
9293

@@ -228,10 +229,3 @@ def _entrypoint_validations(project_dir, entrypoint, report_spec):
228229
'\n>> def generate(client=None, input_data=None, progress_callback=None, '
229230
'renderer_type=None, extra_context_callback=None) <<',
230231
)
231-
232-
233-
def _purge_cookiecutters_dir():
234-
# Avoid asking rewrite clone boilerplate project
235-
cookie_dir = DEFAULT_CONFIG['cookiecutters_dir']
236-
if os.path.isdir(cookie_dir):
237-
rmtree(cookie_dir)
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import os
2+
3+
from click.exceptions import ClickException
4+
from cookiecutter.config import DEFAULT_CONFIG
5+
from cookiecutter.utils import rmtree
6+
from interrogatio import dialogus
7+
from interrogatio.validators.builtins import RequiredValidator
8+
9+
10+
def _purge_cookiecutters_dir():
11+
# Avoid asking rewrite clone boilerplate project
12+
cookie_dir = DEFAULT_CONFIG['cookiecutters_dir']
13+
if os.path.isdir(cookie_dir):
14+
rmtree(cookie_dir)
15+
16+
17+
def _general_questions(idx, total):
18+
questions = [
19+
{
20+
'name': 'project_name',
21+
'type': 'input',
22+
'message': 'Extension project name: ',
23+
'default': 'My Awesome Project',
24+
'validators': (RequiredValidator(message='Please, provide a project name.'),),
25+
},
26+
{
27+
'name': 'description',
28+
'type': 'input',
29+
'message': 'Extension project description: ',
30+
'default': 'Project description',
31+
'validators': (RequiredValidator(message='Please, provide a description.'),),
32+
},
33+
{
34+
'name': 'package_name',
35+
'type': 'input',
36+
'message': 'Extension project package name: ',
37+
'default': 'connect_ext',
38+
'validators': (RequiredValidator(message='Please, provide a package name.'),),
39+
},
40+
{
41+
'name': 'author',
42+
'type': 'input',
43+
'message': 'Extension project author: ',
44+
'default': 'Globex Corporation',
45+
'validators': (RequiredValidator(message='Please, provide an author name.'),),
46+
},
47+
{
48+
'name': 'version',
49+
'type': 'input',
50+
'message': 'Extension project version: ',
51+
'default': '0.1.0',
52+
'validators': (RequiredValidator(message='Please, provide a version.'),),
53+
},
54+
{
55+
'name': 'license',
56+
'type': 'selectone',
57+
'description': 'Extension project license: ',
58+
'values': [
59+
('Apache Software License 2.0', 'Apache Software License 2.0'),
60+
('MIT', 'MIT'),
61+
('BSD', 'BSD'),
62+
],
63+
},
64+
{
65+
'name': 'use_github_actions',
66+
'type': 'selectone',
67+
'description': 'Do you want to use Github actions? ',
68+
'values': [
69+
('n', 'No'),
70+
('y', 'Yes'),
71+
],
72+
},
73+
{
74+
'name': 'use_asyncio',
75+
'type': 'selectone',
76+
'description': 'Do you want to use asynchronous libraries? ',
77+
'values': [
78+
('n', 'No'),
79+
('y', 'Yes'),
80+
],
81+
},
82+
]
83+
answers = _show_dialog(questions, idx, total)
84+
return answers
85+
86+
87+
def _asset_process_capabilities(idx, total):
88+
questions = [
89+
{
90+
'name': 'asset_processing',
91+
'type': 'selectmany',
92+
'description': 'Asset processing capabilities',
93+
'values': [
94+
('subscription_process_capabilities_1of6', 'Purchase Request'),
95+
('subscription_process_capabilities_2of6', 'Change Request'),
96+
('subscription_process_capabilities_3of6', 'Suspend Request'),
97+
('subscription_process_capabilities_4of6', 'Resume Request'),
98+
('subscription_process_capabilities_5of6', 'Cancel Request'),
99+
('subscription_process_capabilities_6of6', 'Adjustment Request'),
100+
],
101+
},
102+
]
103+
answers = _show_dialog(questions, idx, total)
104+
return _gen_cookie_capabilities(answers['asset_processing'])
105+
106+
107+
def _asset_validation_capabilities(idx, total):
108+
questions = [
109+
{
110+
'name': 'asset_validation',
111+
'type': 'selectmany',
112+
'description': 'Asset validation capabilities',
113+
'values': [
114+
('subscription_validation_capabilities_1of2', 'Purchase Request'),
115+
('subscription_validation_capabilities_2of2', 'Change Request'),
116+
],
117+
},
118+
]
119+
answers = _show_dialog(questions, idx, total)
120+
return _gen_cookie_capabilities(answers['asset_validation'])
121+
122+
123+
def _tier_config_capabilities(idx, total):
124+
questions = [
125+
{
126+
'name': 'tierconfig',
127+
'type': 'selectmany',
128+
'description': 'Tier configuration capabilities',
129+
'values': [
130+
('tier_config_process_capabilities_1of2', 'Setup Request Processing'),
131+
('tier_config_process_capabilities_2of2', 'Change Request Processing'),
132+
('tier_config_validation_capabilities_1of2', 'Setup Request Validation'),
133+
('tier_config_validation_capabilities_1of2', 'Change Request Validation'),
134+
],
135+
},
136+
]
137+
answers = _show_dialog(questions, idx, total)
138+
return _gen_cookie_capabilities(answers['tierconfig'])
139+
140+
141+
def _product_capabilities(idx, total):
142+
questions = [
143+
{
144+
'name': 'product',
145+
'type': 'selectmany',
146+
'description': 'Product capabilities',
147+
'values': [
148+
('product_capabilities_1of2', 'Product Action Execution'),
149+
('product_capabilities_2of2', 'Product Custom Event'),
150+
],
151+
},
152+
]
153+
answers = _show_dialog(questions, idx, total)
154+
return _gen_cookie_capabilities(answers['product'])
155+
156+
157+
def _show_dialog(questions, idx, total):
158+
answers = dialogus(
159+
questions,
160+
title=f'Extension Project Configuration ({idx}/{total})',
161+
confirm='Next',
162+
)
163+
if not answers:
164+
raise ClickException('Aborted by user input')
165+
return answers
166+
167+
168+
def _gen_cookie_capabilities(answers):
169+
cookicutter_answers = {}
170+
if answers:
171+
for capability in answers:
172+
cookicutter_answers[capability] = 'y'
173+
174+
return cookicutter_answers

tests/conftest.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@
33
from shutil import copy2
44

55
import pytest
6-
76
import responses
8-
97
from fs.tempfs import TempFS
10-
118
from openpyxl import load_workbook
129

1310
from connect.cli.core.base import cli

tests/data.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212
'name': 'Account 1',
1313
'api_key': 'ApiKey ZZZZ:SSSS',
1414
'endpoint': 'https://localhost/public/v1',
15-
}
15+
},
1616
],
17-
}
17+
}

0 commit comments

Comments
 (0)