Skip to content

Commit 22322aa

Browse files
authored
Merge pull request #3282 from spinframework/provider-may-resolve
variables: Refactor Provider::kind into ::may_resolve
2 parents 21268ac + 294ad3a commit 22322aa

File tree

9 files changed

+79
-127
lines changed

9 files changed

+79
-127
lines changed

crates/expressions/src/lib.rs

Lines changed: 21 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ pub use provider::Provider;
1111
use template::Part;
1212
pub use template::Template;
1313

14-
use crate::provider::ProviderVariableKind;
15-
1614
/// A [`ProviderResolver`] that can be shared.
1715
pub type SharedPreparedResolver =
1816
std::sync::Arc<std::sync::OnceLock<std::sync::Arc<PreparedResolver>>>;
@@ -92,50 +90,25 @@ impl ProviderResolver {
9290
Ok(PreparedResolver { variables })
9391
}
9492

95-
/// Ensures that all required variables are resolvable at startup
96-
pub async fn ensure_required_variables_resolvable(&self) -> Result<()> {
97-
// If a dynamic provider is available, skip validation.
98-
if self
99-
.providers
100-
.iter()
101-
.any(|p| p.kind() == ProviderVariableKind::Dynamic)
102-
{
103-
return Ok(());
104-
}
105-
106-
let mut unvalidated_keys = vec![];
107-
for key in self.internal.variables.keys() {
108-
// If default value is provided, skip validation.
109-
if let Some(value) = self.internal.variables.get(key) {
110-
if value.default.is_some() {
111-
continue;
112-
}
113-
}
114-
115-
let mut resolved = false;
116-
117-
for provider in &self.providers {
118-
if provider
119-
.get(&Key(key))
120-
.await
121-
.map_err(Error::Provider)?
122-
.is_some()
123-
{
124-
resolved = true;
125-
break;
126-
}
127-
}
128-
129-
if !resolved {
130-
unvalidated_keys.push(key);
93+
/// Ensures that all required variables are not unresolvable
94+
pub fn ensure_required_variables_resolvable(&self) -> Result<()> {
95+
let mut unresolvable_keys = vec![];
96+
for key in self.internal.required_variables() {
97+
let key = Key::new(key)?;
98+
let resolvable = self
99+
.providers
100+
.iter()
101+
.any(|provider| provider.may_resolve(&key));
102+
if !resolvable {
103+
unresolvable_keys.push(key);
131104
}
132105
}
133106

134-
if unvalidated_keys.is_empty() {
107+
if unresolvable_keys.is_empty() {
135108
Ok(())
136109
} else {
137110
Err(Error::Provider(anyhow::anyhow!(
138-
"no provider resolved required variable(s): {unvalidated_keys:?}",
111+
"no provider resolved required variable(s): {unresolvable_keys:?}",
139112
)))
140113
}
141114
}
@@ -248,6 +221,12 @@ impl Resolver {
248221
})?;
249222
Ok(template)
250223
}
224+
225+
fn required_variables(&self) -> impl Iterator<Item = &str> {
226+
self.variables
227+
.iter()
228+
.filter_map(|(name, variable)| variable.default.is_none().then_some(name.as_str()))
229+
}
251230
}
252231

253232
/// A resolver who has resolved all variables.
@@ -360,7 +339,6 @@ mod tests {
360339
use async_trait::async_trait;
361340

362341
use super::*;
363-
use crate::provider::ProviderVariableKind;
364342

365343
#[derive(Debug)]
366344
struct TestProvider;
@@ -375,8 +353,8 @@ mod tests {
375353
}
376354
}
377355

378-
fn kind(&self) -> ProviderVariableKind {
379-
ProviderVariableKind::Static
356+
fn may_resolve(&self, key: &Key) -> bool {
357+
key.as_ref() == "required"
380358
}
381359
}
382360

crates/expressions/src/provider.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,13 @@ use crate::Key;
99
pub trait Provider: Debug + Send + Sync {
1010
/// Returns the value at the given config path, if it exists.
1111
async fn get(&self, key: &Key) -> anyhow::Result<Option<String>>;
12-
fn kind(&self) -> ProviderVariableKind;
13-
}
1412

15-
/// The dynamism of a Provider.
16-
#[derive(Clone, Debug, Default, PartialEq)]
17-
pub enum ProviderVariableKind {
18-
/// Variable must be declared on start
19-
Static,
20-
/// Variable can be made available at runtime
21-
#[default]
22-
Dynamic,
13+
/// Returns true if the given key _might_ be resolvable by this Provider.
14+
///
15+
/// Dynamic resolvers will typically return true unconditionally, which is
16+
/// the default implementation.
17+
fn may_resolve(&self, key: &Key) -> bool {
18+
let _ = key;
19+
true
20+
}
2321
}

crates/expressions/tests/validation_test.rs

Lines changed: 38 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::collections::HashMap;
22

33
use async_trait::async_trait;
4-
use spin_expressions::{provider::ProviderVariableKind, Key, Provider, ProviderResolver};
4+
use spin_expressions::{Key, Provider, ProviderResolver};
55
use spin_locked_app::Variable;
66

77
#[derive(Default)]
@@ -43,23 +43,23 @@ impl ResolverTester {
4343
}
4444
}
4545

