Skip to content

Add ValueExpression infrastructure #3036

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed

Add ValueExpression infrastructure #3036

wants to merge 3 commits into from

Conversation

mp911de
Copy link
Member

@mp911de mp911de commented Feb 2, 2024

We now support parsing of value expressions in the context of persistent entities and persistent properties. Value expressions are literals, simple expressions or composite expressions that can refer to SpEL expressions or property placeholders resolved against the Environment:

@Document("my-collection")
@Document("#{tenantService.getCollectionName()}")
@Document("${tenant-config.collection}")
@Document("orders-#{tenantService.getCollectionName()}-${tenant-config.collection}")

Closes #2369

Comment on lines 4 to 5
Value Expressions a composition of {spring-framework-docs}/core/expressions.html[Spring Expression Language (SpEL)] and {spring-framework-docs}/core/beans/environment.html#beans-placeholder-resolution-in-statements[Property Placeholder Resolution].
Value Expressions a combine powerful evaluation of programmatic expressions with the simplicity to resort to property-placeholder resolution to obtain values from the `Environment` such as configuration properties.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those do not read well, starting sentences right after one another with same Value Expressions. 1st one also misses a verb and in the 2nd the a should be removed.

<1> Value Expression using a single SpEL Expression.
<2> Value Expression using a static SpEL Expression evaluating to `2-hello-world`.
<3> Value Expression using a single Property Placeholder.
<4> Composite expression comprised from the literal `orders-` and the Property Placeholder `${tenant-config.suffix}`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be comprised of?

====

Using value expressions introduces a lot of flexibility to your code.
Keep in mind that using value expressions requires evaluation of the expression on each usage.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we stress this a bit more by moving it to a dedicated callout?

assertThat(eval("#{\"foo\"}-")).isEqualTo("foo-");
assertThat(eval("#{\"fo'o\"}-")).isEqualTo("fo'o-");
assertThat(eval("${key.one}-")).isEqualTo("value-");
assertThat(eval("#{#foo}")).isEqualTo("bar");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should also add tests that reference properties that do not exist.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value defaulting would also be good to check ${non-existing.key:defaultValue}

@BeforeEach
void setUp() {

MapPropertySource propertySource = new MapPropertySource("map", Map.of("key.one", "value"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MapPropertySource may contain non String values. What is the expected output when evaluating expressions like "${numeric.key}" having "numeric.key", 1L?

assertThat(eval("#{\"foo\"}-")).isEqualTo("foo-");
assertThat(eval("#{\"fo'o\"}-")).isEqualTo("fo'o-");
assertThat(eval("${key.one}-")).isEqualTo("value-");
assertThat(eval("${key.one}-and-some-#{'foo'}-#{#foo}")).isEqualTo("value-and-some-foo-bar");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it allowed/possible to reference properties from within an expression? like #{#foo - ${numeric.key}}?

* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.spel;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shall we use expression instead of spel for the package name?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I was wondering, too. What about the spel package then? I think we should migrate it into ….expression as well in a compatible way.

}

@Override
public void setEnvironment(Environment environment) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

though it's an override, we could add some doc around it having a since tag.

@@ -137,14 +141,20 @@ public void setApplicationContext(ApplicationContext applicationContext) throws
if (applicationEventPublisher == null) {
this.applicationEventPublisher = applicationContext;
}

this.environment = applicationContext.getEnvironment();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which one wins? this one or setEnvironment?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an ambiguity overall. We could implement BeanFactoryAware and set the environment from setApplicationContext only if it wasn't already set.

Comment on lines 80 to 106
while (startIndex != -1) {

int endIndex = findPlaceholderEndIndex(expression, startIndex);
if (endIndex != -1) {

String part = expression.substring(startIndex, endIndex + 1);

if (part.startsWith(PLACEHOLDER_PREFIX)) {
expressions.add(createPlaceholder(part));
} else {
expressions.add(createExpression(part));
}

placerholderIndex = expression.indexOf(PLACEHOLDER_PREFIX, endIndex);
expressionIndex = expression.indexOf(EXPRESSION_PREFIX, endIndex);

startIndex = getStartIndex(placerholderIndex, expressionIndex);

if (startIndex == -1) {
expressions.add(new LiteralValueExpression(expression.substring(endIndex + 1)));
} else {
expressions.add(new LiteralValueExpression(expression.substring(endIndex + 1, startIndex)));
}
} else {
expressions.add(new LiteralValueExpression(expression.substring(startIndex)));
startIndex = -1;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this part is really hard to grasp - care to leave some comments in there please.
like why do we substring from end to start here expression.substring(endIndex + 1, startIndex))?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. +1 is to skip the closing parenthesis and to not include it in the literal.

@mp911de mp911de force-pushed the issue/2369 branch 3 times, most recently from b4d4d4b to 622087a Compare February 9, 2024 09:14
@christophstrobl
Copy link
Member

LGTM 👍

christophstrobl and others added 2 commits February 12, 2024 09:07
We now support Value Expressions such as `#{1+1}-${spring.application.name:fallback-value}` that are composed of SpEL expressions, literals and Property Placeholders.

See #2369
Original pull request: #3036
Introduce literal, expression and placeholder variants. Add parser for composite expressions.

Closes #2369
Original pull request: #3036
mp911de pushed a commit that referenced this pull request Feb 12, 2024
We now support Value Expressions such as `#{1+1}-${spring.application.name:fallback-value}` that are composed of SpEL expressions, literals and Property Placeholders.

See #2369
Original pull request: #3036
mp911de added a commit that referenced this pull request Feb 12, 2024
Introduce literal, expression and placeholder variants. Add parser for composite expressions.

Closes #2369
Original pull request: #3036
@mp911de mp911de closed this Feb 12, 2024
@mp911de mp911de deleted the issue/2369 branch February 12, 2024 14:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add an ability to resolve property placeholders in BasicPersistentEntity
2 participants