dbos 0.28.0a12__tar.gz → 0.28.0a14__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. {dbos-0.28.0a12 → dbos-0.28.0a14}/PKG-INFO +1 -1
  2. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_conductor/conductor.py +32 -1
  3. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_conductor/protocol.py +19 -2
  4. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_dbos_config.py +8 -11
  5. {dbos-0.28.0a12 → dbos-0.28.0a14}/pyproject.toml +1 -1
  6. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_config.py +61 -29
  7. {dbos-0.28.0a12 → dbos-0.28.0a14}/LICENSE +0 -0
  8. {dbos-0.28.0a12 → dbos-0.28.0a14}/README.md +0 -0
  9. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/__init__.py +0 -0
  10. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/__main__.py +0 -0
  11. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_admin_server.py +0 -0
  12. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_app_db.py +0 -0
  13. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_classproperty.py +0 -0
  14. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_client.py +0 -0
  15. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_context.py +0 -0
  16. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_core.py +0 -0
  17. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_croniter.py +0 -0
  18. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_dbos.py +0 -0
  19. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_debug.py +0 -0
  20. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_docker_pg_helper.py +0 -0
  21. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_error.py +0 -0
  22. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_event_loop.py +0 -0
  23. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_fastapi.py +0 -0
  24. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_flask.py +0 -0
  25. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_kafka.py +0 -0
  26. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_kafka_message.py +0 -0
  27. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_logger.py +0 -0
  28. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_migrations/env.py +0 -0
  29. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_migrations/script.py.mako +0 -0
  30. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_migrations/versions/04ca4f231047_workflow_queues_executor_id.py +0 -0
  31. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_migrations/versions/27ac6900c6ad_add_queue_dedup.py +0 -0
  32. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_migrations/versions/50f3227f0b4b_fix_job_queue.py +0 -0
  33. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_migrations/versions/5c361fc04708_added_system_tables.py +0 -0
  34. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_migrations/versions/83f3732ae8e7_workflow_timeout.py +0 -0
  35. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_migrations/versions/933e86bdac6a_add_queue_priority.py +0 -0
  36. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_migrations/versions/a3b18ad34abe_added_triggers.py +0 -0
  37. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_migrations/versions/d76646551a6b_job_queue_limiter.py +0 -0
  38. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_migrations/versions/d76646551a6c_workflow_queue.py +0 -0
  39. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_migrations/versions/eab0cc1d9a14_job_queue.py +0 -0
  40. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_migrations/versions/f4b9b32ba814_functionname_childid_op_outputs.py +0 -0
  41. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_outcome.py +0 -0
  42. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_queue.py +0 -0
  43. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_recovery.py +0 -0
  44. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_registrations.py +0 -0
  45. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_roles.py +0 -0
  46. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_scheduler.py +0 -0
  47. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_schemas/__init__.py +0 -0
  48. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_schemas/application_database.py +0 -0
  49. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_schemas/system_database.py +0 -0
  50. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_serialization.py +0 -0
  51. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_sys_db.py +0 -0
  52. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_templates/dbos-db-starter/README.md +0 -0
  53. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_templates/dbos-db-starter/__package/__init__.py +0 -0
  54. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_templates/dbos-db-starter/__package/main.py.dbos +0 -0
  55. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_templates/dbos-db-starter/__package/schema.py +0 -0
  56. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_templates/dbos-db-starter/alembic.ini +0 -0
  57. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_templates/dbos-db-starter/dbos-config.yaml.dbos +0 -0
  58. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_templates/dbos-db-starter/migrations/env.py.dbos +0 -0
  59. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_templates/dbos-db-starter/migrations/script.py.mako +0 -0
  60. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_templates/dbos-db-starter/migrations/versions/2024_07_31_180642_init.py +0 -0
  61. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_templates/dbos-db-starter/start_postgres_docker.py +0 -0
  62. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_tracer.py +0 -0
  63. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_utils.py +0 -0
  64. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/_workflow_commands.py +0 -0
  65. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/cli/_github_init.py +0 -0
  66. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/cli/_template_init.py +0 -0
  67. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/cli/cli.py +0 -0
  68. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/dbos-config.schema.json +0 -0
  69. {dbos-0.28.0a12 → dbos-0.28.0a14}/dbos/py.typed +0 -0
  70. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/__init__.py +0 -0
  71. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/atexit_no_ctor.py +0 -0
  72. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/atexit_no_launch.py +0 -0
  73. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/classdefs.py +0 -0
  74. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/client_collateral.py +0 -0
  75. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/client_worker.py +0 -0
  76. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/conftest.py +0 -0
  77. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/dupname_classdefs1.py +0 -0
  78. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/dupname_classdefsa.py +0 -0
  79. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/more_classdefs.py +0 -0
  80. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/queuedworkflow.py +0 -0
  81. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_admin_server.py +0 -0
  82. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_async.py +0 -0
  83. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_classdecorators.py +0 -0
  84. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_client.py +0 -0
  85. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_concurrency.py +0 -0
  86. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_croniter.py +0 -0
  87. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_dbos.py +0 -0
  88. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_debug.py +0 -0
  89. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_docker_secrets.py +0 -0
  90. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_failures.py +0 -0
  91. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_fastapi.py +0 -0
  92. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_fastapi_roles.py +0 -0
  93. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_flask.py +0 -0
  94. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_kafka.py +0 -0
  95. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_outcome.py +0 -0
  96. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_package.py +0 -0
  97. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_queue.py +0 -0
  98. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_scheduler.py +0 -0
  99. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_schema_migration.py +0 -0
  100. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_singleton.py +0 -0
  101. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_spans.py +0 -0
  102. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_sqlalchemy.py +0 -0
  103. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_workflow_introspection.py +0 -0
  104. {dbos-0.28.0a12 → dbos-0.28.0a14}/tests/test_workflow_management.py +0 -0
  105. {dbos-0.28.0a12 → dbos-0.28.0a14}/version/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dbos
