Skip to content

Commit

Permalink
[tcgc] consider renaming when mapping examples (#1431)
Browse files Browse the repository at this point in the history
fix: #1430
  • Loading branch information
tadelesh committed Aug 28, 2024
1 parent 34d4d58 commit 955fc95
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 3 deletions.
7 changes: 7 additions & 0 deletions .chronus/changes/fix_example_load-2024-7-27-14-24-44.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: fix
packages:
- "@azure-tools/typespec-client-generator-core"
---

consider renaming when mapping examples
32 changes: 30 additions & 2 deletions packages/typespec-client-generator-core/src/example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import {
Diagnostic,
DiagnosticCollector,
NoTarget,
Operation,
createDiagnosticCollector,
isGlobalNamespace,
isService,
resolvePath,
} from "@typespec/compiler";
import { HttpStatusCodeRange } from "@typespec/http";
import { resolveOperationId } from "@typespec/openapi";
import { getOperationId } from "@typespec/openapi";
import {
SdkAnyExample,
SdkArrayExample,
Expand Down Expand Up @@ -37,6 +40,7 @@ import {
} from "./interfaces.js";
import { getValidApiVersion } from "./internal-utils.js";
import { createDiagnostic } from "./lib.js";
import { getLibraryName } from "./public-utils.js";

interface LoadedExample {
readonly relativePath: string;
Expand Down Expand Up @@ -139,6 +143,30 @@ async function loadExamples(
return diagnostics.wrap(map);
}

function resolveOperationId(context: TCGCContext, operation: Operation) {
const { program } = context;
// if @operationId was specified use that value
const explicitOperationId = getOperationId(program, operation);
if (explicitOperationId) {
return explicitOperationId;
}

const operationName = getLibraryName(context, operation);
if (operation.interface) {
return `${getLibraryName(context, operation.interface)}_${operationName}`;
}
const namespace = operation.namespace;
if (
namespace === undefined ||
isGlobalNamespace(program, namespace) ||
isService(program, namespace)
) {
return operationName;
}

return `${getLibraryName(context, namespace)}_${operationName}`;
}

export async function handleClientExamples(
context: TCGCContext,
client: SdkClientType<SdkServiceOperation>
Expand All @@ -157,7 +185,7 @@ export async function handleClientExamples(
// since operation could have customization in client.tsp, we need to handle all the original operation (exclude the templated operation)
let operation = method.__raw;
while (operation && operation.templateMapper === undefined) {
const operationId = resolveOperationId(context.program, operation).toLowerCase();
const operationId = resolveOperationId(context, operation).toLowerCase();
if (examples.has(operationId)) {
diagnostics.pipe(handleMethodExamples(context, method, examples.get(operationId)!));
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,37 @@ describe("typespec-client-generator-core: http operation examples", () => {
expectDiagnostics(runner.context.diagnostics, []);
});

it("body fallback", async () => {
await runner.host.addRealTypeSpecFile(
"./examples/parameters.json",
`${__dirname}/http-operation-examples/bodyFallback.json`
);
await runner.compile(`
@service({})
namespace TestClient {
op bodyTest(prop: string): void;
}
`);

const operation = (
runner.context.sdkPackage.clients[0].methods[0] as SdkServiceMethod<SdkHttpOperation>
).operation;
ok(operation);
strictEqual(operation.examples?.length, 1);
strictEqual(operation.examples[0].kind, "http");

const parameters = operation.examples[0].parameters;
ok(parameters);
strictEqual(parameters.length, 1);

strictEqual(parameters[0].value.kind, "model");
strictEqual(parameters[0].value.value["prop"].kind, "string");
strictEqual(parameters[0].value.value["prop"].value, "body");
strictEqual(parameters[0].value.type.kind, "model");

expectDiagnostics(runner.context.diagnostics, []);
});

it("parameters diagnostic", async () => {
await runner.host.addRealTypeSpecFile(
"./examples/parametersDiagnostic.json",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"operationId": "bodyTest",
"title": "bodyTest",
"parameters": {
"body": {
"prop": "body"
}
},
"responses": {}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expectDiagnostics } from "@typespec/compiler/testing";
import { ok, strictEqual } from "assert";
import { beforeEach, describe, it } from "vitest";
import { SdkHttpOperation, SdkServiceMethod } from "../../src/interfaces.js";
import { SdkClientAccessor, SdkHttpOperation, SdkServiceMethod } from "../../src/interfaces.js";
import { SdkTestRunner, createSdkTestRunner } from "../test-host.js";

describe("typespec-client-generator-core: load examples", () => {
Expand Down Expand Up @@ -169,4 +169,46 @@ describe("typespec-client-generator-core: load examples", () => {
ok(operation);
strictEqual(operation.examples?.length, 1);
});

it("load multiple example with @clientName", async () => {
await runner.host.addRealTypeSpecFile(
"./examples/clientName.json",
`${__dirname}/load/clientName.json`
);
await runner.host.addRealTypeSpecFile(
"./examples/clientNameAnother.json",
`${__dirname}/load/clientNameAnother.json`
);
await runner.compile(`
@service({})
namespace TestClient {
@clientName("renamedNS")
namespace NS {
@route("/ns")
@clientName("renamedOP")
op get(): string;
}
@clientName("renamedIF")
namespace IF {
@route("/if")
@clientName("renamedOP")
op get(): string;
}
}
`);

let operation = (
(runner.context.sdkPackage.clients[0].methods[0] as SdkClientAccessor<SdkHttpOperation>)
.response.methods[0] as SdkServiceMethod<SdkHttpOperation>
).operation;
ok(operation);
strictEqual(operation.examples?.length, 1);
operation = (
(runner.context.sdkPackage.clients[0].methods[1] as SdkClientAccessor<SdkHttpOperation>)
.response.methods[0] as SdkServiceMethod<SdkHttpOperation>
).operation;
ok(operation);
strictEqual(operation.examples?.length, 1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"operationId": "renamedNS_renamedOP",
"title": "renamedNS_renamedOP",
"parameters": {},
"responses": {
"200": {
"description": "ARM operation completed successfully.",
"body": "test"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"operationId": "renamedIF_renamedOP",
"title": "renamedIF_renamedOP",
"parameters": {},
"responses": {
"200": {
"description": "ARM operation completed successfully.",
"body": "test"
}
}
}

0 comments on commit 955fc95

Please sign in to comment.