Skip to content

Commit

Permalink
[tcgc] add disableUsageAccessPropagationToBase flag and separate ex…
Browse files Browse the repository at this point in the history
…ception usage from output usage (#1834)

resolve: #1827

add `disableUsageAccessPropagationToBase` flag: this flag aims to
support language that does not generate base model, it will skip the
base model's propagation but will not skip the property or subtype of
the base model.
  • Loading branch information
tadelesh authored Nov 13, 2024
1 parent 8909b98 commit d16a069
Show file tree
Hide file tree
Showing 6 changed files with 311 additions and 2 deletions.
7 changes: 7 additions & 0 deletions .chronus/changes/add_usage_helper-2024-10-11-17-1-36.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@azure-tools/typespec-client-generator-core"
---

add `disableUsageAccessPropagationToBase` to support language that does not generate base model
3 changes: 3 additions & 0 deletions packages/typespec-client-generator-core/src/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ export function createTCGCContext(program: Program, emitterName: string): TCGCCo
__tspTypeToApiVersions: new Map(),
__clientToApiVersionClientDefaultValue: new Map(),
previewStringRegex: /-preview$/,
disableUsageAccessPropagationToBase: false,
};
}

Expand All @@ -626,6 +627,7 @@ interface VersioningStrategy {
export interface CreateSdkContextOptions {
readonly versioning?: VersioningStrategy;
additionalDecorators?: string[];
disableUsageAccessPropagationToBase?: boolean; // this flag is for some languages that has no need to generate base model, but generate model with composition
}

export async function createSdkContext<
Expand Down Expand Up @@ -658,6 +660,7 @@ export async function createSdkContext<
examplesDir: context.options["examples-dir"] ?? context.options["examples-directory"],
decoratorsAllowList: [...defaultDecoratorsAllowList, ...(options?.additionalDecorators ?? [])],
previewStringRegex: options?.versioning?.previewStringRegex || tcgcContext.previewStringRegex,
disableUsageAccessPropagationToBase: options?.disableUsageAccessPropagationToBase ?? false,
};
sdkContext.sdkPackage = diagnostics.pipe(getSdkPackage(sdkContext));
for (const client of sdkContext.sdkPackage.clients) {
Expand Down
1 change: 1 addition & 0 deletions packages/typespec-client-generator-core/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface TCGCContext {
examplesDir?: string;
decoratorsAllowList?: string[];
previewStringRegex: RegExp;
disableUsageAccessPropagationToBase: boolean;
}

export interface SdkContext<
Expand Down
14 changes: 12 additions & 2 deletions packages/typespec-client-generator-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1383,7 +1383,10 @@ function updateUsageOrAccess(
if (options?.seenTypes === undefined) {
options.seenTypes = new Set<SdkType>();
}
if (options.seenTypes.has(type)) return diagnostics.wrap(undefined); // avoid circular references
if (options.seenTypes.has(type)) {
options.skipFirst = false;
return diagnostics.wrap(undefined); // avoid circular references
}
if (type.kind === "array" || type.kind === "dict") {
diagnostics.pipe(updateUsageOrAccess(context, value, type.valueType, options));
return diagnostics.wrap(undefined);
Expand Down Expand Up @@ -1443,10 +1446,12 @@ function updateUsageOrAccess(
}
} else {
options.skipFirst = false;
if (typeof value !== "number") {
type.__accessSet = true;
}
}

if (type.kind === "enum") return diagnostics.wrap(undefined);
if (!options.propagation) return diagnostics.wrap(undefined);
if (type.kind === "union") {
for (const unionType of type.variantTypes) {
diagnostics.pipe(updateUsageOrAccess(context, value, unionType, options));
Expand All @@ -1457,8 +1462,13 @@ function updateUsageOrAccess(
diagnostics.pipe(updateUsageOrAccess(context, value, type.type, options));
return diagnostics.wrap(undefined);
}

if (!options.propagation) return diagnostics.wrap(undefined);
if (type.baseModel) {
options.ignoreSubTypeStack.push(true);
if (context.disableUsageAccessPropagationToBase) {
options.skipFirst = true;
}
diagnostics.pipe(updateUsageOrAccess(context, value, type.baseModel, options));
options.ignoreSubTypeStack.pop();
}
Expand Down
146 changes: 146 additions & 0 deletions packages/typespec-client-generator-core/test/decorators/access.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -730,4 +730,150 @@ describe("typespec-client-generator-core: @access", () => {
code: "@azure-tools/typespec-client-generator-core/conflict-access-override",
});
});

it("disableUsageAccessPropagationToBase true with override", async () => {
runner = await createSdkTestRunner(
{ emitterName: "@azure-tools/typespec-python" },
{ disableUsageAccessPropagationToBase: true },
);
await runner.compileWithBuiltInService(
`
model BaseClassThatsPruned {
id: int32;
}
model DerivedOne extends BaseClassThatsPruned {
name: string;
prop: UsedByProperty;
}
model UsedByProperty {
prop: string;
}
@@usage(DerivedOne, Usage.output);
@@access(DerivedOne, Access.public);
`,
);
const models = runner.context.sdkPackage.models;
strictEqual(models.length, 2);
strictEqual(models[0].access, "public");
strictEqual(models[0].name, "DerivedOne");
strictEqual(models[1].access, "public");
strictEqual(models[1].name, "UsedByProperty");
});

it("disableUsageAccessPropagationToBase true", async () => {
runner = await createSdkTestRunner(
{ emitterName: "@azure-tools/typespec-python" },
{ disableUsageAccessPropagationToBase: true },
);
await runner.compileWithBuiltInService(
`
model BaseClassThatsPruned {
id: int32;
}
model DerivedOne extends BaseClassThatsPruned {
name: string;
prop: UsedByProperty;
}
model UsedByProperty {
prop: string;
}
@access(Access.internal)
op test(): DerivedOne;
`,
);
const models = runner.context.sdkPackage.models;
strictEqual(models.length, 2);
strictEqual(models[0].access, "internal");
strictEqual(models[0].name, "DerivedOne");
strictEqual(models[1].access, "internal");
strictEqual(models[1].name, "UsedByProperty");
});

it("disableUsageAccessPropagationToBase true property propagation", async () => {
runner = await createSdkTestRunner(
{ emitterName: "@azure-tools/typespec-python" },
{ disableUsageAccessPropagationToBase: true },
);
await runner.compileWithBuiltInService(
`
model BaseClassThatsPruned {
id: int32;
foo: UsedByBaseProperty;
}
model DerivedOne extends BaseClassThatsPruned {
name: string;
prop: UsedByProperty;
}
model UsedByProperty {
prop: string;
}
model UsedByBaseProperty {
prop: string;
}
@access(Access.internal)
op test(): DerivedOne;
`,
);
const models = runner.context.sdkPackage.models;
strictEqual(models.length, 3);
strictEqual(models[0].access, "internal");
strictEqual(models[0].name, "DerivedOne");
strictEqual(models[1].access, "internal");
strictEqual(models[1].name, "UsedByProperty");
strictEqual(models[2].access, "internal");
strictEqual(models[2].name, "UsedByBaseProperty");
});

it("disableUsageAccessPropagationToBase true discriminator propagation", async () => {
runner = await createSdkTestRunner(
{ emitterName: "@azure-tools/typespec-python" },
{ disableUsageAccessPropagationToBase: true },
);
await runner.compileWithBuiltInService(
`
@discriminator("kind")
model Fish {
age: int32;
}
@discriminator("sharktype")
model Shark extends Fish {
kind: "shark";
origin: Origin;
}
model Salmon extends Fish {
kind: "salmon";
}
model SawShark extends Shark {
sharktype: "saw";
}
model Origin {
country: string;
city: string;
manufacture: string;
}
@get
@access(Access.internal)
op getModel(): Fish;
`,
);
const models = runner.context.sdkPackage.models;
strictEqual(models.length, 5);
strictEqual(models[0].access, "internal");
strictEqual(models[0].name, "Fish");
strictEqual(models[1].access, "internal");
strictEqual(models[1].name, "Shark");
strictEqual(models[2].access, "internal");
strictEqual(models[2].name, "Origin");
strictEqual(models[3].access, "internal");
strictEqual(models[3].name, "SawShark");
strictEqual(models[4].access, "internal");
strictEqual(models[4].name, "Salmon");
});
});
142 changes: 142 additions & 0 deletions packages/typespec-client-generator-core/test/decorators/usage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,4 +459,146 @@ describe("typespec-client-generator-core: @usage", () => {
strictEqual(models[1].usage, UsageFlags.Output);
strictEqual(models[1].access, "public");
});

