powershell/plugins/sdk-modifiers.ts (157 lines of code) (raw):

/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { AutorestExtensionHost as Host, Channel } from '@autorest/extension-base'; import { pascalCase, serialize, safeEval } from '@azure-tools/codegen'; import { VirtualParameter } from '@autorest/codemodel'; import { items, values, keys, Dictionary, length } from '@azure-tools/linq'; import { stat } from 'fs'; import common = require('mocha/lib/interfaces/common'); import { CommandOperation, isWritableCmdlet } from '../utils/command-operation'; import { ModelState } from '../utils/model-state'; import { SdkModel } from '../utils/SdkModel'; import { allVirtualParameters, allVirtualProperties } from '../utils/resolve-conflicts'; import { EnumValue, PropertyFormat } from '../utils/schema'; type State = ModelState<SdkModel>; let directives: Array<any> = []; function hasSpecialChars(str: string): boolean { return !/^[a-zA-Z0-9]+$/.test(str); } function getFilterError(whereObject: any, prohibitedFilters: Array<string>, selectionType: string): string { let error = ''; for (const each of values(prohibitedFilters)) { if (whereObject[each] !== undefined) { error += `Can't filter by ${each} when selecting command. `; } } return error; } function getSetError(setObject: any, prohibitedSetters: Array<string>, selectionType: string): string { let error = ''; for (const each of values(prohibitedSetters)) { if (setObject[each] !== undefined) { error += `Can't set ${each} when a ${selectionType} is selected. `; } } return error; } interface WhereModelDirective { select?: string; where: { 'model-name'?: string; 'property-name'?: string; }; set: { 'model-name'?: string; 'property-name'?: string; }; } function isWhereModelDirective(it: any): it is WhereModelDirective { const directive = it; const where = directive.where; const set = directive.set; if (where && set && (where['model-name'] || where['property-name'] || directive.select === 'model')) { return true; } return false; } async function tweakModel(state: State): Promise<SdkModel> { // only look at directives without the `transform` node. for (const directive of directives.filter(each => !each.transform)) { const getPatternToMatch = (selector: string | undefined): RegExp | undefined => { return selector ? !hasSpecialChars(selector) ? new RegExp(`^${selector}$`, 'gi') : new RegExp(selector, 'gi') : undefined; }; if (isWhereModelDirective(directive)) { const selectType = directive.select; const modelNameRegex = getPatternToMatch(directive.where['model-name']); const propertyNameRegex = getPatternToMatch(directive.where['property-name']); const modelNameReplacer = directive.set['model-name']; const propertyNameReplacer = directive.set['property-name']; // select all models let models = [...state.model.schemas.objects ?? []]; // let models = values(state.model.schemas).toArray(); if (modelNameRegex) { models = values(models) .where(model => !!`${model.language.csharp?.name}`.match(modelNameRegex)) .toArray(); } if (propertyNameRegex && selectType === 'model') { models = values(models) .where(model => values(allVirtualProperties(model.language.csharp?.virtualProperties)) .any(property => !!`${property.name}`.match(propertyNameRegex))) .toArray(); } if (propertyNameRegex && (selectType === undefined || selectType === 'property')) { const properties = values(models) .selectMany(model => allVirtualProperties(model.language.csharp?.virtualProperties)) .where(property => !!`${property.name}`.match(propertyNameRegex)) .toArray(); for (const property of values(properties)) { const prevName = property.name; property.name = propertyNameReplacer ? propertyNameRegex ? property.name.replace(propertyNameRegex, propertyNameReplacer) : propertyNameReplacer : property.name; if (!property.name) { state.message({ Channel: Channel.Error, Text: `Directive '${directive.where['model-name']}/${directive.where['property-name']}' attempted to change '${prevName}' to '' ` }); } if (propertyNameRegex) { state.message({ Channel: Channel.Debug, Text: `[DIRECTIVE] Changed property-name from ${prevName} to ${property.name}.` }); } } } else if (models) { // comment out below to disable model name change, which will be added in the tweakModelName before the plugin csnamerSdk // for (const model of values(models)) { // const prevName = model.language.csharp?.name; // if (model.language.csharp) { // model.language.default.fullname = model.language.csharp.fullname = model.language.default.name = model.language.csharp.name = modelNameReplacer ? modelNameRegex ? model.language.csharp.name.replace(modelNameRegex, modelNameReplacer) : modelNameReplacer : model.language.csharp.name; // } // state.message({ // Channel: Channel.Debug, Text: `[DIRECTIVE] Changed model-name from ${prevName} to ${model.language.csharp?.name}.` // }); // } } } } return state.model; } async function tweakModelName(state: State): Promise<SdkModel> { // only look at directives without the `transform` node. for (const directive of directives.filter(each => !each.transform)) { const getPatternToMatch = (selector: string | undefined): RegExp | undefined => { return selector ? !hasSpecialChars(selector) ? new RegExp(`^${selector}$`, 'gi') : new RegExp(selector, 'gi') : undefined; }; if (isWhereModelDirective(directive)) { const selectType = directive.select; const modelNameRegex = getPatternToMatch(directive.where['model-name']); const propertyNameRegex = getPatternToMatch(directive.where['property-name']); const modelNameReplacer = directive.set['model-name']; const propertyNameReplacer = directive.set['property-name']; // select all models let models = [...state.model.schemas.objects ?? []]; // let models = values(state.model.schemas).toArray(); if (modelNameRegex) { models = values(models) .where(model => !!`${model.language.default.name}`.match(modelNameRegex)) .toArray(); } if (propertyNameRegex && selectType === 'model') { models = values(models) .where(model => values(allVirtualProperties(model.language.default.virtualProperties)) .any(property => !!`${property.name}`.match(propertyNameRegex))) .toArray(); } if (propertyNameRegex && (selectType === undefined || selectType === 'property')) { // skip directive for property } else if (models) { for (const model of values(models)) { const prevName = model.language.default.name; model.language.default.name = modelNameReplacer ? modelNameRegex ? model.language.default.name.replace(modelNameRegex, modelNameReplacer) : modelNameReplacer : model.language.default.name; state.message({ Channel: Channel.Debug, Text: `[DIRECTIVE] Changed model-name from ${prevName} to ${model.language.default.name}.` }); } } } } return state.model; } export async function applyModifiersSdk(service: Host) { // dolauli implement directives const allDirectives = await service.getValue<any>('directive'); directives = values(allDirectives) // .select(directive => directive) .where(directive => isWhereModelDirective(directive)) .toArray(); const state = await new ModelState<SdkModel>(service).init(); const result = await tweakModel(state); await service.writeFile({ filename: 'code-model-v4-modifiers-sdk.yaml', content: serialize(result), sourceMap: undefined, artifactType: 'code-model-sdk' }); } export async function applyModelNameModifiersSdk(service: Host) { // dolauli implement directives const allDirectives = await service.getValue<any>('directive'); directives = values(allDirectives) // .select(directive => directive) .where(directive => isWhereModelDirective(directive)) .toArray(); const state = await new ModelState<SdkModel>(service).init(); const result = await tweakModelName(state); await service.writeFile({ filename: 'code-model-v4-model-name-modifiers-sdk.yaml', content: serialize(result), sourceMap: undefined, artifactType: 'code-model-sdk' }); }