46-
#[tokio::test(flavor = "multi_thread")]
47-
async fn if_single_static_provider_with_no_key_to_resolve_is_valid() -> anyhow::Result<()> {
46+
#[test]
47+
fn if_single_static_provider_with_no_key_to_resolve_is_valid() -> anyhow::Result<()> {
4848
let resolver = ResolverTester::new()
4949
.with_provider(StaticProvider::with_variable(
5050
"database_host",
5151
Some("localhost"),
5252
))
5353
.make_resolver()?;
5454

55-
resolver.ensure_required_variables_resolvable().await?;
55+
resolver.ensure_required_variables_resolvable()?;
5656

5757
Ok(())
5858
}
5959

60-
#[tokio::test(flavor = "multi_thread")]
61-
async fn if_single_static_provider_has_data_for_variable_key_to_resolve_it_succeeds(
62-
) -> anyhow::Result<()> {
60+
#[test]
61+
fn if_single_static_provider_has_data_for_variable_key_to_resolve_it_succeeds() -> anyhow::Result<()>
62+
{
6363
let resolver = ResolverTester::new()
6464
.with_provider(StaticProvider::with_variable(
6565
"database_host",
@@ -68,13 +68,13 @@ async fn if_single_static_provider_has_data_for_variable_key_to_resolve_it_succe
6868
.with_variable("database_host", None)
6969
.make_resolver()?;
7070

71-
resolver.ensure_required_variables_resolvable().await?;
71+
resolver.ensure_required_variables_resolvable()?;
7272

7373
Ok(())
7474
}
7575

76-
#[tokio::test(flavor = "multi_thread")]
77-
async fn if_there_is_a_single_static_provider_and_it_does_not_contain_a_required_variable_then_validation_fails(
76+
#[test]
77+
fn if_there_is_a_single_static_provider_and_it_does_not_contain_a_required_variable_then_validation_fails(
7878
) -> anyhow::Result<()> {
7979
let resolver = ResolverTester::new()
8080
.with_provider(StaticProvider::with_variable(
@@ -84,29 +84,26 @@ async fn if_there_is_a_single_static_provider_and_it_does_not_contain_a_required
8484
.with_variable("api_key", None)
8585
.make_resolver()?;
8686

87-
assert!(resolver
88-
.ensure_required_variables_resolvable()
89-
.await
90-
.is_err());
87+
assert!(resolver.ensure_required_variables_resolvable().is_err());
9188

9289
Ok(())
9390
}
9491

95-
#[tokio::test(flavor = "multi_thread")]
96-
async fn if_there_is_a_dynamic_provider_then_validation_succeeds_even_without_default_value_in_play(
92+
#[test]
93+
fn if_there_is_a_dynamic_provider_then_validation_succeeds_even_without_default_value_in_play(
9794
) -> anyhow::Result<()> {
9895
let resolver = ResolverTester::new()
9996
.with_provider(DynamicProvider)
10097
.with_variable("api_key", None)
10198
.make_resolver()?;
10299

103-
resolver.ensure_required_variables_resolvable().await?;
100+
resolver.ensure_required_variables_resolvable()?;
104101

105102
Ok(())
106103
}
107104

108-
#[tokio::test(flavor = "multi_thread")]
109-
async fn if_there_is_a_dynamic_provider_and_static_provider_but_the_variable_to_be_resolved_is_not_in_play(
105+
#[test]
106+
fn if_there_is_a_dynamic_provider_and_static_provider_but_the_variable_to_be_resolved_is_not_in_play(
110107
) -> anyhow::Result<()> {
111108
let resolver = ResolverTester::new()
112109
.with_provider(DynamicProvider)
@@ -117,13 +114,13 @@ async fn if_there_is_a_dynamic_provider_and_static_provider_but_the_variable_to_
117114
.with_variable("api_key", None)
118115
.make_resolver()?;
119116

120-
resolver.ensure_required_variables_resolvable().await?;
117+
resolver.ensure_required_variables_resolvable()?;
121118

122119
Ok(())
123120
}
124121

125-
#[tokio::test(flavor = "multi_thread")]
126-
async fn if_there_is_a_dynamic_provider_and_a_static_provider_then_validation_succeeds_even_if_there_is_a_variable_in_play(
122+
#[test]
123+
fn if_there_is_a_dynamic_provider_and_a_static_provider_then_validation_succeeds_even_if_there_is_a_variable_in_play(
127124
) -> anyhow::Result<()> {
128125
let resolver = ResolverTester::new()
129126
.with_provider(DynamicProvider)
@@ -134,13 +131,13 @@ async fn if_there_is_a_dynamic_provider_and_a_static_provider_then_validation_su
134131
.with_variable("api_key", Some("super-secret-key"))
135132
.make_resolver()?;
136133

137-
resolver.ensure_required_variables_resolvable().await?;
134+
resolver.ensure_required_variables_resolvable()?;
138135

139136
Ok(())
140137
}
141138

142-
#[tokio::test(flavor = "multi_thread")]
143-
async fn if_there_are_two_static_providers_where_one_has_data_is_valid() -> anyhow::Result<()> {
139+
#[test]
140+
fn if_there_are_two_static_providers_where_one_has_data_is_valid() -> anyhow::Result<()> {
144141
let resolver = ResolverTester::new()
145142
.with_provider(StaticProvider::with_variable(
146143
"database_host",
@@ -153,14 +150,14 @@ async fn if_there_are_two_static_providers_where_one_has_data_is_valid() -> anyh
153150
.with_variable("database_host", None)
154151
.make_resolver()?;
155152

156-
resolver.ensure_required_variables_resolvable().await?;
153+
resolver.ensure_required_variables_resolvable()?;
157154

158155
Ok(())
159156
}
160157
// Ensure that if there are two or more static providers and the first one does not have data for the variable to be resolved,
161158
// but the second or subsequent one does, then validation still succeeds.
162-
#[tokio::test(flavor = "multi_thread")]
163-
async fn if_there_are_two_static_providers_where_first_provider_does_not_have_data_while_second_provider_does(
159+
#[test]
160+
fn if_there_are_two_static_providers_where_first_provider_does_not_have_data_while_second_provider_does(
164161
) -> anyhow::Result<()> {
165162
let resolver = ResolverTester::new()
166163
.with_provider(StaticProvider::with_variable(
@@ -174,13 +171,13 @@ async fn if_there_are_two_static_providers_where_first_provider_does_not_have_da
174171
.with_variable("api_key", None)
175172
.make_resolver()?;
176173

177-
resolver.ensure_required_variables_resolvable().await?;
174+
resolver.ensure_required_variables_resolvable()?;
178175

179176
Ok(())
180177
}
181178

182-
#[tokio::test(flavor = "multi_thread")]
183-
async fn if_there_is_two_static_providers_neither_having_data_is_invalid() -> anyhow::Result<()> {
179+
#[test]
180+
fn if_there_is_two_static_providers_neither_having_data_is_invalid() -> anyhow::Result<()> {
184181
let resolver = ResolverTester::new()
185182
.with_provider(StaticProvider::with_variable(
186183
"database_host",
@@ -193,37 +190,30 @@ async fn if_there_is_two_static_providers_neither_having_data_is_invalid() -> an
193190
.with_variable("hello", None)
194191
.make_resolver()?;
195192

196-
assert!(resolver
197-
.ensure_required_variables_resolvable()
198-
.await
199-
.is_err());
193+
assert!(resolver.ensure_required_variables_resolvable().is_err());
200194

201195
Ok(())
202196
}
203197

204-
#[tokio::test(flavor = "multi_thread")]
205-
async fn no_provider_data_available_but_variable_default_value_needed_is_invalid(
206-
) -> anyhow::Result<()> {
198+
#[test]
199+
fn no_provider_data_available_but_variable_default_value_needed_is_invalid() -> anyhow::Result<()> {
207200
let resolver = ResolverTester::new()
208201
.with_variable("api_key", None)
209202
.make_resolver()?;
210203

211-
assert!(resolver
212-
.ensure_required_variables_resolvable()
213-
.await
214-
.is_err());
204+
assert!(resolver.ensure_required_variables_resolvable().is_err());
215205

216206
Ok(())
217207
}
218208

219-
#[tokio::test(flavor = "multi_thread")]
220-
async fn no_provider_data_available_but_variable_has_default_value_needed_is_valid(
221-
) -> anyhow::Result<()> {
209+
#[test]
210+
fn no_provider_data_available_but_variable_has_default_value_needed_is_valid() -> anyhow::Result<()>
211+
{
222212
let resolver = ResolverTester::new()
223213
.with_variable("api_key", Some("super-secret-key"))
224214
.make_resolver()?;
225215

226-
resolver.ensure_required_variables_resolvable().await?;
216+
resolver.ensure_required_variables_resolvable()?;
227217

228218
Ok(())
229219
}
@@ -247,8 +237,8 @@ impl Provider for StaticProvider {
247237
Ok(self.variables.get(key.as_str()).cloned().flatten())
248238
}
249239

250-
fn kind(&self) -> ProviderVariableKind {
251-
ProviderVariableKind::Static
240+
fn may_resolve(&self, key: &Key) -> bool {
241+
self.variables.contains_key(key.as_str())
252242
}
253243
}
254244

@@ -260,8 +250,4 @@ impl Provider for DynamicProvider {
260250
async fn get(&self, _key: &Key) -> anyhow::Result<Option<String>> {
261251
panic!("validation should never call get for a dynamic provider")
262252
}
263-
264-
fn kind(&self) -> ProviderVariableKind {
265-
ProviderVariableKind::Dynamic
266-
}
267253
}

crates/factor-variables/tests/factor_test.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use spin_expressions::{provider::ProviderVariableKind, Key, Provider};
1+
use spin_expressions::{Key, Provider};
22
use spin_factor_variables::{runtime_config::RuntimeConfig, VariablesFactor};
33
use spin_factors::{anyhow, RuntimeFactors};
44
use spin_factors_test::{toml, TestEnvironment};
@@ -47,7 +47,7 @@ impl Provider for MockProvider {
4747
}
4848
}
4949

50-
fn kind(&self) -> ProviderVariableKind {
51-
ProviderVariableKind::Static
50+
fn may_resolve(&self, key: &Key) -> bool {
51+
key.as_ref() == "foo"
5252
}
5353
}

crates/trigger/src/cli/variable.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ impl<F: RuntimeFactors, U> ExecutorHooks<F, U> for VariablesValidatorHook {
1414
let variables_factor_app_state = configured_app.app_state::<VariablesFactor>()?;
1515

1616
let expression_resolver = variables_factor_app_state.expression_resolver();
17-
expression_resolver
18-
.ensure_required_variables_resolvable()
19-
.await?;
17+
expression_resolver.ensure_required_variables_resolvable()?;
2018

2119
Ok(())
2220
}

0 commit comments

Comments
 (0)