Skip to content

Commit

Permalink
[http/openapi] Use enum-driven visibility analysis APIs (#5416)
Browse files Browse the repository at this point in the history
This PR brings the core enum-driven visibility system to parity with the
existing visibility concepts and uses its new analysis methods in the
HTTP and OpenAPI libraries.

- Added lifecycle visibility modifiers for `Delete` and `Query` phases.
`Delete` applies when a resource is passed as an argument to a DELETE
operation. `Query` applies when a resource is passed as an argument to a
GET or HEAD operation.
- Added lifecycle transform templates for Delete and Query.
- Made the logic of applying a visibility filter a little bit more
robust. It will consider the `any` constraint of the filter to be
satisfied if it is missing OR if some modifier in the constraint is
present. If the `any` constraint is present, but empty, it will be
unsatisfiable.
- Deprecated `getParameterVisibility` and `getReturnTypeVisibility` in
favor of `getParameterVisibilityFilter` and
`getReturnTypeVisibilityFilter` which return VisibilityFilter objects
instead of `string[]`. Like the modifier analysis methods, the filter
versions of these methods are infallible.
- `getParameterVisibilityFilter` and `getReturnTypeVisibilityFilter`
accept a `VisibilityProvider` object that provides default visibility
filters for parameters and return types if the parameter/returnType
visibilities are not set explicitly. TypeSpec core exports an
`EmptyProvider` that always return filters with no constraints, suitable
as a default. HTTP exports `HttpVisibilityProvider`, which must be used
when working in HTTP, as it provides the HTTP-specific default
visibility logic that determines visibility by verb.
- `HttpVisibilityProvider` can be instantiated with either an `HttpVerb`
directly, in which case that exact verb will be used;
`HttpOperationOptions`, in which case the verbSelector will be used if
present, otherwise the fallback logic will be used; or no argument in
which case the fallback logic will always be used. This gives it parity
with `getHttpOperation`.
- HTTP now marshals conversions between core visibility and HTTP
`Visibility` through visibility filters instead of visibility string
arrays. The HTTP Visibility concept is unchanged.
- Used new forms of visibility analysis APIs in openapi to detect
read-only properties.
- ~~Fixed a bug in http-server-csharp that was surfaced by these changes
where a visibility constraint `Visibility.Create & Visibility.Update`
was provided, resulting in Visibility.None and properties being made
invisible, affecting effective type calculation for anonymous models.~~
(invalidated by merge)
- Fixed a bug in which `hasVisibility` and `getVisibilityForClass` were
improperly initializing the visibility state. This didn't affect the new
APIs, but it would cause the state of the legacy APIs to be corrupted if
visibility is queried from the new APIs before the legacy APIs.

Closes #5119
Closes #5120

---------

Co-authored-by: Will Temple <[email protected]>
Co-authored-by: Mark Cowlishaw <[email protected]>
  • Loading branch information
3 people authored Jan 22, 2025
1 parent f323eb8 commit 546daec
Show file tree
Hide file tree
Showing 21 changed files with 1,207 additions and 223 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
changeKind: internal
packages:
- "@typespec/http"
- "@typespec/openapi"
---

Updated the OpenAPI3 and HTTP libraries to use the new visibility analysis APIs.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@typespec/compiler"
---

Added APIs for getting parameterVisibility and returnTypeVisibility as VisibilityFilter objects.
98 changes: 66 additions & 32 deletions packages/compiler/generated-defs/TypeSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -800,31 +800,47 @@ export type InspectTypeNameDecorator = (
) => void;

/**
* Indicates that a property is only considered to be present or applicable ("visible") with
* the in the given named contexts ("visibilities"). When a property has no visibilities applied
* to it, it is implicitly visible always.
* Sets the visibility modifiers that are active on a property, indicating that it is only considered to be present
* (or "visible") in contexts that select for the given modifiers.
*
* As far as the TypeSpec core library is concerned, visibilities are open-ended and can be arbitrary
* strings, but the following visibilities are well-known to standard libraries and should be used
* with standard emitters that interpret them as follows:
* A property without any visibility settings applied for any visibility class (e.g. `Lifecycle`) is considered to have
* the default visibility settings for that class.
*
* - "read": output of any operation.
* - "create": input to operations that create an entity..
* - "query": input to operations that read data.
* - "update": input to operations that update data.
* - "delete": input to operations that delete data.
* If visibility for the property has already been set for a visibility class (for example, using `@invisible` or
* `@removeVisibility`), this decorator will **add** the specified visibility modifiers to the property.
*
* See: [Visibility](https://typespec.io/docs/language-basics/visibility)
*
* The `@typespec/http` library uses `Lifecycle` visibility to determine which properties are included in the request or
* response bodies of HTTP operations. By default, it uses the following visibility settings:
*
* - For the return type of operations, properties are included if they have `Lifecycle.Read` visibility.
* - For POST operation parameters, properties are included if they have `Lifecycle.Create` visibility.
* - For PUT operation parameters, properties are included if they have `Lifecycle.Create` or `Lifecycle.Update` visibility.
* - For PATCH operation parameters, properties are included if they have `Lifecycle.Update` visibility.
* - For DELETE operation parameters, properties are included if they have `Lifecycle.Delete` visibility.
* - For GET or HEAD operation parameters, properties are included if they have `Lifecycle.Query` visibility.
*
* By default, properties have all five Lifecycle visibility modifiers enabled, so a property is visible in all contexts
* by default.
*
* The default settings may be overridden using the `@returnTypeVisibility` and `@parameterVisibility` decorators.
*
* See also: [Automatic visibility](https://typespec.io/docs/libraries/http/operations#automatic-visibility)
*
* @param visibilities List of visibilities which apply to this property.
* @example
* ```typespec
* model Dog {
* // the service will generate an ID, so you don't need to send it.
* @visibility(Lifecycle.Read) id: int32;
* // the service will store this secret name, but won't ever return it
* @visibility(Lifecycle.Create, Lifecycle.Update) secretName: string;
* // the regular name is always present
* // The service will generate an ID, so you don't need to send it.
* @visibility(Lifecycle.Read)
* id: int32;
*
* // The service will store this secret name, but won't ever return it.
* @visibility(Lifecycle.Create, Lifecycle.Update)
* secretName: string;
*
* // The regular name has all vi
* name: string;
* }
* ```
Expand All @@ -839,7 +855,8 @@ export type VisibilityDecorator = (
* Indicates that a property is not visible in the given visibility class.
*
* This decorator removes all active visibility modifiers from the property within
* the given visibility class.
* the given visibility class, making it invisible to any context that selects for
* visibility modifiers within that class.
*
* @param visibilityClass The visibility class to make the property invisible within.
* @example
Expand Down Expand Up @@ -868,8 +885,8 @@ export type InvisibleDecorator = (
* @example
* ```typespec
* model Example {
* // This property will have the Create and Update visibilities, but not the
* // Read visibility, since it is removed.
* // This property will have all Lifecycle visibilities except the Read
* // visibility, since it is removed.
* @removeVisibility(Lifecycle.Read)
* secret_property: string;
* }
Expand All @@ -882,22 +899,26 @@ export type RemoveVisibilityDecorator = (
) => void;

/**
* Removes properties that are not considered to be present or applicable
* ("visible") in the given named contexts ("visibilities"). Can be used
* together with spread to effectively spread only visible properties into
* a new model.
* Removes properties that do not have at least one of the given visibility modifiers
* active.
*
* If no visibility modifiers are supplied, this decorator has no effect.
*
* See also: [Automatic visibility](https://typespec.io/docs/libraries/http/operations#automatic-visibility)
*
* When using an emitter that applies visibility automatically, it is generally
* not necessary to use this decorator.
*
* @param visibilities List of visibilities which apply to this property.
* @param visibilities List of visibilities that apply to this property.
* @example
* ```typespec
* model Dog {
* @visibility("read") id: int32;
* @visibility("create", "update") secretName: string;
* @visibility(Lifecycle.Read)
* id: int32;
*
* @visibility(Lifecycle.Create, Lifecycle.Update)
* secretName: string;
*
* name: string;
* }
*
Expand All @@ -907,14 +928,14 @@ export type RemoveVisibilityDecorator = (
* //
* // In this case, the id property is removed, and the name and secretName
* // properties are kept.
* @withVisibility("create", "update")
* @withVisibility(Lifecycle.Create, Lifecycle.Update)
* model DogCreateOrUpdate {
* ...Dog;
* }
*
* // In this case the id and name properties are kept and the secretName property
* // is removed.
* @withVisibility("read")
* @withVisibility(Lifecycle.Read)
* model DogRead {
* ...Dog;
* }
Expand All @@ -927,9 +948,19 @@ export type WithVisibilityDecorator = (
) => void;

/**
* Sets which visibilities apply to parameters for the given operation.
* Declares the visibility constraint of the parameters of a given operation.
*
* A parameter or property nested within a parameter will be visible if it has _any_ of the visibilities
* in the list.
*
* @param visibilities List of visibility strings which apply to this operation.
* WARNING: If no arguments are provided to this decorator, the `@typespec/http` library considers only properties
* that do not have visibility modifiers _explicitly_ configured to be visible. Additionally, the HTTP library will
* disable the feature of `@patch` operations that causes the properties of the request body to become effectively
* optional. Some specifications have used this configuration in the past to describe exact PATCH bodies, but using this
* decorator with no arguments in that manner is not recommended. The legacy behavior of `@parameterVisibility` with no
* arguments is preserved for backwards compatibility pending a future review and possible deprecation.
*
* @param visibilities List of visibility modifiers that apply to the parameters of this operation.
*/
export type ParameterVisibilityDecorator = (
context: DecoratorContext,
Expand All @@ -938,9 +969,12 @@ export type ParameterVisibilityDecorator = (
) => void;

/**
* Sets which visibilities apply to the return type for the given operation.
* Declares the visibility constraint of the return type of a given operation.
*
* A property within the return type of the operation will be visible if it has _any_ of the visibilities
* in the list, or if the list is empty (in which case the property is always visible).
*
* @param visibilities List of visibility strings which apply to this operation.
* @param visibilities List of visibility modifiers that apply to the return type of this operation.
*/
export type ReturnTypeVisibilityDecorator = (
context: DecoratorContext,
Expand Down
Loading

0 comments on commit 546daec

Please sign in to comment.