Skip to content

Conversation

@StefanHenke
Copy link
Contributor

Describes the basic feature set of the new @assert annotation. The full blown CXN documentation is pending and will be provided by Patrice later. Once available, we will link it here. That´s why the expressions in the examples are kept simple by intention.

Copy link
Contributor

@agoerler agoerler left a comment

Choose a reason for hiding this comment

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

Please introduce in smaller steps

  • one case
  • multiple cases
  • error function

cosider using ? :

Please add some examples for moderately more complex expressions.

Like, e.g.

  • "if an order has items the order's total amount must be larger then 0"
  • "a book's author's name must not be null"

### `@assert` <Beta/>

Annotate an element with `@assert` to define an expression for more complex validations that need to be fulfilled before data gets persisted. The expectation is that in case of a violated validation, the expression returns the error message to be sent to the client, or `null` if the validation passed. With the help of `case when` syntax, it is possible to run several validations one after another and fail early.
Copy link
Contributor

Choose a reason for hiding this comment

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

The expectation is

could be confused with the expectation of the assert

run several validations one after another

this a quite procedural way of looking at an expression

### `@assert` <Beta/>

Annotate an element with `@assert` to define an expression for more complex validations that need to be fulfilled before data gets persisted. The expectation is that in case of a violated validation, the expression returns the error message to be sent to the client, or `null` if the validation passed. With the help of `case when` syntax, it is possible to run several validations one after another and fail early.

Copy link
Contributor

Choose a reason for hiding this comment

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

please give an example without error function before we make things more complicated

Co-authored-by: Adrian Görler <adrian.goerler@sap.com>
@StefanHenke
Copy link
Contributor Author

Please introduce in smaller steps

  • one case
  • multiple cases
  • error function

cosider using ? :

Please add some examples for moderately more complex expressions.

Like, e.g.

  • "if an order has items the order's total amount must be larger then 0"
  • "a book's author's name must not be null"

What exactly do you mean with consider using ? :`?

We agreed with Johannes and Patrice yesterday that we will keep the expressions simple here and wait for the CXL docs. But I can check for a "moderately more complex expression".

@rjayasinghe
Copy link
Contributor

Please introduce in smaller steps

  • one case
  • multiple cases
  • error function

cosider using ? :
Please add some examples for moderately more complex expressions.
Like, e.g.

  • "if an order has items the order's total amount must be larger then 0"
  • "a book's author's name must not be null"

What exactly do you mean with consider using ? :`?

We agreed with Johannes and Patrice yesterday that we will keep the expressions simple here and wait for the CXL docs. But I can check for a "moderately more complex expression".

I think @agoerler meant that we split up this single block into 3 examples. After each example we explain the nature of the used expression.

@StefanHenke
Copy link
Contributor Author

Please introduce in smaller steps

  • one case
  • multiple cases
  • error function

cosider using ? :
Please add some examples for moderately more complex expressions.
Like, e.g.

  • "if an order has items the order's total amount must be larger then 0"
  • "a book's author's name must not be null"

What exactly do you mean with consider using ? :`?
We agreed with Johannes and Patrice yesterday that we will keep the expressions simple here and wait for the CXL docs. But I can check for a "moderately more complex expression".

I think @agoerler meant that we split up this single block into 3 examples. After each example we explain the nature of the used expression.

Yes, I have split up the examples already. But was not sure if ?: is something in addition

Copy link
Contributor

@patricebender patricebender left a comment

Choose a reason for hiding this comment

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

👍 As Adrian stated, it would be nice to have a little more (complex) real life scenarios. I would have probably taken the well known Bookshop for showcasing the different scenarios. But that may be my personal taste.

@patricebender
Copy link
Contributor

What exactly do you mean with consider using ? :`?

You can use the ternary operator as syntactic sugar for the clumsy case … when … then … statements:

> cds.parse.xpr`1 > 2 ? true : false`
[
  'case',         'when',
  { val: 1 },     '>',
  { val: 2 },     'then',
  { val: true },  'else',
  { val: false }, 'end'
]
@agoerler
Copy link
Contributor