3
- Version: 0.28.0a12
3
+ Version: 0.28.0a14
4
4
  Summary: Ultra-lightweight durable execution in Python
5
5
  Author-Email: "DBOS, Inc." <contact@dbos.dev>
6
6
  License: MIT
@@ -2,6 +2,7 @@ import socket
2
2
  import threading
3
3
  import time
4
4
  import traceback
5
+ import uuid
5
6
  from importlib.metadata import version
6
7
  from typing import TYPE_CHECKING, Optional
7
8
 
@@ -9,6 +10,7 @@ from websockets import ConnectionClosed, ConnectionClosedOK, InvalidStatus
9
10
  from websockets.sync.client import connect
10
11
  from websockets.sync.connection import Connection
11
12
 
13
+ from dbos._context import SetWorkflowID
12
14
  from dbos._utils import GlobalParams
13
15
  from dbos._workflow_commands import (
14
16
  get_workflow,
@@ -168,10 +170,11 @@ class ConductorWebsocket(threading.Thread):
168
170
  )
169
171
  websocket.send(resume_response.to_json())
170
172
  elif msg_type == p.MessageType.RESTART:
173
+ # TODO: deprecate this message type in favor of Fork
171
174
  restart_message = p.RestartRequest.from_json(message)
172
175
  success = True
173
176
  try:
174
- self.dbos.restart_workflow(restart_message.workflow_id)
177
+ self.dbos.fork_workflow(restart_message.workflow_id, 1)
175
178
  except Exception as e:
176
179
  error_message = f"Exception encountered when restarting workflow {restart_message.workflow_id}: {traceback.format_exc()}"
177
180
  self.dbos.logger.error(error_message)
@@ -183,6 +186,34 @@ class ConductorWebsocket(threading.Thread):
183
186
  error_message=error_message,
184
187
  )
185
188
  websocket.send(restart_response.to_json())
