Skip to content

Commit

Permalink
Initial version of tsp emitter (Azure#1347)
Browse files Browse the repository at this point in the history
* Init tsp

* Add language node

* Add query, path and global parameters

* Add body request

* Init schema

* Add some workaround

* initial support for enum

* Handle unkown type

* Fixed names, handle x-ms-mutability and fixed some issues

* Fixed a few issues found in module generation

* Fixed a few issues

* Add tsphost

* inital generator base on tsp

* Fixed some minor issues

* Add support for header response

* Union support for enum

* Skip deep clone for some operation properties

* Fixed the issue for void input of request

* Fixed a few issues

* Fixed an issue related to anonymous object

* remove redundant psNamer(state)

* Add support for validation

* upgrade tps to 0.57

* Fixed a lint error
  • Loading branch information
dolauli authored Jul 4, 2024
1 parent cc5cec2 commit 1bb7ece
Show file tree
Hide file tree
Showing 38 changed files with 5,182 additions and 586 deletions.
2,319 changes: 1,781 additions & 538 deletions common/config/rush/pnpm-lock.yaml

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions powershell/cmdlets/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ export class CmdletNamespace extends Namespace {
operation.parameters = operation.parameters.filter(element => element.required === true);
}
const newClass = await new CmdletClass(this, operation, this.state.path('commands', 'operations', index)).init();

const refCopyPropertyNames = ['parameters', 'requests', 'responses', 'exceptions', 'requestMediaTypes'];
if (operation.variant.includes('ViaJsonString')) {
const name = 'JsonString';
operation.details.csharp.name = `${operation.variant}Via${name}`;

operation.callGraph[operation.callGraph.length - 1] = clone(operation.callGraph[operation.callGraph.length - 1]);
operation.callGraph[operation.callGraph.length - 1] = clone(operation.callGraph[operation.callGraph.length - 1], false, undefined, undefined, refCopyPropertyNames);
operation.callGraph[operation.callGraph.length - 1].language.csharp!.name = `${(<any>operation.callGraph[operation.callGraph.length - 1]).language.csharp!.name}ViaJsonString`;
}
if (operation.variant.includes('ViaJsonFilePath')) {
Expand All @@ -67,7 +67,7 @@ export class CmdletNamespace extends Namespace {
const jsonStringField = new Field('_jsonString', System.String);
newClass.add(jsonStringField);

operation.callGraph[operation.callGraph.length - 1] = clone(operation.callGraph[operation.callGraph.length - 1]);
operation.callGraph[operation.callGraph.length - 1] = clone(operation.callGraph[operation.callGraph.length - 1], false, undefined, undefined, refCopyPropertyNames);
operation.callGraph[operation.callGraph.length - 1].language.csharp!.name = `${(<any>operation.callGraph[operation.callGraph.length - 1]).language.csharp!.name}ViaJsonString`;
}
this.addClass(newClass);
Expand Down
2 changes: 2 additions & 0 deletions powershell/exports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './utils/pwshModel';
export * from './utils/tsp-generator';
12 changes: 9 additions & 3 deletions powershell/internal/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
Schema as NewSchema,
SchemaType,
} from '@autorest/codemodel';
import { TspHost } from '../utils/tsp-host';

export type Schema = T.SchemaT<
LanguageDetails<SchemaDetails>,
Expand Down Expand Up @@ -186,17 +187,22 @@ export class Project extends codeDomProject {
}

constructor(
protected service: Host,
protected service: Host | TspHost,
objectInitializer?: DeepPartial<Project>
) {
super();
this.apply(objectInitializer);
}

public async init(): Promise<this> {
public async init(state?: ModelState<PwshModel>): Promise<this> {
await super.init();

this.state = await new State(this.service).init(this);

if (state) {
this.state.model = state.model;
}

this.schemaDefinitionResolver = new PSSchemaResolver();

this.projectNamespace = this.state.model.language.csharp?.namespace || '';
Expand Down Expand Up @@ -260,7 +266,7 @@ export class Project extends codeDomProject {
// modelcmdlets are models that we will create cmdlets for.
this.modelCmdlets = [];
let directives: Array<any> = [];
const allDirectives = await this.state.getValue('directive');
const allDirectives = await this.state.service.getValue('directive');
directives = values(<any>allDirectives).toArray();
for (const directive of directives.filter((each) => each['model-cmdlet'])) {
this.modelCmdlets = this.modelCmdlets.concat(
Expand Down
3 changes: 2 additions & 1 deletion powershell/internal/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Project } from './project';
import { DeepPartial } from '@azure-tools/codegen';
import { PwshModel } from '../utils/PwshModel';
import { ModelState } from '../utils/model-state';
import { TspHost } from '../utils/tsp-host';


export interface GeneratorSettings {
Expand All @@ -27,7 +28,7 @@ export interface GeneratorSettings {
export class State extends ModelState<PwshModel> {
project!: Project;

public constructor(service: Host, objectInitializer?: DeepPartial<State>) {
public constructor(service: Host | TspHost, objectInitializer?: DeepPartial<State>) {
super(service);
this.apply(objectInitializer);
}
Expand Down
3 changes: 2 additions & 1 deletion powershell/llcsharp/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ import { Dictionary } from '@azure-tools/linq';
import { DeepPartial } from '@azure-tools/codegen';
import { PwshModel } from '../utils/PwshModel';
import { ModelState } from '../utils/model-state';
import { TspHost } from '../utils/tsp-host';

export class State extends ModelState<PwshModel> {
project!: Project;

public constructor(service: Host, objectInitializer?: DeepPartial<State>) {
public constructor(service: Host | TspHost, objectInitializer?: DeepPartial<State>) {
super(service);
this.apply(objectInitializer);
}
Expand Down
12 changes: 9 additions & 3 deletions powershell/llcsharp/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { ServiceNamespace } from './operation/namespace';
import { SupportNamespace } from './enums/namespace';
import { DeepPartial } from '@azure-tools/codegen';
import { PropertyFormat } from '../utils/schema';
import { TspHost } from '../utils/tsp-host';
import { ModelState } from '../utils/model-state';
import { PwshModel } from '../utils/pwshModel';

export class Project extends codeDomProject {

Expand All @@ -36,15 +39,18 @@ export class Project extends codeDomProject {
supportJsonInput!: boolean;
formats!: Dictionary<PropertyFormat>;

constructor(protected service: Host, objectInitializer?: DeepPartial<Project>) {
constructor(protected service: Host | TspHost, objectInitializer?: DeepPartial<Project>) {
super();
this.apply(objectInitializer);
}

public async init(): Promise<this> {
public async init(state?: ModelState<PwshModel>): Promise<this> {
await super.init();

this.state = await new State(this.service).init(this);
if (state) {
this.state.model = state.model;
}

this.apifolder = await this.state.getValue('api-folder', '');
this.runtimefolder = await this.state.getValue('runtime-folder', 'runtime');
this.azure = await this.state.getValue('azure', false) || await this.state.getValue('azure-arm', false);
Expand Down
2 changes: 1 addition & 1 deletion powershell/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"devDependencies": {
"@types/js-yaml": "3.12.1",
"@types/mocha": "5.2.5",
"@types/node": "12.7.2",
"@types/node": "~20.14.0",
"mocha": "10.2.0",
"@testdeck/mocha": "0.3.3",
"typescript": "~5.1.3",
Expand Down
4 changes: 2 additions & 2 deletions powershell/plugins/add-azure-completers-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const locationNames = new Set<string>([
'location',
]);

async function tweakModel(state: State): Promise<PwshModel> {
export async function tweakModel(state: State): Promise<PwshModel> {
const model = state.model;
for (const operation of values(model.commands.operations)) {
for (const parameter of values(operation.parameters)) {
Expand All @@ -46,5 +46,5 @@ async function tweakModel(state: State): Promise<PwshModel> {

export async function addCompleterV2(service: Host) {
const state = await new ModelState<PwshModel>(service).init();
await service.writeFile({filename: 'code-model-v4-add-azure-completers-v2.yaml', content: serialize(await tweakModel(state)), sourceMap: undefined, artifactType: 'code-model-v4'});
await service.writeFile({ filename: 'code-model-v4-add-azure-completers-v2.yaml', content: serialize(await tweakModel(state)), sourceMap: undefined, artifactType: 'code-model-v4' });
}
4 changes: 2 additions & 2 deletions powershell/plugins/cs-namer-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ async function setOperationNames(state: State, resolver: SchemaDefinitionResolve
}
}

async function nameStuffRight(state: State): Promise<PwshModel> {
export async function nameStuffRight(state: State): Promise<PwshModel> {
const resolver = new SchemaDefinitionResolver();
const model = state.model;

Expand Down Expand Up @@ -271,6 +271,6 @@ export async function csnamerV2(service: Host) {
//const session = await startSession<PwshModel>(service, {}, codeModelSchema);
//const result = tweakModelV2(session);
const state = await new ModelState<PwshModel>(service).init();
await service.writeFile({ filename: 'code-model-v4-csnamer-v2.yaml', content: serialize(await nameStuffRight(state)), sourceMap: undefined, artifactType: 'code-model-v4'});
await service.writeFile({ filename: 'code-model-v4-csnamer-v2.yaml', content: serialize(await nameStuffRight(state)), sourceMap: undefined, artifactType: 'code-model-v4' });
}

19 changes: 11 additions & 8 deletions powershell/plugins/llcsharp-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,30 @@ import { applyOverrides, copyResources, deserialize, serialize, } from '@azure-t
import { join } from 'path';
import { Model } from '../llcsharp/code-model';
import { Project } from '../llcsharp/project';
import { TspHost } from '../utils/tsp-host';
import { ModelState } from '../utils/model-state';
import { PwshModel } from '../utils/pwshModel';

const resources = `${__dirname}/../../resources`;

export async function llcsharpV2(service: Host) {
export async function llcsharpV2(service: Host | TspHost, state?: ModelState<PwshModel>) {
try {
const project = await new Project(service).init();
const project = await new Project(service).init(state);
const transformOutput = async (input: string) => {
return await project.state.resolveVariables(input);
};

await project.writeFiles(async (fname, content) => service.writeFile({ filename: join(project.apifolder, fname), content: applyOverrides(content, project.overrides), sourceMap: undefined, artifactType: 'source-file-csharp'}));
await project.writeFiles(async (fname, content) => project.state.writeFile(join(project.apifolder, fname), applyOverrides(content, project.overrides), undefined, 'source-file-csharp'));

// recursive copy resources
await copyResources(join(resources, 'runtime', 'csharp', 'client'), async (fname, content) => service.writeFile({ filename: join(project.runtimefolder, fname), content: content, sourceMap: undefined, artifactType: 'source-file-csharp'}), project.overrides);
await copyResources(join(resources, 'runtime', 'csharp', 'pipeline'), async (fname, content) => service.writeFile({ filename: join(project.runtimefolder, fname), content: content, sourceMap: undefined, artifactType: 'source-file-csharp'}), project.overrides, transformOutput);
await copyResources(join(resources, 'runtime', 'csharp', 'client'), async (fname, content) => project.state.writeFile(join(project.runtimefolder, fname), content, undefined, 'source-file-csharp'), project.overrides);
await copyResources(join(resources, 'runtime', 'csharp', 'pipeline'), async (fname, content) => project.state.writeFile(join(project.runtimefolder, fname), content, undefined, 'source-file-csharp'), project.overrides, transformOutput);
// Note:
// we are using the Carbon.Json library, but we don't *really* want to expose that as public members where we don't have to
// and I don't want to make code changes in the source repository, so I can keep merging from upstream as simple as possible.
// so, we're converting as much as possible to internal, and exposing only what must be exposed to make the code compile.

await copyResources(join(resources, 'runtime', 'csharp', 'json'), async (fname, content) => service.writeFile({ filename: join(project.runtimefolder, fname), content: content, sourceMap: undefined, artifactType: 'source-file-csharp'}), {
await copyResources(join(resources, 'runtime', 'csharp', 'json'), async (fname, content) => project.state.writeFile(join(project.runtimefolder, fname), content, undefined, 'source-file-csharp'), {
...project.overrides,
'public': 'internal',
'internal (.*) class JsonNumber': 'public $1 class JsonNumber',
Expand Down Expand Up @@ -65,11 +68,11 @@ export async function llcsharpV2(service: Host) {
});

if (project.xmlSerialization) {
await copyResources(join(resources, 'runtime', 'csharp', 'xml'), async (fname, content) => service.writeFile({ filename: join(project.runtimefolder, fname), content: content, sourceMap: undefined, artifactType: 'source-file-csharp'}), project.overrides);
await copyResources(join(resources, 'runtime', 'csharp', 'xml'), async (fname, content) => project.state.writeFile(join(project.runtimefolder, fname), content, undefined, 'source-file-csharp'), project.overrides);
}

if (project.azure) {
await copyResources(join(resources, 'runtime', 'csharp.azure'), async (fname, content) => service.writeFile({ filename: join(project.runtimefolder, fname), content: content, sourceMap: undefined, artifactType: 'source-file-csharp'}), project.overrides);
await copyResources(join(resources, 'runtime', 'csharp.azure'), async (fname, content) => project.state.writeFile(join(project.runtimefolder, fname), content, undefined, 'source-file-csharp'), project.overrides);
}

} catch (E) {
Expand Down
8 changes: 8 additions & 0 deletions powershell/plugins/modifiers-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,14 @@ function isWhereEnumDirective(it: any): it is WhereEnumDirective {
return false;
}

export async function tweakModelForTsp(state: State): Promise<PwshModel> {
const allDirectives = await state.service.getValue<any>('directive');
directives = values(allDirectives)
.where(directive => isWhereCommandDirective(directive) || isWhereModelDirective(directive) || isWhereEnumDirective(directive) || isRemoveCommandDirective(directive))
.toArray();
return await tweakModel(state);
}

async function tweakModel(state: State): Promise<PwshModel> {

const isAzure = await state.getValue('azure', false) || await state.getValue('azure-arm', false);
Expand Down
2 changes: 1 addition & 1 deletion powershell/plugins/plugin-create-inline-properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ function createVirtualParameters(operation: CommandOperation) {
operation.details.default.virtualParameters = virtualParameters;
}

async function createVirtuals(state: State): Promise<PwshModel> {
export async function createVirtuals(state: State): Promise<PwshModel> {
/*
A model class should provide inlined properties for anything in a property called properties
Expand Down
9 changes: 8 additions & 1 deletion powershell/plugins/plugin-tweak-m4-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ import { AutorestExtensionHost as Host } from '@autorest/extension-base';

type State = ModelState<PwshModel>;

let directives: Array<any> = [];
export let directives: Array<any> = [];

export async function tweakModelForTsp(state: State): Promise<PwshModel> {
const allDirectives = await state.service.getValue<any>('directive');
directives = values(allDirectives).toArray();
return await tweakModel(state);
}

async function tweakModel(state: State): Promise<PwshModel> {
const model = state.model;
Expand Down Expand Up @@ -208,3 +214,4 @@ export async function tweakM4ModelPlugin(service: Host) {
const state = await new ModelState<PwshModel>(service).init();
service.writeFile({ filename: 'code-model-v4-tweakm4codemodel.yaml', content: serialize(await tweakModel(state)), sourceMap: undefined, artifactType: 'code-model-v4' });
}

4 changes: 2 additions & 2 deletions powershell/plugins/plugin-tweak-model-azure-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type State = ModelState<PwshModel>;

const xmsPageable = 'x-ms-pageable';

async function tweakModel(state: State): Promise<PwshModel> {
export async function tweakModel(state: State): Promise<PwshModel> {
const model = state.model;

// service.message{ Channel: Channel.Debug, Text: "THIS IS THE AZURE TWEAKER" });
Expand Down Expand Up @@ -198,5 +198,5 @@ function getSchema(response: Response): Schema {

export async function tweakModelAzurePluginV2(service: Host) {
const state = await new ModelState<PwshModel>(service).init();
await service.writeFile({ filename: 'code-model-v4-tweakcodemodelazure-v2.yaml', content: serialize(await tweakModel(state)), sourceMap: undefined, artifactType: 'code-model-v4'});
await service.writeFile({ filename: 'code-model-v4-tweakcodemodelazure-v2.yaml', content: serialize(await tweakModel(state)), sourceMap: undefined, artifactType: 'code-model-v4' });
}
4 changes: 2 additions & 2 deletions powershell/plugins/plugin-tweak-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ function dropDuplicatePropertiesInChildSchemas(schema: ObjectSchema, state: Stat
return success;
}

async function tweakModelV2(state: State): Promise<PwshModel> {
export async function tweakModelV2(state: State): Promise<PwshModel> {
const title = pascalCase(fixLeadingNumber(deconstruct(await state.getValue('title', state.model.info.title))));
state.setValue('title', title);

Expand Down Expand Up @@ -645,6 +645,6 @@ export async function tweakModelPlugin(service: Host) {
//const session = await startSession<PwshModel>(service, {}, codeModelSchema);
const state = await new ModelState<PwshModel>(service).init();
//const result = tweakModelV2(session);
await service.writeFile({ filename: 'code-model-v4-tweakcodemodel-v2.yaml', content: serialize(await tweakModelV2(state)), sourceMap: undefined, artifactType: 'code-model-v4'});
await service.writeFile({ filename: 'code-model-v4-tweakcodemodel-v2.yaml', content: serialize(await tweakModelV2(state)), sourceMap: undefined, artifactType: 'code-model-v4' });
//return processCodeModel(tweakModelV2, service, 'tweakcodemodel-v2');
}
28 changes: 16 additions & 12 deletions powershell/plugins/powershell-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ import { generateGitIgnore } from '../generators/gitignore';
import { generateGitAttributes } from '../generators/gitattributes';
import { generateReadme } from '../generators/readme';
import { generateScriptCmdlets } from '../generators/script-cmdlet';
import { TspHost } from '../utils/tsp-host';
import { State } from '../internal/state';
import { ModelState } from '../utils/model-state';
import { PwshModel } from '../utils/pwshModel';

const sourceFileCSharp = 'source-file-csharp';
const resources = `${__dirname}/../../resources`;
Expand Down Expand Up @@ -99,11 +103,11 @@ async function copyRequiredFiles(project: Project) {
}
}

export async function powershellV2(service: Host) {
const debug = (await service.getValue('debug')) || false;
export async function powershellV2(service: Host | TspHost, state?: ModelState<PwshModel>) {
let debug = false;

try {
const project = await new Project(service).init();
const project = await new Project(service).init(state);

await project.writeFiles(async (filename, content) =>
project.state.writeFile(
Expand All @@ -113,15 +117,15 @@ export async function powershellV2(service: Host) {
sourceFileCSharp
)
);

await service.protectFiles(project.psd1);
await service.protectFiles(project.readme);
await service.protectFiles(project.customFolder);
await service.protectFiles(project.testFolder);
await service.protectFiles(project.docsFolder);
await service.protectFiles(project.examplesFolder);
await service.protectFiles(project.resourcesFolder);
await service.protectFiles(project.uxFolder);
debug = (await project.state.service.getValue('debug')) || false;
await project.state.protectFiles(project.psd1);
await project.state.protectFiles(project.readme);
await project.state.protectFiles(project.customFolder);
await project.state.protectFiles(project.testFolder);
await project.state.protectFiles(project.docsFolder);
await project.state.protectFiles(project.examplesFolder);
await project.state.protectFiles(project.resourcesFolder);
await project.state.protectFiles(project.uxFolder);

// wait for all the generation to be done
await copyRequiredFiles(project);
Expand Down
4 changes: 2 additions & 2 deletions powershell/plugins/ps-namer-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function getDeduplicatedNoun(subjectPrefix: string, subject: string): { s
return { subjectPrefix: pascalCase(finalPrefix), subject: pascalCase(reversedFinalSubject.reverse()) };
}

async function tweakModel(state: State): Promise<PwshModel> {
export async function tweakModel(state: State): Promise<PwshModel> {
// get the value
const isAzure = await state.getValue('azure', false);
// without setting snitize-names, isAzure is applied
Expand Down Expand Up @@ -209,5 +209,5 @@ export async function namerV2(service: Host) {
//const session = await startSession<PwshModel>(service, {}, codeModelSchema);
//const result = tweakModelV2(session);
const state = await new ModelState<PwshModel>(service).init();
await service.writeFile({ filename: 'code-model-v4-psnamer-v2.yaml', content: serialize(await tweakModel(state)), sourceMap: undefined, artifactType: 'code-model-v4'});
await service.writeFile({ filename: 'code-model-v4-psnamer-v2.yaml', content: serialize(await tweakModel(state)), sourceMap: undefined, artifactType: 'code-model-v4' });
}
Loading

0 comments on commit 1bb7ece

Please sign in to comment.