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

feat(decorator): add @flattenProperty #94

Merged
merged 4 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@azure-tools/typespec-client-generator-core",
"comment": "feat(decorator): add `@flattenProperty`",
"type": "none"
}
],
"packageName": "@azure-tools/typespec-client-generator-core"
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,34 @@ model ModelToExclude {
}
```

### `@flattenProperty` {#@Azure.ClientGenerator.Core.flattenProperty}

Set whether a model property should be flattened or not.

```typespec
haolingdong-msft marked this conversation as resolved.
Show resolved Hide resolved
@Azure.ClientGenerator.Core.flattenProperty(scope?: valueof string)
```

#### Target

`ModelProperty`

#### Parameters

| Name | Type | Description |
| ----- | ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| scope | `valueof scalar string` | The language scope you want this decorator to apply to. If not specified, will apply to all language emitters |

#### Examples

```typespec
model Foo {
@flattenProperty
prop: Bar;
}
model Bar {}
```

### `@include` {#@Azure.ClientGenerator.Core.include}

Whether to include a model in generation for specific languages. By default we generate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ npm install --save-peer @azure-tools/typespec-client-generator-core
- [`@clientFormat`](./decorators.md#@Azure.ClientGenerator.Core.clientFormat)
- [`@convenientAPI`](./decorators.md#@Azure.ClientGenerator.Core.convenientAPI)
- [`@exclude`](./decorators.md#@Azure.ClientGenerator.Core.exclude)
- [`@flattenProperty`](./decorators.md#@Azure.ClientGenerator.Core.flattenProperty)
- [`@include`](./decorators.md#@Azure.ClientGenerator.Core.include)
- [`@internal`](./decorators.md#@Azure.ClientGenerator.Core.internal)
- [`@operationGroup`](./decorators.md#@Azure.ClientGenerator.Core.operationGroup)
Expand Down
29 changes: 29 additions & 0 deletions packages/typespec-client-generator-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ npm install @azure-tools/typespec-client-generator-core
- [`@clientFormat`](#@clientformat)
- [`@convenientAPI`](#@convenientapi)
- [`@exclude`](#@exclude)
- [`@flattenProperty`](#@flattenproperty)
- [`@include`](#@include)
- [`@internal`](#@internal)
- [`@operationGroup`](#@operationgroup)
Expand Down Expand Up @@ -187,6 +188,34 @@ model ModelToExclude {
}
```

#### `@flattenProperty`

Set whether a model property should be flattened or not.

```typespec
@Azure.ClientGenerator.Core.flattenProperty(scope?: valueof string)
```

##### Target

`ModelProperty`

##### Parameters

| Name | Type | Description |
| ----- | ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| scope | `valueof scalar string` | The language scope you want this decorator to apply to. If not specified, will apply to all language emitters |

##### Examples

```typespec
model Foo {
@flattenProperty
prop: Bar;
}
model Bar {}
```

#### `@include`