it("disableUsageAccessPropagationToBase true with override", async () => {
runner = await createSdkTestRunner(
{ emitterName: "@azure-tools/typespec-python" },
{ disableUsageAccessPropagationToBase: true },
);
await runner.compileWithBuiltInService(
`
model BaseClassThatsPruned {
id: int32;
}
model DerivedOne extends BaseClassThatsPruned {
name: string;
prop: UsedByProperty;
}
model UsedByProperty {
prop: string;
}
@@usage(DerivedOne, Usage.output);
`,
);
const models = runner.context.sdkPackage.models;
strictEqual(models.length, 2);
strictEqual(models[0].usage, UsageFlags.Output);
strictEqual(models[0].name, "DerivedOne");
strictEqual(models[1].usage, UsageFlags.Output);
strictEqual(models[1].name, "UsedByProperty");
});

it("disableUsageAccessPropagationToBase true", async () => {
runner = await createSdkTestRunner(
{ emitterName: "@azure-tools/typespec-python" },
{ disableUsageAccessPropagationToBase: true },
);
await runner.compileWithBuiltInService(
`
model BaseClassThatsPruned {
id: int32;
}
model DerivedOne extends BaseClassThatsPruned {
name: string;
prop: UsedByProperty;
}
model UsedByProperty {
prop: string;
}
op test(): DerivedOne;
`,
);
const models = runner.context.sdkPackage.models;
strictEqual(models.length, 2);
strictEqual(models[0].usage, UsageFlags.Output | UsageFlags.Json);
strictEqual(models[0].name, "DerivedOne");
strictEqual(models[1].usage, UsageFlags.Output | UsageFlags.Json);
strictEqual(models[1].name, "UsedByProperty");
});