189
+ elif msg_type == p.MessageType.FORK_WORKFLOW:
190
+ fork_message = p.ForkWorkflowRequest.from_json(message)
191
+ new_workflow_id = fork_message.body["new_workflow_id"]
192
+ if new_workflow_id is None:
193
+ new_workflow_id = str(uuid.uuid4())
194
+ workflow_id = fork_message.body["workflow_id"]
195
+ start_step = fork_message.body["start_step"]
196
+ app_version = fork_message.body["application_version"]
197
+ try:
198
+ with SetWorkflowID(new_workflow_id):
199
+ new_handle = self.dbos.fork_workflow(
200
+ workflow_id,
201
+ start_step,
202
+ application_version=app_version,
203
+ )
204
+ new_workflow_id = new_handle.workflow_id
205
+ except Exception as e:
206
+ error_message = f"Exception encountered when forking workflow {workflow_id} to new workflow {new_workflow_id} on step {start_step}, app version {app_version}: {traceback.format_exc()}"
207
+ self.dbos.logger.error(error_message)
208
+ new_workflow_id = None
209
+
210
+ fork_response = p.ForkWorkflowResponse(
211
+ type=p.MessageType.FORK_WORKFLOW,
212
+ request_id=base_message.request_id,
213
+ new_workflow_id=new_workflow_id,
214
+ error_message=error_message,
215
+ )
216
+ websocket.send(fork_response.to_json())
186
217
  elif msg_type == p.MessageType.LIST_WORKFLOWS:
187
218
  list_workflows_message = p.ListWorkflowsRequest.from_json(
188
219
  message
@@ -17,6 +17,7 @@ class MessageType(str, Enum):
17
17
  GET_WORKFLOW = "get_workflow"
18
18
  EXIST_PENDING_WORKFLOWS = "exist_pending_workflows"
19
19
  LIST_STEPS = "list_steps"
20
+ FORK_WORKFLOW = "fork_workflow"
20
21
 
21
22
 
22
23
  T = TypeVar("T", bound="BaseMessage")
@@ -133,7 +134,6 @@ class WorkflowsOutput:
133
134
  AuthenticatedRoles: Optional[str]
134
135
  Input: Optional[str]
135
136
  Output: Optional[str]
136
- Request: Optional[str]
137
137
  Error: Optional[str]
138
138
  CreatedAt: Optional[str]
139
139
  UpdatedAt: Optional[str]
@@ -167,7 +167,6 @@ class WorkflowsOutput:
167
167
  AuthenticatedRoles=roles_str,
168
168
  Input=inputs_str,
169
169
  Output=outputs_str,
170
- Request=request_str,
171
170
  Error=error_str,
172
171
  CreatedAt=created_at_str,
173
172
  UpdatedAt=updated_at_str,
@@ -263,3 +262,21 @@ class ListStepsRequest(BaseMessage):
263
262
  class ListStepsResponse(BaseMessage):
264
263
  output: Optional[List[WorkflowSteps]]
265
264
  error_message: Optional[str] = None
265
+
266
+
267
+ class ForkWorkflowBody(TypedDict):
268
+ workflow_id: str
269
+ start_step: int
270
+ application_version: Optional[str]
271
+ new_workflow_id: Optional[str]
272
+
273
+
274
+ @dataclass
275
+ class ForkWorkflowRequest(BaseMessage):
276
+ body: ForkWorkflowBody
277
+
278
+
279
+ @dataclass
280
+ class ForkWorkflowResponse(BaseMessage):
281
+ new_workflow_id: Optional[str]
282
+ error_message: Optional[str] = None
@@ -436,22 +436,19 @@ def overwrite_config(provided_config: ConfigFile) -> ConfigFile:
436
436
  # Name
437
437
  provided_config["name"] = config_from_file["name"]
438
438
 
439
- # Database config. Note we disregard a potential database_url in config_from_file because it is not expected from DBOS Cloud
439
+ # Database config. Expects DBOS_DATABASE_URL to be set
440
440
  if "database" not in provided_config:
441
441
  provided_config["database"] = {}
442
- provided_config["database"]["hostname"] = config_from_file["database"]["hostname"]
443
- provided_config["database"]["port"] = config_from_file["database"]["port"]
444
- provided_config["database"]["username"] = config_from_file["database"]["username"]
445
- provided_config["database"]["password"] = config_from_file["database"]["password"]
446
- provided_config["database"]["app_db_name"] = config_from_file["database"][
447
- "app_db_name"
448
- ]
449
442
  provided_config["database"]["sys_db_name"] = config_from_file["database"][
450
443
  "sys_db_name"
451
444
  ]
452
- provided_config["database"]["ssl"] = config_from_file["database"]["ssl"]
453
- if "ssl_ca" in config_from_file["database"]:
454
- provided_config["database"]["ssl_ca"] = config_from_file["database"]["ssl_ca"]
445
+
446
+ db_url = os.environ.get("DBOS_DATABASE_URL")
447
+ if db_url is None:
448
+ raise DBOSInitializationError(
449
+ "DBOS_DATABASE_URL environment variable is not set. This is required to connect to the database."
450
+ )
451
+ provided_config["database_url"] = db_url
455
452
 
456
453
  # Telemetry config
457
454
  if "telemetry" not in provided_config or provided_config["telemetry"] is None:
@@ -27,7 +27,7 @@ dependencies = [
27
27
  ]
28
28
  requires-python = ">=3.9"
29
29
  readme = "README.md"
30
- version = "0.28.0a12"
30
+ version = "0.28.0a14"
31
31
 
32
32
  [project.license]
33
33
  text = "MIT"
@@ -865,18 +865,14 @@ def test_overwrite_config(mocker):
865
865
  "FOO": "BAR",
866
866
  },
