Skip to content
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

[compiler] Enum-driven visibility #4825

Merged
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
c1667c2
WIP
willmtemple Sep 22, 2024
8f6beed
Merge remote-tracking branch 'upstream/main' into witemple-msft/visib…
willmtemple Sep 26, 2024
7ac53e5
WIP
willmtemple Oct 10, 2024
fc849f7
Nearly code complete
willmtemple Oct 22, 2024
3115040
Merge remote-tracking branch 'upstream/main' into witemple-msft/visib…
willmtemple Oct 22, 2024
b6f893c
Fix issue with resource create operations
willmtemple Oct 28, 2024
c883c9f
Merge remote-tracking branch 'upstream/main' into witemple-msft/visib…
willmtemple Oct 28, 2024
c68c2c9
Fix circular reference issue
timotheeguerin Oct 28, 2024
b06b4da
Implement visibility transform decorators
willmtemple Oct 29, 2024
649e5e4
Merge remote-tracking branch 'origin/witemple-msft/visibility-enum' i…
willmtemple Oct 29, 2024
50743ca
Lint/format
willmtemple Oct 30, 2024
62dfafb
Clean API surface
willmtemple Oct 30, 2024
35d8400
Test new core visibility functionality.
willmtemple Oct 30, 2024
1145808
Format
willmtemple Oct 30, 2024
f6930b1
Test legacy visibility string coercion in core APIs.
willmtemple Oct 30, 2024
b26a77e
Fix an issue with recursive transforms
willmtemple Oct 30, 2024
5b1705c
Fix issue with module instancing for tests, allowing running mutators…
willmtemple Oct 31, 2024
37a2941
Actually fix visibility transform mutators and consolidate implementa…
willmtemple Nov 1, 2024
8f3cce5
Separate mutators into model/property
willmtemple Nov 1, 2024
2f18683
Finally, realy fix the mutator
willmtemple Nov 1, 2024
bc9b464
Lint/format
willmtemple Nov 1, 2024
660e1a3
Merge remote-tracking branch 'upstream/main' into witemple-msft/visib…
willmtemple Nov 1, 2024
f013701
Revert change to openapi visibility computation
willmtemple Nov 4, 2024
e0c677e
Merge remote-tracking branch 'upstream/main' into witemple-msft/visib…
willmtemple Nov 4, 2024
7f88db0
satisfy vitest obsession with circular module imports
willmtemple Nov 4, 2024
d1b4c70
Enhance mutator to walk more indirect types.
willmtemple Nov 6, 2024
d77ace7
Add visibility reference documentation
willmtemple Nov 6, 2024
3872973
Merge remote-tracking branch 'upstream/main' into witemple-msft/visib…
willmtemple Nov 6, 2024
bc5320a
Update generated-defs
willmtemple Nov 6, 2024
acc25c0
Chronus
willmtemple Nov 6, 2024
e7d92bc
Chronus 2
willmtemple Nov 6, 2024
4ce30a5
fix chronus
willmtemple Nov 6, 2024
436627b
Merge remote-tracking branch 'upstream/main' into witemple-msft/visib…
willmtemple Nov 6, 2024
3cf29bc
JSDoc improvement
willmtemple Nov 6, 2024
a584fcb
Improve legacy backcompat for empty @visibility decorator
willmtemple Nov 7, 2024
6f00136
Merge remote-tracking branch 'upstream/main' into witemple-msft/visib…
willmtemple Nov 7, 2024
e85bda6
Allow recasting legacy visibility through explicitly calling $visibil…
willmtemple Nov 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/compiler/.scripts/gen-extern-signature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ const program = await compile(NodeHost, root, {});

