-
Notifications
You must be signed in to change notification settings - Fork 139
@assert #2216
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
base: main
Are you sure you want to change the base?
@assert #2216
Conversation
agoerler
left a comment
There was a problem hiding this 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"
guides/providing-services.md
Outdated
|
|
||
| ### `@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. |
There was a problem hiding this comment.
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. | ||
|
|
There was a problem hiding this comment.
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>
What exactly do you mean with 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 |
patricebender
left a comment
There was a problem hiding this 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.
You can use the ternary operator as syntactic sugar for the clumsy > 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
|
That makes sense. When I tested it, compiler still required the default |
Co-authored-by: Patrice Bender <patrice.bender@sap.com>
I have improved the examples to use Bookshop related entities. Regarding the ternary operator, we still have to put the |
guides/providing-services.md
Outdated
| parent : Association to Orders; | ||
| book : Association to Books; | ||
| @assert: (case | ||
| when book.stock <= amount then 'Stock exceeded' |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| 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')
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
|
I would like to suggest that you introduce a few sub-sections for
|
| ```cds | ||
| entity OrderItems : cuid { | ||
| @assert: ((book.stock <= quantity) ? 'Stock exceeded' : null) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
guides/providing-services.md
Outdated
| 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; | ||
| } | ||
| ``` |
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
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>
guides/providing-services.md
Outdated
| ### `@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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| 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. |
Co-authored-by: Daniel Schlachter <schlachter.daniel@gmail.com>
Co-authored-by: Adrian Görler <adrian.goerler@sap.com>
| ``` | ||
|
|
||
| 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: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a single
when..thenblock
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| 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. |
Describes the basic feature set of the new
@assertannotation. 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.