867
867
  }
868
+ exported_db_url = "postgres://dbosadmin:pwd@hostname:1234/appdbname?connect_timeout=10000&sslmode=require&sslrootcert=cert.pem"
869
+ os.environ["DBOS_DATABASE_URL"] = exported_db_url
870
+
868
871
  config = overwrite_config(provided_config)
869
872
 
870
873
  assert config["name"] == "stock-prices"
871
- assert config["database"]["hostname"] == "hostname"
872
- assert config["database"]["port"] == 1234
873
- assert config["database"]["username"] == "dbosadmin"
874
- assert config["database"]["password"] == "pwd"
875
- assert config["database"]["app_db_name"] == "appdbname"
874
+ assert config["database_url"] == exported_db_url
876
875
  assert config["database"]["sys_db_name"] == "sysdbname"
877
- assert config["database"]["ssl"] == True
878
- assert config["database"]["ssl_ca"] == "cert.pem"
879
- assert config["database"]["connectionTimeoutMillis"] == 10000
880
876
  assert config["database"]["app_db_pool_size"] == 10
881
877
  assert "sys_db_pool_size" not in config["database"]
882
878
  assert config["telemetry"]["logs"]["logLevel"] == "DEBUG"
@@ -892,6 +888,8 @@ def test_overwrite_config(mocker):
892
888
  assert "run_admin_server" not in config["runtimeConfig"]
893
889
  assert "env" not in config
894
890
 
891
+ del os.environ["DBOS_DATABASE_URL"]
892
+
895
893
 
896
894
  def test_overwrite_config_minimal(mocker):