it("disableUsageAccessPropagationToBase true property propagation", async () => {
runner = await createSdkTestRunner(
{ emitterName: "@azure-tools/typespec-python" },
{ disableUsageAccessPropagationToBase: true },
);
await runner.compileWithBuiltInService(
`
model BaseClassThatsPruned {
id: int32;
foo: UsedByBaseProperty;
}
model DerivedOne extends BaseClassThatsPruned {
name: string;
prop: UsedByProperty;
}
model UsedByProperty {
prop: string;
}
model UsedByBaseProperty {
prop: string;
}
op test(): DerivedOne;
`,
);
const models = runner.context.sdkPackage.models;
strictEqual(models.length, 3);
strictEqual(models[0].usage, UsageFlags.Output | UsageFlags.Json);
strictEqual(models[0].name, "DerivedOne");
strictEqual(models[1].usage, UsageFlags.Output | UsageFlags.Json);
strictEqual(models[1].name, "UsedByProperty");
strictEqual(models[2].usage, UsageFlags.Output | UsageFlags.Json);
strictEqual(models[2].name, "UsedByBaseProperty");
});

it("disableUsageAccessPropagationToBase true discriminator propagation", async () => {
runner = await createSdkTestRunner(
{ emitterName: "@azure-tools/typespec-python" },
{ disableUsageAccessPropagationToBase: true },
);
await runner.compileWithBuiltInService(
`
@discriminator("kind")
model Fish {
age: int32;
}
@discriminator("sharktype")
model Shark extends Fish {
kind: "shark";
origin: Origin;
}
model Salmon extends Fish {
kind: "salmon";
}
model SawShark extends Shark {
sharktype: "saw";
}
model Origin {
country: string;
city: string;
manufacture: string;
}
@get
op getModel(): Fish;
`,
);
const models = runner.context.sdkPackage.models;
strictEqual(models.length, 5);
strictEqual(models[0].usage, UsageFlags.Output | UsageFlags.Json);
strictEqual(models[0].name, "Fish");
strictEqual(models[1].usage, UsageFlags.Output | UsageFlags.Json);
strictEqual(models[1].name, "Shark");
strictEqual(models[2].usage, UsageFlags.Output | UsageFlags.Json);
strictEqual(models[2].name, "Origin");
strictEqual(models[3].usage, UsageFlags.Output | UsageFlags.Json);
strictEqual(models[3].name, "SawShark");
strictEqual(models[4].usage, UsageFlags.Output | UsageFlags.Json);
strictEqual(models[4].name, "Salmon");
});
});

0 comments on commit d16a069

Please sign in to comment.