Whether to include a model in generation for specific languages. By default we generate
Expand Down
17 changes: 17 additions & 0 deletions packages/typespec-client-generator-core/lib/decorators.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,20 @@ enum Access {
* ```
*/
extern dec access(target: Model | Operation | Enum, value: EnumMember, scope?: valueof string);

/**
* Set whether a model property should be flattened or not.
* @param scope The language scope you want this decorator to apply to. If not specified, will apply to all language emitters
*
* @example
* ```typespec
* model Foo {
* @flattenProperty
* prop: Bar;
* }
* model Bar {
* }
* ```
*/
#deprecated "@flattenProperty decorator is not recommended to use."
extern dec flattenProperty(target: ModelProperty, scope?: valueof string);
24 changes: 24 additions & 0 deletions packages/typespec-client-generator-core/src/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -771,3 +771,27 @@ export function getAccess(
? getSdkModel(context, entity).access
: getSdkEnum(context, entity).access;
}

const flattenPropertyKey = createStateSymbol("flattenPropertyKey");
/**
* Whether a model property should be flattened.
*
* @param context DecoratorContext
* @param target ModelProperty to mark as flattened
* @param scope Names of the projection (e.g. "python", "csharp", "java", "javascript")
* @deprecated This decorator is not recommended to use.
*/
export function $flattenProperty(context: DecoratorContext, target: ModelProperty, scope?: string) {
setScopedDecoratorData(context, $flattenProperty, flattenPropertyKey, target, true, scope); // eslint-disable-line deprecation/deprecation
}

/**
* Whether a model property should be flattened or not.
*
* @param context SdkContext
* @param target ModelProperty that we want to check whether it should be flattened or not
* @returns whether the model property should be flattened or not
*/
export function shouldFlattenProperty(context: SdkContext, target: ModelProperty): boolean {
return getScopedDecoratorData(context, flattenPropertyKey, target) ?? false;
}
111 changes: 109 additions & 2 deletions packages/typespec-client-generator-core/test/decorators.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { Enum, Interface, Model, Namespace, Operation, UsageFlags } from "@typespec/compiler";
import {
Enum,
Interface,
Model,
ModelProperty,
Namespace,
Operation,
UsageFlags,
} from "@typespec/compiler";
import { expectDiagnostics } from "@typespec/compiler/testing";
import { deepStrictEqual, ok, strictEqual } from "assert";
import { deepStrictEqual, notStrictEqual, ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import {
getAccess,
Expand All @@ -10,6 +18,7 @@ import {
listClients,
listOperationGroups,
listOperationsInOperationGroup,
shouldFlattenProperty,
shouldGenerateConvenient,
shouldGenerateProtocol,
} from "../src/decorators.js";
Expand Down Expand Up @@ -2197,4 +2206,102 @@ describe("typespec-client-generator-core: decorators", () => {
strictEqual(getUsage(runner.context, Dog), UsageFlags.Output);
});
});

describe("@flattenProperty", () => {
timotheeguerin marked this conversation as resolved.
Show resolved Hide resolved
it("marks a model property to be flattened with suppression of deprecation warning", async () => {
const { Model1 } = (await runner.compile(`
@service({})
@test namespace MyService {
@test
model Model1{
#suppress "deprecated" "@flattenProperty decorator is not recommended to use."
@flattenProperty
child: Model2;
}

@test
model Model2{}

@test
@route("/func1")
op func1(@body body: Model1): void;
}
`)) as { Model1: Model };

const childProperty = Model1.properties.get("child");
notStrictEqual(childProperty, undefined);
strictEqual(shouldFlattenProperty(runner.context, childProperty as ModelProperty), true);
});

it("doesn't mark a un-flattened model property", async () => {
const { Model1 } = (await runner.compile(`
@service({})
@test namespace MyService {
@test
model Model1{
child: Model2;
}

@test
model Model2{}

@test
@route("/func1")
op func1(@body body: Model1): void;
}
`)) as { Model1: Model };

const childProperty = Model1.properties.get("child");
notStrictEqual(childProperty, undefined);
strictEqual(shouldFlattenProperty(runner.context, childProperty as ModelProperty), false);
});

it("throws deprecation warning if not suppressed", async () => {
const diagnostics = await runner.diagnose(`
@service({})
@test namespace MyService {
@test
model Model1{
@flattenProperty
child: Model2;
}

@test
model Model2{}

@test
@route("/func1")
op func1(@body body: Model1): void;
}
`);

expectDiagnostics(diagnostics, {
code: "deprecated",
});
});

it("throws error when used on other targets", async () => {
const diagnostics = await runner.diagnose(`
@service({})
@test namespace MyService {
@test
@flattenProperty
model Model1{
child: Model2;
}

@test
model Model2{}

@test
@route("/func1")
op func1(@body body: Model1): void;
}
`);

expectDiagnostics(diagnostics, {
code: "decorator-wrong-target",
});
});
});
});