agoerler commented Nov 21, 2025

What exactly do you mean with consider using ? :`?

You can use the ternary operator as syntactic sugar for the clumsy case … when … then … statements:

> cds.parse.xpr`1 > 2 ? true : false`
[
  'case',         'when',
  { val: 1 },     '>',
  { val: 2 },     'then',
  { val: true },  'else',
  { val: false }, 'end'
]

@patricebender - could you also omit the :-clause and it would default to else null ? E.g.

size > 100 ? 'size must be below 100'

@StefanHenke
Copy link
Contributor Author

What exactly do you mean with consider using ? :`?

You can use the ternary operator as syntactic sugar for the clumsy case … when … then … statements:

> cds.parse.xpr`1 > 2 ? true : false`
[
  'case',         'when',
  { val: 1 },     '>',
  { val: 2 },     'then',
  { val: true },  'else',
  { val: false }, 'end'
]

@patricebender - could you also omit the :-clause and it would default to else null ? E.g.

size > 100 ? 'size must be below 100'

That makes sense. When I tested it, compiler still required the default else null a few weeks back.

StefanHenke and others added 2 commits November 21, 2025 08:19
Co-authored-by: Patrice Bender <patrice.bender@sap.com>
@StefanHenke
Copy link
Contributor Author

👍 As Adrian stated, it would be nice to have a little more (complex) real life scenarios. I would have probably taken the well known Bookshop for showcasing the different scenarios. But that may be my personal taste.

I have improved the examples to use Bookshop related entities. Regarding the ternary operator, we still have to put the : else as the compiler complains otherwise.

