forked from microsoft/typespec
-
Notifications
You must be signed in to change notification settings - Fork 1
Expressing data criticality in TypeSpec #19
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
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| # Expressing data criticality in TypeSpec | ||
|
|
||
| This proposal suggests a new decorator, `@critical`, that is used to indicate that the failure to produce a value for a property or operation will result in some larger failure of the operation. | ||
|
|
||
| ## Goals | ||
|
|
||
| Be able to communicate to consumers of TypeSpec that a property or operation should be treated with different error handling semantics than the default. | ||
|
|
||
| ## Definition | ||
|
|
||
| ````typespec | ||
| /** | ||
| * Specify that failure to produce a value for this property or operation will result in | ||
| * some larger failure of the operation. | ||
| * | ||
| * @param error Optional error model that should be produced if the property or operation fails. | ||
| * | ||
| * @example | ||
| * | ||
| * ```typespec | ||
| * model User { | ||
| * @critical email: string; | ||
| * } | ||
| * @critical op getUserEmail(user: User): string; | ||
| * ``` | ||
| */ | ||
| extern dec critical(target: ModelProperty | Operation, error?: Model); | ||
| ```` | ||
|
|
||
| The decorator can be applied either to model properties or to operations. | ||
| Depending on the emitted language, a critical operation may refer to a data fetch or write that, if it fails, should halt further execution. | ||
| A critical property may refer to a property that, if it is not fulfilled, should prevent the entirety of the model from being resolved. | ||
|
|
||
| The optional `error` parameter specifies the model that should be produced if the property or operation fails. The model must be decorated with the [`@error` decorator][error-decorator]. | ||
|
|
||
| ## vs. requiredness | ||
|
|
||
| TypeSpec allows for specifying that a field is ["required" or "optional"][optional-properties] by the presence or absence of the `?` symbol. | ||
|
|
||
| In addition, there is a [`null` literal][null-literal] that be combined using a union to indicate that the value of a property can be `null`. | ||
|
|
||
| `@critical` does not specify anything about a property's requiredness, nor whether it may be nullable. A property that can have the value `null` can still be a critical property, and an optional property may still be critical. | ||
|
|
||
| <br> | ||
|
|
||
| # Use cases | ||
|
|
||
| ## Client libraries | ||
|
|
||
| When generating client code with TypeSpec, the `@critical` decorator offers client libraries a means to apply language-specific concepts to the critical property. | ||
|
|
||
| In a Java client, for example, this could be done by applying a [`@NonNull` annotation][java-nonnull] to the property. This provides a different semantic from [`Objects.requireNonNull`]([java-requirenonnull]) in that it can be enforced by the compiler rather than a runtime exception. | ||
|
|
||
| ## Server libraries | ||
|
|
||
| When generating server code with TypeSpec, the `@critical` decorator offers a way to generate server code that properly fulfills the TypeSpec-defined contract by throwing an error when a critical property is not fulfilled. | ||
|
|
||
| In an HTTP server, for example, this could be done by throwing an HTTP error with a 5xx status code. | ||
|
|
||
| ## GraphQL | ||
|
|
||
| In GraphQL, [all fields are nullable by default][graphql-non-null]. | ||
| When there is an error in [resolving a field][graphql-resolving-field], a GraphQL implementation [will return a `null` value for that field][graphql-handling-field-errors]. | ||
|
|
||
| A field can also be [marked as Non-Null][graphql-non-null]. When there is an error in resolving such a field, a GraphQL implementation [will propagate the error][graphql-handling-field-errors] to the parent field. | ||
|
|
||
| ## Examples | ||
|
|
||
| ### Avoiding dangerous behavior | ||
|
|
||
| Consider an application that serves content that should not be viewable by certain users. The application needs to: | ||
|
|
||
| - check if the user is allowed to view the content | ||
| - prompt the user for verification if their status is unknown | ||
| - avoid showing the content to the user unless they are known to be verified | ||
|
|
||
| We might create a property on a `User` model like this: | ||
|
|
||
| ```typespec | ||
| model User { | ||
| @key id: string; | ||
| email: string; | ||
| fullName?: string; | ||
| @critical canViewRestrictedContent: boolean | null; | ||
| } | ||
| ``` | ||
|
|
||
| Without `@critical`, a server implementation might by default return `null` for the `canViewRestrictedContent` property if an error occurs. This would trigger unwanted behavior, as the client would likely respond by prompting the user for verification even though we were unable to determine if the user needs verification. | ||
|
|
||
| With `@critical`, the server can instead raise an error if it is unable to determine if the user can view restricted content. This would allow the client to handle the error in a way that does not prompt the user for verification, but property suggests that an error occurred and they should try again later. | ||
|
|
||
| [graphql-non-null]: https://spec.graphql.org/October2021/#sec-Non-Null | ||
| [graphql-resolving-field]: https://spec.graphql.org/October2021/#sec-Value-Completion | ||
| [graphpql-handling-field-errors]: https://spec.graphql.org/October2021/#sec-Handling-Field-Errors | ||
| [optional-properties]: https://typespec.io/docs/language-basics/models/#optional-properties | ||
| [null-literal]: https://typespec.io/docs/language-basics/values/#null-values | ||
| [error-decorator]: https://typespec.io/docs/standard-library/built-in-decorators/#@error | ||
| [java-nonnull]: https://checkerframework.org/jsr308/specification/java-annotation-design.html#type-qualifiers | ||
| [java-requirenonnull]: https://docs.oracle.com/javase/8/docs/api/java/util/Objects.html#requireNonNull-T- | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
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.
small typo here
graphpqlbreaks links above :)