Skip to content

Commit

Permalink
Support usage and access (#2356)
Browse files Browse the repository at this point in the history
* support-usage-and-access

* add generate orphan models options

* test push code to pr

* add test case for clientGeneratorCore access and usage

* rename public as operation name for now

* fix integration test

* add modular test case and remove duplicate case

* regenerate all test

* regenerate rlc test

* fix ci

---------

Co-authored-by: Jiao Di (MSFT) <80496810+v-jiaodi@users.noreply.github.com>
  • Loading branch information
qiaozha and v-jiaodi committed Mar 21, 2024
1 parent 9759655 commit 5ff6da3
Show file tree
Hide file tree
Showing 66 changed files with 2,296 additions and 19 deletions.
2 changes: 1 addition & 1 deletion packages/rlc-common/src/helpers/nameUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const ReservedModelNames: ReservedName[] = [
{ name: "package", reservedFor: [NameType.Parameter] },
{ name: "private", reservedFor: [NameType.Parameter] },
{ name: "protected", reservedFor: [NameType.Parameter] },
{ name: "public", reservedFor: [NameType.Parameter] },
{ name: "public", reservedFor: [NameType.Parameter, NameType.Operation] },
{ name: "requestoptions", reservedFor: [NameType.Parameter] },
{ name: "require", reservedFor: [NameType.Parameter] },
{ name: "return", reservedFor: [NameType.Parameter] },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ options:
azureSdkForJs: false
isModularLibrary: true
hierarchyClient: false
generateOrphanModels: true
"emitter-output-dir": "{project-root}/../generated/typespec-ts"
packageDetails:
name: "@msinternal/openai_modular"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ export interface ListWidgetsPagesResults {
results: Widget[];
}

// @public (undocumented)
export interface NonReferencedModel {
prop1: number;
prop2: string;
}

// @public
export interface PagedAsyncIterableIterator<TElement, TPage = TElement[], TPageSettings extends PageSettings = PageSettings> {
[Symbol.asyncIterator](): PagedAsyncIterableIterator<TElement, TPage, TPageSettings>;
Expand All @@ -56,6 +62,12 @@ export interface Widget {
weight: number;
}

// @public (undocumented)
export interface WidgetError {
code: number;
message: string;
}

// @public (undocumented)
export interface WidgetsAnalyzeWidgetOptions extends OperationOptions {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ export {
} from "./WidgetServiceClient.js";
export {
Widget,
WidgetError,
ListWidgetsPagesResults,
CreateWidget,
UpdateWidget,
AnalyzeResult,
NonReferencedModel,
WidgetsListWidgetsOptions,
WidgetsListWidgetsPagesOptions,
WidgetsQueryWidgetsPagesOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@

export {
Widget,
WidgetError,
ListWidgetsPagesResults,
CreateWidget,
UpdateWidget,
AnalyzeResult,
NonReferencedModel,
} from "./models.js";
export {
WidgetsListWidgetsOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ export interface Widget {
color: "red" | "blue";
}

export interface WidgetError {
/** The HTTP error code. */
code: number;
/** A human-readable message describing the error. */
message: string;
}

export interface ListWidgetsPagesResults {
/** The current page of results. */
results: Widget[];
Expand All @@ -34,3 +41,10 @@ export interface UpdateWidget {
export interface AnalyzeResult {
summary: string;
}

export interface NonReferencedModel {
/** The weight of the widget. This is an int32, but must be greater than zero. */
prop1: number;
/** The color of the widget. */
prop2: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ export {
} from "./WidgetServiceClient.js";
export {
Widget,
WidgetError,
ListWidgetsPagesResults,
CreateWidget,
UpdateWidget,
AnalyzeResult,
NonReferencedModel,
WidgetsListWidgetsOptions,
WidgetsListWidgetsPagesOptions,
WidgetsQueryWidgetsPagesOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@

export {
Widget,
WidgetError,
ListWidgetsPagesResults,
CreateWidget,
UpdateWidget,
AnalyzeResult,
NonReferencedModel,
} from "./models.js";
export {
WidgetsListWidgetsOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ export interface Widget {
color: "red" | "blue";
}

export interface WidgetError {
/** The HTTP error code. */
code: number;
/** A human-readable message describing the error. */
message: string;
}

export interface ListWidgetsPagesResults {
/** The current page of results. */
results: Widget[];
Expand All @@ -34,3 +41,10 @@ export interface UpdateWidget {
export interface AnalyzeResult {
summary: string;
}

export interface NonReferencedModel {
/** The weight of the widget. This is an int32, but must be greater than zero. */
prop1: number;
/** The color of the widget. */
prop2: string;
}
8 changes: 8 additions & 0 deletions packages/typespec-test/test/widget_dpg/spec/client.tsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import "@azure-tools/typespec-client-generator-core";
import "./main.tsp";

using Azure.ClientGenerator.Core;

// Some models from routes with suppressed visibility are still desired for custom public surface.
@@access(WidgetService.NonReferencedModel, Access.public);
@@usage(WidgetService.NonReferencedModel, Usage.input | Usage.output);
7 changes: 7 additions & 0 deletions packages/typespec-test/test/widget_dpg/spec/main.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ model CreateWidget {
color: "red" | "blue";
}

model NonReferencedModel {
@doc("The weight of the widget. This is an int32, but must be greater than zero.")
prop1: int32;
@doc("The color of the widget.")
prop2: string;
}

model UpdateWidget {
@doc("The UUID of this widget. This is generated automatically by the service.")
@path
Expand Down
1 change: 1 addition & 0 deletions packages/typespec-test/test/widget_dpg/tspconfig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ options:
multiClient: false
azureSdkForJs: false
isModularLibrary: true
generateOrphanModels: true
"emitter-output-dir": "{project-root}/generated/typespec-ts"
packageDetails:
name: "@msinternal/widget_dpg"
Expand Down
8 changes: 8 additions & 0 deletions packages/typespec-ts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@ addCredentials: true
credentialKeyHeaderName: Your-Subscription-Key
```

### generateOrphanModels

By default, we will not generate orphan models, if we want to generate them, we have to enable this option besides the `@access` and `@usage` configurations in client.tsp.

```yaml
generateOrphanModels: true
```

# Contributing

If you want to contribute on this project read the [contrubuting document](./CONTRIBUTING.md) for more details.
4 changes: 3 additions & 1 deletion packages/typespec-ts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,9 @@ export async function $onEmit(context: EmitContext) {
buildRootIndex(modularCodeModel, subClient, rootIndexFile);
}

removeUnusedInterfaces(project);
if (!emitterOptions.generateOrphanModels) {
removeUnusedInterfaces(project);
}

for (const file of project.getSourceFiles()) {
await emitContentByBuilder(
Expand Down
2 changes: 2 additions & 0 deletions packages/typespec-ts/src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Options } from "prettier";

export interface EmitterOptions extends RLCOptions {
branded?: boolean;
generateOrphanModels?: boolean;
}

export const RLCOptionsSchema: JSONSchemaType<EmitterOptions> = {
Expand Down Expand Up @@ -83,6 +84,7 @@ export const RLCOptionsSchema: JSONSchemaType<EmitterOptions> = {
hierarchyClient: { type: "boolean", nullable: true },
branded: { type: "boolean", nullable: true },
flavor: { type: "string", nullable: true },
generateOrphanModels: { type: "boolean", nullable: true },
moduleKind: {
type: "string",
nullable: true,
Expand Down
8 changes: 8 additions & 0 deletions packages/typespec-ts/test/commands/cadl-ranch-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,14 @@ export const nonBrandedRlcTsps: TypeSpecRanchConfig[] = [
];

export const modularTsps: TypeSpecRanchConfig[] = [
{
outputPath: "azure/clientGeneratorCore/access",
inputPath: "azure/client-generator-core/access"
},
{
outputPath: "azure/clientGeneratorCore/usage",
inputPath: "azure/client-generator-core/usage"
},
{
outputPath: "parameters/body-optionality",
inputPath: "parameters/body-optionality"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,6 @@ describe("Access Client", () => {
}
});

it("should get internal decorator in internal operation", async () => {
try {
const result = await client
.path(
"/azure/client-generator-core/access/internalOperation/internalDecoratorInInternal"
)
.get({ queryParameters: { name: "myname" } });
assert.strictEqual(result.status, "200");
assert.strictEqual(result.body.name, "myname");
} catch (err) {
assert.fail(err as string);
}
});

it("should get public decorator in internal operation", async () => {
try {
const result = await client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
NoDecoratorInInternal200Response,
InternalDecoratorInInternal200Response,
PublicDecoratorInInternal200Response,
Public200Response,
PublicOperation200Response,
Internal200Response,
Operation200Response,
Discriminator200Response,
Expand Down Expand Up @@ -56,7 +56,7 @@ export interface PublicDecoratorInInternal {
}

export interface Public {
get(options: PublicParameters): StreamableMethod<Public200Response>;
get(options: PublicParameters): StreamableMethod<PublicOperation200Response>;
}

export interface Internal {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export interface PublicDecoratorInInternal200Response extends HttpResponse {
}

/** The request has succeeded. */
export interface Public200Response extends HttpResponse {
export interface PublicOperation200Response extends HttpResponse {
status: "200";
body: SharedModelOutput;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { assert } from "chai";
import { AccessClient } from "./generated/azure/clientGeneratorCore/access/src/index.js";
describe("Azure ClientGeneratorCore Access Client", () => {
let client: AccessClient;

beforeEach(() => {
client = new AccessClient({
allowInsecureConnection: true,
retryOptions: {
maxRetries: 0
}
});
});

it("should get no decorator in public operation", async () => {
try {
const result = await client.noDecoratorInPublic("myname");
assert.equal(result.name, "myname");
} catch (err) {
assert.fail(err as string);
}
});

it("should get public decorator in public operation", async () => {
try {
const result = await client.publicDecoratorInPublic("myname");
assert.equal(result.name, "myname");
} catch (err) {
assert.fail(err as string);
}
});

it("should get no decorator in internal operation", async () => {
try {
const result = await client.noDecoratorInInternal("myname");
assert.equal(result.name, "myname");
} catch (err) {
assert.fail(err as string);
}
});

it("should get internal decorator in internal operation", async () => {
try {
const result = await client.internalDecoratorInInternal("myname");
assert.equal(result.name, "myname");
} catch (err) {
assert.fail(err as string);
}
});

it("should get public decorator in internal operation", async () => {
try {
const result = await client.publicDecoratorInInternal("myname");
assert.equal(result.name, "myname");
} catch (err) {
assert.fail(err as string);
}
});

it("should get public shared model in operation", async () => {
try {
const result = await client.publicOperation("myname");
assert.equal(result.name, "myname");
} catch (err) {
assert.fail(err as string);
}
});

it("should get internal shared model in operation", async () => {
try {
const result = await client.internal("myname");
assert.equal(result.name, "myname");
} catch (err) {
assert.fail(err as string);
}
});

it("should get relative model in operation", async () => {
try {
const result = await client.operation("myname");
assert.equal(result.name, "Madge");
assert.equal(result.inner.name, "Madge");
} catch (err) {
assert.fail(err as string);
}
});

it("should get relative model in discriminator", async () => {
try {
const result = await client.discriminator("myname");
assert.equal(result.name, "Madge");
assert.equal(result.kind, "real");
} catch (err) {
assert.fail(err as string);
}
});
});
Loading

0 comments on commit 5ff6da3

Please sign in to comment.