Skip to content

Commit

Permalink
Dbt init with provided project name
Browse files Browse the repository at this point in the history
  • Loading branch information
kadero committed Nov 9, 2021
1 parent 178f74b commit 89cae6e
Show file tree
Hide file tree
Showing 3 changed files with 297 additions and 19 deletions.
16 changes: 16 additions & 0 deletions core/dbt/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,22 @@ def _build_init_subparser(subparsers, base_subparser):
Initialize a new DBT project.
'''
)
sub.add_argument(
'project_name',
nargs='?',
help='''
Name of the new DBT new project.
'''
)
sub.add_argument(
'-s',
'--skip-profile-setup',
dest='skip_profile_setup',
action='store_true',
help='''
Skip interative profile setup.
'''
)
sub.set_defaults(cls=init_task.InitTask, which='init', rpc_method=None)
return sub

Expand Down
46 changes: 27 additions & 19 deletions core/dbt/task/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,28 +295,36 @@ def run(self):
self.create_profile_from_target(
adapter, profile_name=profile_name
)
else:
# When dbt init is run outside of an existing project,
# create a new project and set up the user's profile.
return

# When dbt init is run outside of an existing project,
# create a new project and set up the user's profile.
project_name = self.args.project_name
if project_name is None:
# If project name is not provided,
# ask the user which project name they'd like to use.
project_name = click.prompt("What is the desired project name?")
project_path = Path(project_name)
if project_path.exists():
logger.info(
f"A project called {project_name} already exists here."
)
return

self.copy_starter_repo(project_name)
os.chdir(project_name)
with open("dbt_project.yml", "r+") as f:
content = f"{f.read()}".format(
project_name=project_name,
profile_name=project_name
)
f.seek(0)
f.write(content)
f.truncate()
project_path = Path(project_name)
if project_path.exists():
logger.info(
f"A project called {project_name} already exists here."
)
return

self.copy_starter_repo(project_name)
os.chdir(project_name)
with open("dbt_project.yml", "r+") as f:
content = f"{f.read()}".format(
project_name=project_name,
profile_name=project_name
)
f.seek(0)
f.write(content)
f.truncate()

# Ask for adapter only if skip_profile_setup flag is not provided.
if not self.args.skip_profile_setup:
if not self.check_if_can_write_profile(profile_name=project_name):
return
adapter = self.ask_for_adapter_choice()
Expand Down
254 changes: 254 additions & 0 deletions test/integration/040_init_test/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,260 @@ def test_postgres_init_task_outside_of_project(self, mock_prompt, mock_confirm):
- "dbt_packages"
# Configuring models
# Full documentation: https://docs.getdbt.com/docs/configuring-models
# In this example config, we tell dbt to build all models in the example/ directory
# as tables. These settings can be overridden in the individual model files
# using the `{{{{ config(...) }}}}` macro.
models:
{project_name}:
# Config indicated by + and applies to all files under models/example/
example:
+materialized: view
"""

@use_profile('postgres')
@mock.patch('click.confirm')
@mock.patch('click.prompt')
def test_postgres_init_with_provided_project_name(self, mock_prompt, mock_confirm):
manager = Mock()
manager.attach_mock(mock_prompt, 'prompt')
manager.attach_mock(mock_confirm, 'confirm')

# Start by removing the dbt_project.yml so that we're not in an existing project
os.remove('dbt_project.yml')

manager.prompt.side_effect = [
1,
'localhost',
5432,
'test_username',
'test_password',
'test_db',
'test_schema',
4,
]

# Provide project name through the init command.
project_name = self.get_project_name()
self.run_dbt(['init', project_name])
manager.assert_has_calls([
call.prompt("Which database would you like to use?\n[1] postgres\n\n(Don't see the one you want? https://docs.getdbt.com/docs/available-adapters)\n\nEnter a number", type=click.INT),
call.prompt('host (hostname for the instance)', default=None, hide_input=False, type=None),
call.prompt('port', default=5432, hide_input=False, type=click.INT),
call.prompt('user (dev username)', default=None, hide_input=False, type=None),
call.prompt('pass (dev password)', default=None, hide_input=True, type=None),
call.prompt('dbname (default database that dbt will build objects in)', default=None, hide_input=False, type=None),
call.prompt('schema (default schema that dbt will build objects in)', default=None, hide_input=False, type=None),
call.prompt('threads (1 or more)', default=1, hide_input=False, type=click.INT),
])

with open(os.path.join(self.test_root_dir, 'profiles.yml'), 'r') as f:
assert f.read() == f"""config:
send_anonymous_usage_stats: false
{project_name}:
outputs:
dev:
dbname: test_db
host: localhost
pass: test_password
port: 5432
schema: test_schema
threads: 4
type: postgres
user: test_username
target: dev
test:
outputs:
default2:
dbname: dbt
host: localhost
pass: password
port: 5432
schema: {self.unique_schema()}
threads: 4
type: postgres
user: root
noaccess:
dbname: dbt
host: localhost
pass: password
port: 5432
schema: {self.unique_schema()}
threads: 4
type: postgres
user: noaccess
target: default2
"""

with open(os.path.join(self.test_root_dir, project_name, 'dbt_project.yml'), 'r') as f:
assert f.read() == f"""
# Name your project! Project names should contain only lowercase characters
# and underscores. A good package name should reflect your organization's
# name or the intended use of these models
name: '{project_name}'
version: '1.0.0'
config-version: 2
# This setting configures which "profile" dbt uses for this project.
profile: '{project_name}'
# These configurations specify where dbt should look for different types of files.
# The `model-paths` config, for example, states that models in this project can be
# found in the "models/" directory. You probably won't need to change these!
model-paths: ["models"]
analysis-paths: ["analyses"]
test-paths: ["tests"]
seed-paths: ["seeds"]
macro-paths: ["macros"]
snapshot-paths: ["snapshots"]
target-path: "target" # directory which will store compiled SQL files
clean-targets: # directories to be removed by `dbt clean`
- "target"
- "dbt_packages"
# Configuring models
# Full documentation: https://docs.getdbt.com/docs/configuring-models
# In this example config, we tell dbt to build all models in the example/ directory
# as tables. These settings can be overridden in the individual model files
# using the `{{{{ config(...) }}}}` macro.
models:
{project_name}:
# Config indicated by + and applies to all files under models/example/
example:
+materialized: view
"""

@use_profile('postgres')
@mock.patch('click.confirm')
@mock.patch('click.prompt')
def test_postgres_init_skip_profile_setup(self, mock_prompt, mock_confirm):
manager = Mock()
manager.attach_mock(mock_prompt, 'prompt')
manager.attach_mock(mock_confirm, 'confirm')

# Start by removing the dbt_project.yml so that we're not in an existing project
os.remove('dbt_project.yml')

project_name = self.get_project_name()
manager.prompt.side_effect = [
project_name,
1,
'localhost',
5432,
'test_username',
'test_password',
'test_db',
'test_schema',
4,
]

# provide project name through the ini command
self.run_dbt(['init', '-s'])
manager.assert_has_calls([
call.prompt('What is the desired project name?')
])

with open(os.path.join(self.test_root_dir, project_name, 'dbt_project.yml'), 'r') as f:
assert f.read() == f"""
# Name your project! Project names should contain only lowercase characters
# and underscores. A good package name should reflect your organization's
# name or the intended use of these models
name: '{project_name}'
version: '1.0.0'
config-version: 2
# This setting configures which "profile" dbt uses for this project.
profile: '{project_name}'
# These configurations specify where dbt should look for different types of files.
# The `model-paths` config, for example, states that models in this project can be
# found in the "models/" directory. You probably won't need to change these!
model-paths: ["models"]
analysis-paths: ["analyses"]
test-paths: ["tests"]
seed-paths: ["seeds"]
macro-paths: ["macros"]
snapshot-paths: ["snapshots"]
target-path: "target" # directory which will store compiled SQL files
clean-targets: # directories to be removed by `dbt clean`
- "target"
- "dbt_packages"
# Configuring models
# Full documentation: https://docs.getdbt.com/docs/configuring-models
# In this example config, we tell dbt to build all models in the example/ directory
# as tables. These settings can be overridden in the individual model files
# using the `{{{{ config(...) }}}}` macro.
models:
{project_name}:
# Config indicated by + and applies to all files under models/example/
example:
+materialized: view
"""

@use_profile('postgres')
@mock.patch('click.confirm')
@mock.patch('click.prompt')
def test_postgres_init_provided_project_name_and_skip_profile_setup(self, mock_prompt, mock_confirm):
manager = Mock()
manager.attach_mock(mock_prompt, 'prompt')
manager.attach_mock(mock_confirm, 'confirm')

# Start by removing the dbt_project.yml so that we're not in an existing project
os.remove('dbt_project.yml')

manager.prompt.side_effect = [
1,
'localhost',
5432,
'test_username',
'test_password',
'test_db',
'test_schema',
4,
]

# provide project name through the ini command
project_name = self.get_project_name()
self.run_dbt(['init', project_name, '-s'])
manager.assert_not_called()

with open(os.path.join(self.test_root_dir, project_name, 'dbt_project.yml'), 'r') as f:
assert f.read() == f"""
# Name your project! Project names should contain only lowercase characters
# and underscores. A good package name should reflect your organization's
# name or the intended use of these models
name: '{project_name}'
version: '1.0.0'
config-version: 2
# This setting configures which "profile" dbt uses for this project.
profile: '{project_name}'
# These configurations specify where dbt should look for different types of files.
# The `model-paths` config, for example, states that models in this project can be
# found in the "models/" directory. You probably won't need to change these!
model-paths: ["models"]
analysis-paths: ["analyses"]
test-paths: ["tests"]
seed-paths: ["seeds"]
macro-paths: ["macros"]
snapshot-paths: ["snapshots"]
target-path: "target" # directory which will store compiled SQL files
clean-targets: # directories to be removed by `dbt clean`
- "target"
- "dbt_packages"
# Configuring models
# Full documentation: https://docs.getdbt.com/docs/configuring-models
Expand Down

0 comments on commit 89cae6e

Please sign in to comment.