const files = await generateExternDecorators(program, "@typespec/compiler");
for (const [name, content] of Object.entries(files)) {
const updatedContent = content.replace(/from "\@typespec\/compiler"/g, `from "../src/index.js"`);
const updatedContent = content.replace(
/from "\@typespec\/compiler"/g,
name === "TypeSpec.ts-test.ts" ? `from "../src/index.js"` : `from "../src/core/index.js"`,
);
const prettierConfig = await resolveConfig(root);

await NodeHost.writeFile(
Expand Down
191 changes: 164 additions & 27 deletions packages/compiler/generated-defs/TypeSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type {
Type,
Union,
UnionVariant,
} from "../src/index.js";
} from "../src/core/index.js";

export interface ExampleOptions {
readonly title?: string;
Expand All @@ -24,6 +24,12 @@ export interface OperationExample {
readonly returnType?: unknown;
}

export interface VisibilityFilter {
readonly any?: readonly EnumValue[];
readonly all?: readonly EnumValue[];
readonly none?: readonly EnumValue[];
}

/**
* Specify how to encode the target type.
*
Expand Down Expand Up @@ -114,12 +120,22 @@ export type WithoutDefaultValuesDecorator = (context: DecoratorContext, target:
/**
* Set the visibility of key properties in a model if not already set.
*
* @param visibility The desired default visibility value. If a key property already has a `visibility` decorator then the default visibility is not applied.
* This will set the visibility modifiers of all key properties in the model if the visibility is not already _explicitly_ set,
* but will not change the visibility of any properties that have visibility set _explicitly_, even if the visibility
* is the same as the default visibility.
*
* Visibility may be explicitly set using any of the following decorators:
*
* - `@visibility`
* - `@removeVisibility`
* - `@invisible`
*
* @param visibility The desired default visibility value. If a key property already has visibility set, it will not be changed.
*/
export type WithDefaultKeyVisibilityDecorator = (
context: DecoratorContext,
target: Model,
visibility: string,
visibility: string | EnumValue,
) => void;

/**
Expand Down Expand Up @@ -629,6 +645,24 @@ export type OpExampleDecorator = (
options?: ExampleOptions,
) => void;

/**
* A debugging decorator used to inspect a type.
*
* @param text Custom text to log
*/
export type InspectTypeDecorator = (context: DecoratorContext, target: Type, text: string) => void;

/**
* A debugging decorator used to inspect a type name.
*
* @param text Custom text to log
*/
export type InspectTypeNameDecorator = (
context: DecoratorContext,
target: Type,
text: string,
) => 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
Expand All @@ -651,9 +685,9 @@ export type OpExampleDecorator = (
* ```typespec
* model Dog {
* // the service will generate an ID, so you don't need to send it.
* @visibility("read") id: int32;
* @visibility(Lifecycle.Read) id: int32;
* // the service will store this secret name, but won't ever return it
* @visibility("create", "update") secretName: string;
* @visibility(Lifecycle.Create, Lifecycle.Update) secretName: string;
* // the regular name is always present
* name: string;
* }
Expand All @@ -662,7 +696,53 @@ export type OpExampleDecorator = (
export type VisibilityDecorator = (
context: DecoratorContext,
target: ModelProperty,
...visibilities: string[]
...visibilities: (string | EnumValue)[]
) => void;

/**
* 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.
*
* @param visibilityClass The visibility class to make the property invisible within.
* @example
* ```typespec
* model Example {
* @invisible(Lifecycle)
* hidden_property: string;
* }
* ```
*/
export type InvisibleDecorator = (
context: DecoratorContext,
target: ModelProperty,
visibilityClass: Enum,
) => void;

/**
* Removes visibility modifiers from a property.
*
* If the visibility modifiers for a visibility class have not been initialized,
* this decorator will use the default visibility modifiers for the visibility
* class as the default modifier set.
*
* @param target The property to remove visibility from.
* @param visibilities The visibility modifiers to remove from the target property.
* @example
* ```typespec
* model Example {
* // This property will have the Create and Update visibilities, but not the
* // Read visibility, since it is removed.
* @removeVisibility(Lifecycle.Read)
* secret_property: string;
* }
* ```
*/
export type RemoveVisibilityDecorator = (
context: DecoratorContext,
target: ModelProperty,
...visibilities: EnumValue[]
) => void;

/**
Expand Down Expand Up @@ -707,49 +787,101 @@ export type VisibilityDecorator = (
export type WithVisibilityDecorator = (
context: DecoratorContext,
target: Model,
...visibilities: string[]
...visibilities: (string | EnumValue)[]
) => void;

/**
* A debugging decorator used to inspect a type.
* Sets which visibilities apply to parameters for the given operation.
*
* @param text Custom text to log
* @param visibilities List of visibility strings which apply to this operation.
*/
export type InspectTypeDecorator = (context: DecoratorContext, target: Type, text: string) => void;
export type ParameterVisibilityDecorator = (
context: DecoratorContext,
target: Operation,
...visibilities: (string | EnumValue)[]
) => void;

/**
* A debugging decorator used to inspect a type name.
* Sets which visibilities apply to the return type for the given operation.
*
* @param text Custom text to log
* @param visibilities List of visibility strings which apply to this operation.
*/
export type InspectTypeNameDecorator = (
export type ReturnTypeVisibilityDecorator = (
context: DecoratorContext,
target: Type,
text: string,
target: Operation,
...visibilities: (string | EnumValue)[]
) => void;

/**
* Sets which visibilities apply to parameters for the given operation.
* Declares the default visibility modifiers for a visibility class.
*
* @param visibilities List of visibility strings which apply to this operation.
* The default modifiers are used when a property does not have any visibility decorators
* applied to it.
*/
export type ParameterVisibilityDecorator = (
export type DefaultVisibilityDecorator = (
context: DecoratorContext,
target: Operation,
...visibilities: string[]
target: Enum,
...visibilities: EnumValue[]
) => void;

/**
* Sets which visibilities apply to the return type for the given operation.
* Applies the given visibility filter to the properties of the target model.
*
* @param visibilities List of visibility strings which apply to this operation.
* This transformation is recursive, so it will also apply the filter to any nested
* or referenced models that are the types of any properties in the `target`.
*
* @param target The model to apply the visibility filter to.
* @param filter The visibility filter to apply to the properties of the target model.
* @example
* ```typespec
* model Dog {
* @visibility(Lifecycle.Read)
* id: int32;
*
* name: string;
* }
*
* @withVisibilityFilter(#{ all: #[Lifecycle.Read] })
* model DogRead {
* ...Dog
* }
* ```
*/
export type ReturnTypeVisibilityDecorator = (
export type WithVisibilityFilterDecorator = (
context: DecoratorContext,
target: Operation,
...visibilities: string[]
target: Model,
filter: VisibilityFilter,
) => void;

/**
* Transforms the `target` model to include only properties that are visible during the
* "Update" lifecycle phase.
*
* Any nested models of optional properties will be transformed into the "CreateOrUpdate"
* lifecycle phase instead of the "Update" lifecycle phase, so that nested models may be
* fully updated.
*
* @param target The model to apply the transformation to.
* @example
* ```typespec
* model Dog {
* @visibility(Lifecycle.Read)
* id: int32;
*
* @visibility(Lifecycle.Create, Lifecycle.Update)
* secretName: string;
*
* name: string;
* }
*
* @withLifecycleUpdate
* model DogUpdate {
* ...Dog
* }
* ```
*/
export type WithLifecycleUpdateDecorator = (context: DecoratorContext, target: Model) => void;

export type TypeSpecDecorators = {
encode: EncodeDecorator;
doc: DocDecorator;
Expand Down Expand Up @@ -787,10 +919,15 @@ export type TypeSpecDecorators = {
discriminator: DiscriminatorDecorator;
example: ExampleDecorator;
opExample: OpExampleDecorator;
visibility: VisibilityDecorator;
withVisibility: WithVisibilityDecorator;
inspectType: InspectTypeDecorator;
inspectTypeName: InspectTypeNameDecorator;
visibility: VisibilityDecorator;
invisible: InvisibleDecorator;
removeVisibility: RemoveVisibilityDecorator;
withVisibility: WithVisibilityDecorator;
parameterVisibility: ParameterVisibilityDecorator;
returnTypeVisibility: ReturnTypeVisibilityDecorator;
defaultVisibility: DefaultVisibilityDecorator;
withVisibilityFilter: WithVisibilityFilterDecorator;
withLifecycleUpdate: WithLifecycleUpdateDecorator;
};
99 changes: 0 additions & 99 deletions packages/compiler/lib/std/decorators.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -579,93 +579,6 @@ extern dec opExample(
example: valueof OperationExample,
options?: valueof ExampleOptions
);
/**
* 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.
*
* 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:
*
* - "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.
*
* 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("read") id: int32;
* // the service will store this secret name, but won't ever return it
* @visibility("create", "update") secretName: string;
* // the regular name is always present
* name: string;
* }
* ```
*/
extern dec visibility(target: ModelProperty, ...visibilities: valueof string[]);

/**
* 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.
*
* 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.
*
* @example
* ```typespec
* model Dog {
* @visibility("read") id: int32;
* @visibility("create", "update") secretName: string;
* name: string;
* }
*
* // The spread operator will copy all the properties of Dog into DogRead,
* // and @withVisibility will then remove those that are not visible with
* // create or update visibility.
* //
* // In this case, the id property is removed, and the name and secretName
* // properties are kept.
* @withVisibility("create", "update")
* model DogCreateOrUpdate {
* ...Dog;
* }
*
* // In this case the id and name properties are kept and the secretName property
* // is removed.
* @withVisibility("read")
* model DogRead {
* ...Dog;
* }
* ```
*/
extern dec withVisibility(target: Model, ...visibilities: valueof string[]);

/**
* Set the visibility of key properties in a model if not already set.
*
* @param visibility The desired default visibility value. If a key property already has a `visibility` decorator then the default visibility is not applied.
*/
extern dec withDefaultKeyVisibility(target: Model, visibility: valueof string);

/**
* Returns the model with non-updateable properties removed.
*/
extern dec withUpdateableProperties(target: Model);

/**
* Returns the model with required properties removed.
Expand Down Expand Up @@ -704,15 +617,3 @@ extern dec inspectType(target: unknown, text: valueof string);
* @param text Custom text to log
*/
extern dec inspectTypeName(target: unknown, text: valueof string);

/**
* Sets which visibilities apply to parameters for the given operation.
* @param visibilities List of visibility strings which apply to this operation.
*/
extern dec parameterVisibility(target: Operation, ...visibilities: valueof string[]);

/**
* Sets which visibilities apply to the return type for the given operation.
* @param visibilities List of visibility strings which apply to this operation.
*/
extern dec returnTypeVisibility(target: Operation, ...visibilities: valueof string[]);
Loading
Loading