897
895
  mock_config = """
@@ -924,17 +922,15 @@ def test_overwrite_config_minimal(mocker):
924
922
  provided_config: ConfigFile = {
925
923
  "name": "test-app",
926
924
  }
925
+
926
+ exported_db_url = "postgres://dbosadmin:pwd@hostname:1234/appdbname?connect_timeout=10000&sslmode=require&sslrootcert=cert.pem"
927
+ os.environ["DBOS_DATABASE_URL"] = exported_db_url
928
+
927
929
  config = overwrite_config(provided_config)
928
930
 
929
931
  assert config["name"] == "stock-prices"
930
- assert config["database"]["hostname"] == "hostname"
931
- assert config["database"]["port"] == 1234
932
- assert config["database"]["username"] == "dbosadmin"
933
- assert config["database"]["password"] == "pwd"
934
- assert config["database"]["app_db_name"] == "appdbname"
932
+ assert config["database_url"] == exported_db_url
935
933
  assert config["database"]["sys_db_name"] == "sysdbname"
936
- assert config["database"]["ssl"] == True
937
- assert "ssl_ca" not in config["database"]
938
934
  assert config["telemetry"]["OTLPExporter"]["tracesEndpoint"] == [
939
935
  "thetracesendpoint"
940
936
  ]
@@ -942,6 +938,8 @@ def test_overwrite_config_minimal(mocker):
942
938
  assert "runtimeConfig" not in config
943
939
  assert "env" not in config
944
940
 
941
+ del os.environ["DBOS_DATABASE_URL"]
942
+
945
943
 
946
944
  def test_overwrite_config_has_telemetry(mocker):
947
945
  mock_config = """
@@ -983,17 +981,15 @@ def test_overwrite_config_has_telemetry(mocker):
983
981
  },
984
982
  "telemetry": {"logs": {"logLevel": "DEBUG"}},
985
983
  }
984
+
985
+ exported_db_url = "postgres://dbosadmin:pwd@hostname:1234/appdbname?connect_timeout=10000&sslmode=require&sslrootcert=cert.pem"
986
+ os.environ["DBOS_DATABASE_URL"] = exported_db_url
987
+
986
988
  config = overwrite_config(provided_config)
987
989
 
988
990
  assert config["name"] == "stock-prices"
989
- assert config["database"]["hostname"] == "hostname"
990
- assert config["database"]["port"] == 1234
991
- assert config["database"]["username"] == "dbosadmin"
992
- assert config["database"]["password"] == "pwd"
993
- assert config["database"]["app_db_name"] == "appdbname"
991
+ assert config["database_url"] == exported_db_url
994
992
  assert config["database"]["sys_db_name"] == "sysdbname"
995
- assert config["database"]["ssl"] == True
996
- assert config["database"]["ssl_ca"] == "cert.pem"
997
993
  assert config["telemetry"]["OTLPExporter"]["tracesEndpoint"] == [
998
994
  "thetracesendpoint"
999
995
  ]
@@ -1002,6 +998,8 @@ def test_overwrite_config_has_telemetry(mocker):
1002
998
  assert "runtimeConfig" not in config
1003
999
  assert "env" not in config
1004
1000
 
1001
+ del os.environ["DBOS_DATABASE_URL"]
1002
+
1005
1003
 
1006
1004
  # Not expected in practice, but exercise the code path
1007
1005
  def test_overwrite_config_no_telemetry_in_file(mocker):
@@ -1034,14 +1032,20 @@ def test_overwrite_config_no_telemetry_in_file(mocker):
1034
1032
  "telemetry": {"logs": {"logLevel": "DEBUG"}},
1035
1033
  }
1036
1034
 
1035
+ exported_db_url = "postgres://dbosadmin:pwd@hostname:1234/appdbname?connect_timeout=10000&sslmode=require&sslrootcert=cert.pem"
1036
+ os.environ["DBOS_DATABASE_URL"] = exported_db_url
1037
+
1037
1038
  config = overwrite_config(provided_config)
1038
1039
  # Test that telemetry from provided_config is preserved
1040
+ assert config["database_url"] == exported_db_url
1039
1041
  assert config["telemetry"]["logs"]["logLevel"] == "DEBUG"
1040
1042
  assert config["telemetry"]["OTLPExporter"] == {
1041
1043
  "tracesEndpoint": [],
1042
1044
  "logsEndpoint": [],
1043
1045
  }
1044
1046
 
1047
+ del os.environ["DBOS_DATABASE_URL"]
1048
+
1045
1049
 