parent : Association to Orders;
book : Association to Books;
@assert: (case
when book.stock <= amount then 'Stock exceeded'
Copy link
Contributor

Choose a reason for hiding this comment

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

'Stock exceeded'

I think we gave the recommendation that error messages should not contain element names and should be rather imperative like "order less books". See https://github.wdf.sap.corp/cds-java/home/issues/1164

But I am not sure wether this recommendation should also apply to @ssert messages.

@beckermarc - what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess as error messages are defined in the domain of the application, they can contain terms from the business domain. I think that is the case with "stock" here, which is a valid term in the business domain of orders and just happens to also be the element name. As the target of this error message is also the amount I don't feel like this creates a duplication on the UI. Maybe one could however detail it out a little to: 'Available stock of' || books.stock || 'exceeded'

entity OrderItems : cuid {
@assert: (case
when book.stock = 0 then 'Stock is zero'
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
when book.stock = 0 then 'Stock is zero'
when amount <= 0 then 'Amount is not positive'

(as the book.stock = 0 would also be caught by 'book.stock <= amount')

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe rather: "Enter a positive number". Although such a condition could be also covered with @assert.range

Copy link
Contributor

Choose a reason for hiding this comment

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

@agoerler Why not the initial expression with book.stock = 0?

@agoerler
Copy link
Contributor

I would like to suggest that you introduce a few sub-sections for

  • condition
  • error messages
  • message targets
@rjayasinghe rjayasinghe self-assigned this Nov 24, 2025
```cds
entity OrderItems : cuid {
@assert: ((book.stock <= quantity) ? 'Stock exceeded' : null)
Copy link
Contributor

Choose a reason for hiding this comment

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

do we need the parentheses here?

Copy link
Contributor

Choose a reason for hiding this comment

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

@patricebender do we need the parentheses here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess we need it to indicate it is a string. Otherwise, how would the compiler be able to identify that "Stock" and "exceeded" belongs together?


::: warning Limitations
- Validations will only be enforced in `CREATE` and `UPDATE` statements that contain all key fields (including deep insert and deep update)
- Only elements with simple types (like, `String`, `Integer`, `Boolean`) can be annotated with `@assert`. Elements typed with structured or arrayed types are not supported
Copy link
Contributor

Choose a reason for hiding this comment

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

  • Does this only mean that I cannot annotate a structured type or does it also mean that I cannot annotate an element inside a structured type?
  • What about associations?
Copy link
Contributor

Choose a reason for hiding this comment

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

I would say that elements inside a structured type should be OK. Structured types itself are not working at the moment.

Regarding associations: following associations in expressions works but is not supported as there are some open questions.

@sjvans sjvans requested a review from BobdenOs November 25, 2025 10:38
Comment on lines 973 to 981
Alternatively, the same condition can be simplified by using the ternary operator:

```cds
entity OrderItems : cuid {
@assert: ((book.stock <= quantity) ? 'Stock exceeded' : null)
quantity : Integer;
}
```
Copy link
Contributor

@sjvans sjvans Nov 26, 2025

Choose a reason for hiding this comment

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

ternary style/ else is not supported by assert collector!

Copy link
Contributor

Choose a reason for hiding this comment

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

but this is parsed as case … when statement - it should look identical in the CSN

Copy link
Contributor

Choose a reason for hiding this comment

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

right, but with an else, which causes issues. @beckermarc can provide details

Copy link
Contributor

Choose a reason for hiding this comment

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

however, the collector isn't described here and also not on by default, so i guess it's fine. we want to start out with "do it on service level" anyways, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think the collector in general has problems with explicit else nulls. Even case ... when ... then ... else null would cause the same error. On the other hand side, the compiler does not allow to write the ternary style without :else. Are we confident to improve the collector that it can handle else null in the future?

rjayasinghe and others added 8 commits November 27, 2025 12:52
Co-authored-by: Marcel Schwarz <marcel.schwarz@sap.com>
Co-authored-by: Adrian Görler <adrian.goerler@sap.com>
Co-authored-by: Adrian Görler <adrian.goerler@sap.com>
Co-authored-by: Marcel Schwarz <marcel.schwarz@sap.com>
Co-authored-by: Patrice Bender <patrice.bender@sap.com>
Co-authored-by: Johannes Vogt <j.vogt@sap.com>
### `@assert` <Beta/>

Annotate an element with `@assert` to define an expression for more complex validations that need to be fulfilled before data gets persisted. If the validation should fail, the expression must return the error message to be sent to the client, or `null` if the validation passed.
Annotate an element with `@assert` to define arbitrary validation expressions that are run after the data has been written to the database. If the validation should fail, the expression must return a String containing the error message to be sent to the client. If at least one such validation fails the transaction is rolled back.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Annotate an element with `@assert` to define arbitrary validation expressions that are run after the data has been written to the database. If the validation should fail, the expression must return a String containing the error message to be sent to the client. If at least one such validation fails the transaction is rolled back.
Annotate an element with `@assert` to define expressions that are validated after the data has been written to the database. If the validation should fail, the expression must return a String containing the error message to be sent to the client. If at least one such validation fails the transaction is rolled back.
```

Alternatively, the same condition can be simplified by using the ternary operator:
Alternatively, an assert with a single `when` .. `then` block can be simplified by using the ternary operator:
Copy link
Contributor

Choose a reason for hiding this comment

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

a single when .. then block

I think multiple blocks work as well:

(year = 2000 ? : 'Year is 2000' : year = 3000 : 'Year is 3000' : null)

Refer to [Expressions as Annotation Values](../cds/cdl.md#expressions-as-annotation-values) for detailed rules on expression syntax.

::: info Error Messages
As mentioned above, the error message returned by the CXL expression inside the annotation can be either a static message or a message key to support i18n. If a message key is used, the message is looked up in the message bundle of the service.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
As mentioned above, the error message returned by the CXL expression inside the annotation can be either a static message or a message key to support i18n. If a message key is used, the message is looked up in the message bundle of the service.
The error message returned by the CXL expression inside the annotation can be either a static message or a message key to support i18n. If a message key is used, the message is looked up in the message bundle of the service.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

10 participants