Skip to content

Commit 045b38c

Browse files
authored
Simplify Database Configuration (#136)
Make `app_db_name` optional and remove it from the template. If it's not specified, use the app name (suitably transformed) as `app_db_name`.
1 parent 4ae4a5b commit 045b38c

File tree

5 files changed

+115
-24
lines changed

5 files changed

+115
-24
lines changed

dbos/cli.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
from dbos import load_config
1919
from dbos.application_database import ApplicationDatabase
20+
from dbos.dbos_config import is_valid_app_name
2021
from dbos.system_database import SystemDatabase
2122

2223
app = typer.Typer()
@@ -125,11 +126,9 @@ def copy_template(src_dir: str, project_name: str, config_mode: bool) -> None:
125126
dst_dir = path.abspath(".")
126127

127128
package_name = project_name.replace("-", "_")
128-
db_name = package_name if not package_name[0].isdigit() else f"_{package_name}"
129129
ctx = {
130130
"project_name": project_name,
131131
"package_name": package_name,
132-
"db_name": db_name,
133132
"migration_command": "alembic upgrade head",
134133
}
135134

@@ -167,14 +166,6 @@ def get_project_name() -> typing.Union[str, None]:
167166
return name
168167

169168

170-
def is_valid_app_name(name: str) -> bool:
171-
name_len = len(name)
172-
if name_len < 3 or name_len > 30:
173-
return False
174-
match = re.match("^[a-z0-9-_]+$", name)
175-
return True if match != None else False
176-
177-
178169
@app.command()
179170
def init(
180171
project_name: Annotated[

dbos/dbos-config.schema.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,7 @@
8686
"hostname",
8787
"port",
8888
"username",
89-
"password",
90-
"app_db_name"
89+
"password"
9190
]
9291
},
9392
"telemetry": {

dbos/dbos_config.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,34 @@ def load_config(config_file_path: str = "dbos-config.yaml") -> ConfigFile:
167167

168168
data = cast(ConfigFile, data)
169169

170+
if not is_valid_app_name(data["name"]):
171+
raise DBOSInitializationError(
172+
f'Invalid app name {data["name"]}. App names must be between 3 and 30 characters and contain only alphanumeric characters, dashes, and underscores.'
173+
)
174+
175+
if "app_db_name" not in data["database"]:
176+
data["database"]["app_db_name"] = app_name_to_db_name(data["name"])
177+
170178
if "local_suffix" in data["database"] and data["database"]["local_suffix"]:
171179
data["database"]["app_db_name"] = f"{data['database']['app_db_name']}_local"
172180

173181
# Return data as ConfigFile type
174182
return data # type: ignore
175183

176184

185+
def is_valid_app_name(name: str) -> bool:
186+
name_len = len(name)
187+
if name_len < 3 or name_len > 30:
188+
return False
189+
match = re.match("^[a-z0-9-_]+$", name)
190+
return True if match != None else False
191+
192+
193+
def app_name_to_db_name(app_name: str) -> str:
194+
name = app_name.replace("-", "_")
195+
return name if not name[0].isdigit() else f"_{name}"
196+
197+
177198
def set_env_vars(config: ConfigFile) -> None:
178199
for env, value in config.get("env", {}).items():
179200
if value is not None:

dbos/templates/hello/dbos-config.yaml.dbos

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ database:
1313
port: 5432
1414
username: postgres
1515
password: ${PGPASSWORD}
16-
app_db_name: ${db_name}
1716
migrate:
1817
- ${migration_command}
1918
telemetry:

tests/test_config.py

Lines changed: 92 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def conditional_mock_open(*args, **kwargs):
2727

2828
def test_valid_config(mocker):
2929
mock_config = """
30-
name: "some app"
30+
name: "some-app"
3131
language: "python"
3232
runtimeConfig:
3333
start:
@@ -51,7 +51,7 @@ def test_valid_config(mocker):
5151
)
5252

5353
configFile = load_config(mock_filename)
54-
assert configFile["name"] == "some app"
54+
assert configFile["name"] == "some-app"
5555
assert configFile["language"] == "python"
5656
assert configFile["database"]["hostname"] == "some host"
5757
assert configFile["database"]["port"] == 1234
@@ -68,11 +68,34 @@ def test_valid_config(mocker):
6868
assert "bob" not in os.environ
6969

7070

71-
def test_config_missing_params(mocker):
71+
def test_valid_config_without_appdbname(mocker):
7272
mock_config = """
73-
name: "some app"
73+
name: "some-app"
74+
language: "python"
75+
runtimeConfig:
76+
start:
77+
- "python3 main.py"
78+
admin_port: 8001
7479
database:
7580
hostname: 'some host'
81+
port: 1234
82+
username: 'some user'
83+
password: ${PGPASSWORD}
84+
connectionTimeoutMillis: 3000
85+
"""
86+
os.environ["BARBAR"] = "FOOFOO"
87+
mocker.patch(
88+
"builtins.open", side_effect=generate_mock_open(mock_filename, mock_config)
89+
)
90+
91+
configFile = load_config(mock_filename)
92+
assert configFile["database"]["app_db_name"] == "some_app"
93+
94+
95+
def test_config_missing_params(mocker):
96+
mock_config = """
97+
name: "some-app"
98+
database:
7699
port: 1234
77100
username: 'some user'
78101
password: abc123
@@ -85,12 +108,12 @@ def test_config_missing_params(mocker):
85108
with pytest.raises(DBOSInitializationError) as exc_info:
86109
load_config(mock_filename)
87110

88-
assert "'app_db_name' is a required property" in str(exc_info.value)
111+
assert "'hostname' is a required property" in str(exc_info.value)
89112

90113

91114
def test_config_extra_params(mocker):
92115
mock_config = """
93-
name: "some app"
116+
name: "some-app"
94117
database:
95118
hostname: 'some host'
96119
port: 1234
@@ -136,7 +159,7 @@ def test_config_missing_name(mocker):
136159

137160
def test_config_missing_language(mocker):
138161
mock_config = """
139-
name: "some app"
162+
name: "some-app"
140163
database:
141164
hostname: 'some host'
142165
port: 1234
@@ -157,7 +180,7 @@ def test_config_missing_language(mocker):
157180

158181
def test_config_bad_language(mocker):
159182
mock_config = """
160-
name: "some app"
183+
name: "some-app"
161184
language: typescript
162185
database:
163186
hostname: 'some host'
@@ -177,10 +200,35 @@ def test_config_bad_language(mocker):
177200
assert "invalid language" in str(exc_info.value)
178201

179202

180-
def test_config_no_start(mocker):
203+
def test_config_bad_name(mocker):
181204
mock_config = """
182205
name: "some app"
183206
language: python
207+
runtimeConfig:
208+
start:
209+
- "python3 main.py"
210+
database:
211+
hostname: 'some host'
212+
port: 1234
213+
username: 'some user'
214+
password: abc123
215+
app_db_name: 'some db'
216+
connectionTimeoutMillis: 3000
217+
"""
218+
mocker.patch(
219+
"builtins.open", side_effect=generate_mock_open(mock_filename, mock_config)
220+
)
221+
222+
with pytest.raises(DBOSInitializationError) as exc_info:
223+
load_config(mock_filename)
224+
225+
assert "Invalid app name" in str(exc_info.value)
226+
227+
228+
def test_config_no_start(mocker):
229+
mock_config = """
230+
name: "some-app"
231+
language: python
184232
database:
185233
hostname: 'some host'
186234
port: 1234
@@ -201,7 +249,7 @@ def test_config_no_start(mocker):
201249

202250
def test_local_config(mocker):
203251
mock_config = """
204-
name: "some app"
252+
name: "some-app"
205253
language: "python"
206254
runtimeConfig:
207255
start:
@@ -222,7 +270,7 @@ def test_local_config(mocker):
222270
)
223271

224272
configFile = load_config(mock_filename)
225-
assert configFile["name"] == "some app"
273+
assert configFile["name"] == "some-app"
226274
assert configFile["database"]["local_suffix"] == True
227275
assert configFile["language"] == "python"
228276
assert configFile["database"]["hostname"] == "some host"
@@ -231,3 +279,36 @@ def test_local_config(mocker):
231279
assert configFile["database"]["password"] == os.environ["PGPASSWORD"]
232280
assert configFile["database"]["app_db_name"] == "some_db_local"
233281
assert configFile["database"]["connectionTimeoutMillis"] == 3000
282+
283+
284+
def test_local_config_without_name(mocker):
285+
mock_config = """
286+
name: "some-app"
287+
language: "python"
288+
runtimeConfig:
289+
start:
290+
- "python3 main.py"
291+
admin_port: 8001
292+
database:
293+
hostname: 'some host'
294+
port: 1234
295+
username: 'some user'
296+
password: ${PGPASSWORD}
297+
connectionTimeoutMillis: 3000
298+
local_suffix: true
299+
"""
300+
os.environ["BARBAR"] = "FOOFOO"
301+
mocker.patch(
302+
"builtins.open", side_effect=generate_mock_open(mock_filename, mock_config)
303+
)
304+
305+
configFile = load_config(mock_filename)
306+
assert configFile["name"] == "some-app"
307+
assert configFile["database"]["local_suffix"] == True
308+
assert configFile["language"] == "python"
309+
assert configFile["database"]["hostname"] == "some host"
310+
assert configFile["database"]["port"] == 1234
311+
assert configFile["database"]["username"] == "some user"
312+
assert configFile["database"]["password"] == os.environ["PGPASSWORD"]
313+
assert configFile["database"]["app_db_name"] == "some_app_local"
314+
assert configFile["database"]["connectionTimeoutMillis"] == 3000

0 commit comments

Comments
 (0)