-
Notifications
You must be signed in to change notification settings - Fork 742
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Inlcude csharp rename decorator when openai-to-typespec generating tsp (
#4907) Fixes Azure/autorest.csharp#4236. Generate csharp renaming decorator when openai-to-typespec generating tsp. (include rename of resource, resource.property, model, model.property, enum, enum.member and operation name) related autorest.csharp change can be found at Azure/autorest.csharp#4380
- Loading branch information
Showing
16 changed files
with
357 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -296,3 +296,4 @@ regression-tests/output | |
|
||
# TS incremental build cache | ||
*.tsbuildinfo | ||
*.njsproj |
10 changes: 10 additions & 0 deletions
10
...nges/@autorest/openapi-to-typespec/openai-to-typespec-csharp-rename_2024-03-12-05-16.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"changes": [ | ||
{ | ||
"packageName": "@autorest/openapi-to-typespec", | ||
"comment": "support generating csharp rename decorator when converting to tsp", | ||
"type": "minor" | ||
} | ||
], | ||
"packageName": "@autorest/openapi-to-typespec" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
136 changes: 136 additions & 0 deletions
136
packages/extensions/openapi-to-typespec/src/pretransforms/rename-pretransform.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { | ||
ChoiceSchema, | ||
CodeModel, | ||
ObjectSchema, | ||
SealedChoiceSchema, | ||
Schema, | ||
ChoiceValue, | ||
Property, | ||
Parameter, | ||
SchemaType, | ||
Operation, | ||
} from "@autorest/codemodel"; | ||
import { TypespecDecorator } from "../interfaces"; | ||
import { getOptions } from "../options"; | ||
import { getLogger } from "../utils/logger"; | ||
import { Metadata, getArmResourcesMetadata } from "../utils/resource-discovery"; | ||
|
||
type RenamableSchema = Schema | Property | Parameter | ChoiceValue | Operation; | ||
|
||
const logger = () => getLogger("rename-pretransform"); | ||
|
||
export function pretransformRename(codeModel: CodeModel): void { | ||
const { isArm } = getOptions(); | ||
if (!isArm) { | ||
return; | ||
} | ||
|
||
const metadata = getArmResourcesMetadata(); | ||
|
||
applyRenameMapping(metadata, codeModel); | ||
applyOverrideOperationName(metadata, codeModel); | ||
} | ||
|
||
export function createCSharpNameDecorator(schema: RenamableSchema): TypespecDecorator { | ||
return { | ||
name: "clientName", | ||
module: "@azure-tools/typespec-client-generator-core", | ||
namespace: "Azure.ClientGenerator.Core", | ||
arguments: [schema.language.csharp!.name, "csharp"], | ||
}; | ||
} | ||
|
||
function parseNewCSharpNameAndSetToSchema(schema: RenamableSchema, renameValue: string) { | ||
const newName = parseNewName(renameValue); | ||
setSchemaCSharpName(schema, newName); | ||
} | ||
|
||
function setSchemaCSharpName(schema: RenamableSchema, newName: string) { | ||
if (!schema.language.csharp) | ||
schema.language.csharp = { name: newName, description: schema.language.default.description }; | ||
else schema.language.csharp.name = newName; | ||
} | ||
|
||
function parseNewName(value: string) { | ||
// TODO: format not supported | ||
return value.split("|")[0].trim(); | ||
} | ||
|
||
function applyOverrideOperationName(metadata: Metadata, codeModel: CodeModel) { | ||
for (const opId in metadata.OverrideOperationName) { | ||
const found = codeModel.operationGroups.flatMap((og) => og.operations).find((op) => op.operationId === opId); | ||
if (found) parseNewCSharpNameAndSetToSchema(found, metadata.OverrideOperationName[opId]); | ||
else | ||
logger().warning( | ||
`Can't find operation to rename for OverrideOperationName rule: ${opId}->${metadata.OverrideOperationName[opId]}`, | ||
); | ||
} | ||
} | ||
|
||
function applyRenameMapping(metadata: Metadata, codeModel: CodeModel) { | ||
for (const key in metadata.RenameMapping) { | ||
const subKeys = key | ||
.split(".") | ||
.map((s) => s.trim()) | ||
.filter((s) => s.length > 0); | ||
if (subKeys.length === 0) continue; | ||
const lowerFirstSubKey = subKeys[0].toLowerCase(); | ||
const value = metadata.RenameMapping[key]; | ||
|
||
const found: Schema | undefined = [ | ||
...(codeModel.schemas.choices ?? []), | ||
...(codeModel.schemas.sealedChoices ?? []), | ||
...(codeModel.schemas.objects ?? []), | ||
].find((o: Schema) => o.language.default.name.toLowerCase() === lowerFirstSubKey); | ||
|
||
if (!found) { | ||
logger().warning(`Can't find object or enum for RenameMapping rule: ${key} -> ${value}`); | ||
continue; | ||
} | ||
|
||
if (found.type === SchemaType.Choice || found.type == SchemaType.SealedChoice) { | ||
transformEnum(subKeys, value, found as ChoiceSchema | SealedChoiceSchema); | ||
} else if (found.type === SchemaType.Object) { | ||
transformObject(subKeys, value, found as ObjectSchema); | ||
} else { | ||
logger().error(`Unexpected schema type '${found.type}' found with key ${key}`); | ||
} | ||
} | ||
} | ||
|
||
function transformEnum(keys: string[], value: string, target: ChoiceSchema | SealedChoiceSchema) { | ||
if (keys.length === 1) parseNewCSharpNameAndSetToSchema(target, value); | ||
else if (keys.length === 2) { | ||
const lowerMemberValue = keys[1].toLowerCase(); | ||
const found = target.choices.find((c) => c.language.default.name.toLowerCase() === lowerMemberValue); | ||
if (found) parseNewCSharpNameAndSetToSchema(found, value); | ||
else logger().warning(`Can't find enum member for RenameMapping rule: ${keys.join(".")} -> ${value}`); | ||
} else { | ||
logger().error(`Unexpected keys for enum RenameMapping: ${keys.join(".")}`); | ||
} | ||
} | ||
|
||
function transformObject(keys: string[], value: string, target: ObjectSchema) { | ||
if (keys.length === 1) parseNewCSharpNameAndSetToSchema(target, value); | ||
else if (keys.length === 2) { | ||
const lowerPropertyName = keys[1].toLowerCase(); | ||
const found = target.properties?.find((p) => p.language.default.name.toLowerCase() === lowerPropertyName); | ||
if (found) parseNewCSharpNameAndSetToSchema(found, value); | ||
else logger().warning(`Can't find object property for RenameMapping rule: ${keys.join(".")} -> ${value}`); | ||
} else if (keys.length > 2) { | ||
// handle flatten scenario | ||
const lowerPropName = keys.pop()?.toLowerCase(); | ||
let cur = target; | ||
for (let i = 1; i < keys.length && cur; i++) { | ||
const foundProp = cur.properties?.find((p) => p.language.default.name.toLowerCase() === keys[i].toLowerCase()); | ||
cur = foundProp?.schema as ObjectSchema; | ||
} | ||
const foundProp = cur?.properties?.find((p) => p.language.default.name.toLowerCase() === lowerPropName); | ||
if (foundProp) parseNewCSharpNameAndSetToSchema(foundProp, value); | ||
else { | ||
logger().warning(`Can't find object property for RenameMapping rule: ${keys.join(".")} -> ${value}`); | ||
} | ||
} else { | ||
logger().error(`Unexpected keys for object property RenameMapping: ${keys.join(".")}`); | ||
} | ||
} |
Oops, something went wrong.