1046
1050
  # Not expected in practice, but exercise the code path
1047
1051
  def test_overwrite_config_no_otlp_in_file(mocker):
@@ -1082,12 +1086,18 @@ def test_overwrite_config_no_otlp_in_file(mocker):
1082
1086
  },
1083
1087
  }
1084
1088
 
1089
+ exported_db_url = "postgres://dbosadmin:pwd@hostname:1234/appdbname?connect_timeout=10000&sslmode=require&sslrootcert=cert.pem"
1090
+ os.environ["DBOS_DATABASE_URL"] = exported_db_url
1091
+
1085
1092
  config = overwrite_config(provided_config)
1093
+ assert config["database_url"] == exported_db_url
1086
1094
  # Test that OTLPExporter from provided_config is preserved
1087
1095
  assert config["telemetry"]["OTLPExporter"]["tracesEndpoint"] == ["original-trace"]
1088
1096
  assert config["telemetry"]["OTLPExporter"]["logsEndpoint"] == ["original-log"]
1089
1097
  assert "logs" not in config["telemetry"]
1090
1098
 
1099
+ del os.environ["DBOS_DATABASE_URL"]
1100
+
1091
1101
 
1092
1102
  # Not expected in practice, but exercise the code path
1093
1103
  def test_overwrite_config_with_provided_database_url(mocker):
@@ -1123,17 +1133,15 @@ def test_overwrite_config_with_provided_database_url(mocker):
1123
1133
  "name": "test-app",
1124
1134
  "database_url": "ignored",
1125
1135
  }
1136
+
1137
+ exported_db_url = "postgres://dbosadmin:pwd@hostname:1234/appdbname?connect_timeout=10000&sslmode=require&sslrootcert=cert.pem"
1138
+ os.environ["DBOS_DATABASE_URL"] = exported_db_url
1139
+
1126
1140
  config = overwrite_config(provided_config)
1127
1141
 
1128
1142
  assert config["name"] == "stock-prices"
1129
- assert config["database"]["hostname"] == "hostname"
1130
- assert config["database"]["port"] == 1234
1131
- assert config["database"]["username"] == "dbosadmin"
1132
- assert config["database"]["password"] == "pwd"
1133
- assert config["database"]["app_db_name"] == "appdbname"
1143
+ assert config["database_url"] == exported_db_url
1134
1144
  assert config["database"]["sys_db_name"] == "sysdbname"
1135
- assert config["database"]["ssl"] == True
1136
- assert config["database"]["ssl_ca"] == "cert.pem"
1137
1145
  assert config["telemetry"]["OTLPExporter"]["tracesEndpoint"] == [
1138
1146
  "thetracesendpoint"
1139
1147
  ]
@@ -1141,6 +1149,30 @@ def test_overwrite_config_with_provided_database_url(mocker):
1141
1149
  assert "runtimeConfig" not in config
1142
1150
  assert "env" not in config
1143
1151
 
1152
+ del os.environ["DBOS_DATABASE_URL"]
1153
+
1154
+
1155
+ def test_overwrite_config_missing_dbos_database_url(mocker):
1156
+ mock_config = """
1157
+ name: "stock-prices"
1158
+ database:
1159
+ sys_db_name: "sysdbname"
1160
+ """
1161
+
1162
+ mocker.patch(
1163
+ "builtins.open", side_effect=generate_mock_open("dbos-config.yaml", mock_config)
1164
+ )
1165
+
1166
+ provided_config: ConfigFile = {
1167
+ "name": "test-app",
1168
+ }
1169
+ with pytest.raises(DBOSInitializationError) as exc_info:
1170
+ overwrite_config(provided_config)
1171
+ assert (
1172
+ "DBOS_DATABASE_URL environment variable is not set. This is required to connect to the database."
1173
+ in str(exc_info.value)
1174
+ )
1175
+
1144
1176
 
1145
1177
  ####################
1146
1178
  # PROVIDED CONFIGS vs CONFIG FILE
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes