Skip to content

Commit a057def

Browse files
committed
fix: allow missing config datasource URL
1 parent ab56fe7 commit a057def

8 files changed

Lines changed: 47 additions & 45 deletions

File tree

schema-engine/cli/src/commands.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,28 @@ impl Cli {
3838
) -> Result<String, ConnectorError> {
3939
let mut api = schema_core::schema_api(None, datasource_urls.clone(), None, extensions)?;
4040

41+
let url = datasource_urls
42+
.url
43+
.ok_or_else(|| ConnectorError::from_msg("No URL defined in the configured datasource".to_owned()))?;
44+
4145
let work = async {
4246
match self.command {
4347
CliCommand::CreateDatabase => api
4448
.create_database(schema_core::json_rpc::types::CreateDatabaseParams {
45-
datasource: DatasourceParam::ConnectionString(UrlContainer {
46-
url: datasource_urls.url.clone(),
47-
}),
49+
datasource: DatasourceParam::ConnectionString(UrlContainer { url }),
4850
})
4951
.await
5052
.map(|schema_core::json_rpc::types::CreateDatabaseResult { database_name }| {
5153
format!("Database '{database_name}' was successfully created.")
5254
}),
5355
CliCommand::CanConnectToDatabase => api
5456
.ensure_connection_validity(schema_core::json_rpc::types::EnsureConnectionValidityParams {
55-
datasource: DatasourceParam::ConnectionString(UrlContainer {
56-
url: datasource_urls.url.clone(),
57-
}),
57+
datasource: DatasourceParam::ConnectionString(UrlContainer { url }),
5858
})
5959
.await
6060
.map(|_| "Connection successful".to_owned()),
6161
CliCommand::DropDatabase => api
62-
.drop_database(datasource_urls.url.clone())
62+
.drop_database(url)
6363
.await
6464
.map(|_| "The database was successfully dropped.".to_owned()),
6565
}

schema-engine/core/src/commands/diff_cli.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,10 @@ async fn json_rpc_diff_target_to_dialect(
137137
extension_types: &dyn ExtensionTypes,
138138
) -> CoreResult<Option<(Box<dyn SchemaDialect>, DatabaseSchema)>> {
139139
let datasource_urls = if let Some(shadow_database_url) = shadow_database_url {
140-
&DatasourceUrls::from_url_and_shadow_database_url(&datasource_urls.url, shadow_database_url)
140+
&DatasourceUrls {
141+
url: datasource_urls.url.clone(),
142+
shadow_database_url: Some(shadow_database_url.to_owned()),
143+
}
141144
} else {
142145
datasource_urls
143146
};
@@ -170,7 +173,7 @@ async fn json_rpc_diff_target_to_dialect(
170173
connector.schema_dialect(),
171174
),
172175
Err(_) => {
173-
let dialect = crate::schema_to_dialect(&sources, datasource_urls)?;
176+
let dialect = crate::schema_to_dialect(&sources)?;
174177
(dialect.default_namespace().map(|ns| ns.to_string()), dialect)
175178
}
176179
};

schema-engine/core/src/lib.rs

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub use extensions::{ExtensionType, ExtensionTypeConfig};
2727
use json_rpc::types::{SchemaContainer, SchemasContainer, SchemasWithConfigDir};
2828
pub use schema_connector;
2929

30+
use ::commands::dialect_for_provider;
3031
use enumflags2::BitFlags;
3132
use mongodb_schema_connector::MongoDbSchemaConnector;
3233
use psl::{
@@ -89,32 +90,18 @@ fn connector_for_connection_string(
8990
}
9091

9192
/// Same as schema_to_connector, but it will only read the provider, not the connector params.
92-
/// This uses `schema_files` to read `preview_features` and the `datasource` block.
93-
fn schema_to_dialect(
94-
schema_files: &[(String, SourceFile)],
95-
datasource_urls: &DatasourceUrls,
96-
) -> CoreResult<Box<dyn schema_connector::SchemaDialect>> {
93+
/// This uses `schema_files` to read the `datasource` block.
94+
fn schema_to_dialect(schema_files: &[(String, SourceFile)]) -> CoreResult<Box<dyn schema_connector::SchemaDialect>> {
9795
let (_, config) = psl::parse_configuration_multi_file(schema_files)
9896
.map_err(|(files, err)| CoreError::new_schema_parser_error(files.render_diagnostics(&err)))?;
9997

100-
let preview_features = config.preview_features();
10198
let datasource = config
10299
.datasources
103100
.into_iter()
104101
.next()
105102
.ok_or_else(|| CoreError::from_msg("There is no datasource in the schema.".into()))?;
106103

107-
let datasource_urls = datasource_urls.validate(datasource.active_connector)?;
108-
109-
let connector_params = ConnectorParams {
110-
connection_string: datasource_urls.url().to_owned(),
111-
preview_features,
112-
shadow_database_connection_string: datasource_urls.shadow_database_url().map(<_>::to_owned),
113-
};
114-
115-
let conn = connector_for_provider(datasource.active_provider, connector_params)?;
116-
117-
Ok(conn.schema_dialect())
104+
dialect_for_provider(datasource.active_provider)
118105
}
119106

120107
/// Go from a schema to a connector.
@@ -128,14 +115,20 @@ fn schema_to_connector(
128115

129116
let (connection_string, shadow_database_connection_string) = if let Some(config_dir) = config_dir {
130117
let urls = datasource_urls.with_config_dir(datasource.active_connector.flavour(), config_dir);
131-
(urls.url().to_owned(), urls.shadow_database_url().map(<_>::to_owned))
118+
(
119+
urls.url().map(<_>::to_owned),
120+
urls.shadow_database_url().map(<_>::to_owned),
121+
)
132122
} else {
133123
(
134-
datasource_urls.url().to_owned(),
124+
datasource_urls.url().map(<_>::to_owned),
135125
datasource_urls.shadow_database_url().map(<_>::to_owned),
136126
)
137127
};
138128

129+
let connection_string = connection_string
130+
.ok_or_else(|| CoreError::from_msg("No URL defined in the configured datasource".to_owned()))?;
131+
139132
let params = ConnectorParams {
140133
connection_string,
141134
preview_features,
@@ -153,7 +146,10 @@ fn initial_datamodel_to_connector(
153146
let (datasource, preview_features) = extract_configuration_ref(configuration)?;
154147

155148
let params = ConnectorParams {
156-
connection_string: datasource_urls.url().to_owned(),
149+
connection_string: datasource_urls
150+
.url()
151+
.ok_or_else(|| CoreError::from_msg("No URL defined in the configured datasource".to_owned()))?
152+
.to_owned(),
157153
preview_features,
158154
shadow_database_connection_string: datasource_urls.shadow_database_url().map(<_>::to_owned),
159155
};

schema-engine/core/src/state.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ impl EngineState {
218218
Ok(self
219219
.validate_datasource_urls(&datasource)?
220220
.url_with_config_dir(datasource.active_connector.flavour(), Path::new(&container.config_dir))
221+
.ok_or_else(|| CoreError::from_msg("No URL defined in the configured datasource".to_owned()))?
221222
.into_owned())
222223
}
223224

schema-engine/core/src/url.rs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ use crate::core_error::CoreError;
1212
#[serde(rename_all = "camelCase")]
1313
pub struct DatasourceUrls {
1414
/// Direct URL to the database.
15-
pub url: String,
15+
pub url: Option<String>,
1616
/// The URL to a live shadow database, if Prisma should use it instead of creating one.
1717
pub shadow_database_url: Option<String>,
1818
}
1919

2020
/// Datasource URLs that have passed validation and are safe to consume by the schema engine.
2121
#[derive(Debug, Clone)]
2222
pub struct ValidatedDatasourceUrls {
23-
url: String,
23+
url: Option<String>,
2424
shadow_database_url: Option<String>,
2525
}
2626

@@ -43,15 +43,15 @@ impl DatasourceUrls {
4343
/// Creates a `DatasourceUrls` instance containing only a primary URL.
4444
pub fn from_url(url: impl Into<String>) -> Self {
4545
Self {
46-
url: url.into(),
46+
url: Some(url.into()),
4747
shadow_database_url: None,
4848
}
4949
}
5050

5151
/// Creates a `DatasourceUrls` instance with both primary and shadow database URLs.
5252
pub fn from_url_and_shadow_database_url(url: impl Into<String>, shadow_database_url: impl Into<String>) -> Self {
5353
Self {
54-
url: url.into(),
54+
url: Some(url.into()),
5555
shadow_database_url: Some(shadow_database_url.into()),
5656
}
5757
}
@@ -61,7 +61,9 @@ impl DatasourceUrls {
6161
&self,
6262
connector: &dyn psl::datamodel_connector::Connector,
6363
) -> Result<ValidatedDatasourceUrls, DatasourceError> {
64-
validate_datasource_url(&self.url, connector, "url")?;
64+
if let Some(url) = &self.url {
65+
validate_datasource_url(url, connector, "url")?;
66+
}
6567

6668
if let Some(shadow_database_url) = &self.shadow_database_url {
6769
validate_datasource_url(shadow_database_url, connector, "shadowDatabaseUrl")?;
@@ -115,8 +117,8 @@ impl From<DatasourceError> for CoreError {
115117

116118
impl ValidatedDatasourceUrls {
117119
/// Returns the validated primary datasource URL.
118-
pub fn url(&self) -> &str {
119-
&self.url
120+
pub fn url(&self) -> Option<&str> {
121+
self.url.as_deref()
120122
}
121123

122124
/// Returns the validated shadow database URL, if any.
@@ -125,14 +127,14 @@ impl ValidatedDatasourceUrls {
125127
}
126128

127129
/// Resolves relative paths in the URL against the provided configuration directory.
128-
pub fn url_with_config_dir(&self, flavour: Flavour, config_dir: &Path) -> Cow<'_, str> {
129-
set_config_dir(flavour, config_dir, &self.url)
130+
pub fn url_with_config_dir(&self, flavour: Flavour, config_dir: &Path) -> Option<Cow<'_, str>> {
131+
self.url.as_deref().map(|url| set_config_dir(flavour, config_dir, url))
130132
}
131133

132134
/// Returns both URLs with relative file paths rewritten relative to the configuration directory.
133135
pub fn with_config_dir(&self, flavour: Flavour, config_dir: &Path) -> DatasourceUrlsWithConfigDir<'_> {
134136
DatasourceUrlsWithConfigDir {
135-
url: set_config_dir(flavour, config_dir, &self.url),
137+
url: self.url_with_config_dir(flavour, config_dir),
136138
shadow_database_url: self
137139
.shadow_database_url
138140
.as_deref()
@@ -144,14 +146,14 @@ impl ValidatedDatasourceUrls {
144146
/// Datasource URLs with relative paths resolved against the configuration directory.
145147
#[derive(Debug, Clone)]
146148
pub struct DatasourceUrlsWithConfigDir<'a> {
147-
url: Cow<'a, str>,
149+
url: Option<Cow<'a, str>>,
148150
shadow_database_url: Option<Cow<'a, str>>,
149151
}
150152

151153
impl DatasourceUrlsWithConfigDir<'_> {
152154
/// Returns the primary datasource URL, with resolved paths if needed.
153-
pub fn url(&self) -> &str {
154-
&self.url
155+
pub fn url(&self) -> Option<&str> {
156+
self.url.as_deref()
155157
}
156158

157159
/// Returns the shadow database URL, with resolved paths if present.

schema-engine/sql-migration-tests/src/test_api.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ impl TestApi {
206206
pub fn diff(&self, params: DiffParams) -> ConnectorResult<DiffResult> {
207207
self.diff_with_datasource(
208208
&DatasourceUrls {
209-
url: self.connection_string().to_owned(),
209+
url: Some(self.connection_string().to_owned()),
210210
shadow_database_url: self.shadow_database_connection_string().map(<_>::to_owned),
211211
},
212212
params,

schema-engine/sql-migration-tests/tests/migrations/diff.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ fn from_empty_to_migrations_folder_without_shadow_db_url_must_error(mut api: Tes
424424
let err = api
425425
.diff_with_datasource(
426426
&DatasourceUrls {
427-
url: api.connection_string().to_owned(),
427+
url: Some(api.connection_string().to_owned()),
428428
shadow_database_url: None,
429429
},
430430
params,

schema-engine/sql-migration-tests/tests/migrations/jsonrpc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ impl TestApi {
1717
api: RpcApi::new(
1818
None,
1919
DatasourceUrls {
20-
url: args.database_url().to_owned(),
20+
url: Some(args.database_url().to_owned()),
2121
shadow_database_url: args.shadow_database_url().map(ToOwned::to_owned),
2222
},
2323
host,

0 commit comments

Comments
 (0)