2. support changing naming convention for language->default
3. update doc
This commit is contained in:
Rodge Fu 2020-02-27 18:39:30 +08:00
Родитель 5c641eebd6
Коммит 929f80f675
5 изменённых файлов: 253 добавлений и 126 удалений

131
README.md
Просмотреть файл

@ -40,17 +40,34 @@ modelerfour:
client: 'pascal'
clicommon:
naming:
singularize:
- operationGroup
- operation
parameter: 'camel'
operation: 'pascal'
operationGroup: 'pascal'
property: 'camel'
type: 'pascal'
choice: 'pascal'
choiceValue: 'pascal'
naming:
cli:
singularize:
- operationGroup
- operation
override:
cmyk : CMYK
$host: $host
LRO: LRO
parameter: 'camel'
operation: 'pascal'
operationGroup: 'pascal'
property: 'camel'
type: 'pascal'
choice: 'pascal'
choiceValue: 'pascal'
default:
override:
cmyk : CMYK
$host: $host
LRO: LRO
parameter: 'camel'
operation: 'pascal'
operationGroup: 'pascal'
property: 'camel'
type: 'pascal'
choice: 'pascal'
choiceValue: 'pascal'
```
# Available Configurations for CLI Common:
@ -62,29 +79,42 @@ clicommon:
> Naming convention to be used in the output of clicommon.
> Please make sure **snake_naming_convention** is used if the name is changed through directive
> so that it will be converted to correct naming convention in the output
> so that it will be converted to correct naming convention in the output. Samples as below:
``` $(sample-cli-directive)
clicommon:
# known word that won't be singularized
glossary:
- 'insights'
naming:
# list the resource that needs to singularize
singularize:
- operationGroup
- operation
# possible value: pascal|camel|snake|upper|kebab|space
parameter: 'camel'
operation: 'pascal'
operationGroup: 'pascal'
property: 'camel'
type: 'pascal'
choice: 'pascal'
choiceValue: 'pascal'
naming:
cli:
singularize:
- operationGroup
- operation
override:
cmyk : CMYK
$host: $host
LRO: LRO
parameter: 'camel'
operation: 'pascal'
operationGroup: 'pascal'
property: 'camel'
type: 'pascal'
choice: 'pascal'
choiceValue: 'pascal'
default:
override:
cmyk : CMYK
$host: $host
LRO: LRO
parameter: 'camel'
operation: 'pascal'
operationGroup: 'pascal'
property: 'camel'
type: 'pascal'
choice: 'pascal'
choiceValue: 'pascal'
```
## Directive
> so please make sure **snake_naming_convention** is used for 'where' and 'name' clause in directive
@ -100,6 +130,7 @@ clicommon:
- conditions to locate the object to apply directive
- required
- **snake_naming_converion** is expected as the value
- regex is supported in the value
- possible search condition, refer to sample below for more detail usage:
- search for operatoinGroup, operation or parameter
- 'operationGroup' | 'group' | 'resource': 'name_in_snake_naming_convention'
@ -115,22 +146,33 @@ clicommon:
- set anything property in the selected object(s)
- optional
- name:
- add 'name: ...' in the selected object(s). Please make sure **snake_naming_convention** is used
- add 'name: ...' under 'language->cli'. Please make sure **snake_naming_convention** is used
- optional
- hide:
- add 'hide: ...' in the selected object(s).
- add 'hide: ...' under 'language->cli'.
- optional
- remove:
- add 'remove: ...' in the seleected object(s).
- add 'remove: ...' under 'language->cli'.
- optional
- alias:
- add 'alias: ...' under 'language->cli'
- optional
- formatTable:
- add properties information in the selected object(s).
- add properties information under 'language->cli'.
- optional
- possible sub item:
- value format:
- properties:
- prop1Name
- prop2Name
- ...
- replace:
- do replacement
- optional
- value format:
- field: 'name'
- old: 'old_value'
- new: 'new_value'
- isRegex: true | false
#### How to figure out the name to be used in directives
- Enable output for clicommon by following configuration:
@ -145,20 +187,18 @@ clicommon:
``` $(sample-cli-directive)
clicommon:
cli-directive:
# set operationGroup's name.
# as alias, 'resource' or 'group' can also be used
# directive on operationGroup
- select: 'operationGroup'
where:
operationGroup: 'old_name'
name: 'new_name'
- where:
resource: 'old_name'
name: 'new_name'
hide: true
- where:
group: 'old_name'
name: 'new_name'
remove: 'true
# add hide property for operation
# as alias, 'op' can also be used
- where:
group: 'group_name'
operation: 'operation_name'
@ -168,7 +208,6 @@ clicommon:
op: 'operatoin_name'
hide: true
# add remove property for parameter
# as alias, 'param' can also be used
- where:
group: 'group_name'
op: 'operation_name'
@ -179,8 +218,11 @@ clicommon:
op: 'operation_name'
param: 'parameter_name'
remove: true
# add hide property for all parameter start with 'abc'
- where:
parameter: '^abc.*$'
hide: true
# set table format under for schema
# as alias, 'type' and 'object' can also be used
- where:
schemaObject: 'schema_name'
tableFormat:
@ -200,7 +242,6 @@ clicommon:
- 'p1'
- 'p2'
# set anything for schema property
# as alias, 'prop' can also be used
- where:
type: 'schema_name'
property: 'property_name'
@ -219,7 +260,7 @@ clicommon:
key3:
- v1
- v2
# replace 'name_a' with 'name_b' (whole word match)
# replac 'name_a' with 'name_b' (whole word match) in operation's name
- where:
group: 'group_name'
op: 'operation_name'
@ -234,10 +275,10 @@ clicommon:
op: 'operation_name'
replace:
field: 'description'
old: 'startByThis(.*)'
new: 'startByThat$1'
old: '(startByThis)(.*)'
new: 'startByThat$2'
isRegex: true
# add alias property
# add alias for enum value
- where:
choiceSchema: 'choice_type'
choiceValue: 'choice_value_name'

Просмотреть файл

@ -1,7 +1,9 @@
import { ChoiceSchema, ChoiceValue, CodeModel, ObjectSchema, Operation, OperationGroup, Parameter, Property, SealedChoiceSchema } from "@azure-tools/codemodel";
import { keys } from "@azure-tools/linq";
import { isArray, isNull, isNullOrUndefined, isObject, isString, isUndefined } from "util";
import { CliConst, M4Node, M4NodeType, NamingType } from "./schema";
import { CliConst, M4Node, M4NodeType, NamingType, CliCommonSchema } from "./schema";
import { pascalCase, EnglishPluralizationService } from '@azure-tools/codegen';
export class Helper {
public static isEmptyString(str): boolean {
@ -101,6 +103,98 @@ export class Helper {
throw Error(`Unsupported node type: ${typeof (node)}`);
}
public static singularize(settings: CliCommonSchema.NamingConvention, word: string): string {
let low = word.toLowerCase();
if (settings.glossary.findIndex(v => v === low) >= 0)
return word;
const eps = new EnglishPluralizationService();
eps.addWord('Database', 'Databases');
eps.addWord('database', 'databases');
return eps.singularize(word);
}
public static normalizeNamingSettings(settings: CliCommonSchema.NamingConvention) {
if (isNullOrUndefined(settings.singularize))
settings.singularize = [];
if (isNullOrUndefined(settings.glossary))
settings.glossary = [];
else
settings.glossary = settings.glossary.map(v => v.toLowerCase());
if (isNullOrUndefined(settings.override))
settings.override = {};
else {
for (let key in settings.override)
settings.override[key.toLowerCase()] = settings.override[key];
}
return settings;
}
/**
* Remark: Please make sure the singularize, glossary and override is set to [] or {} instead of null or undefined when calling this method
* No check for them will be done in this method for better performance. exception may be thrown if they are null or undefiend.
* You can call Helper.normalizeNamingSettings to do these normalization
* @param settings
* @param node
* @param languageKey
*/
public static applyNamingConvention(settings: CliCommonSchema.NamingConvention, node: M4Node, languageKey: string) {
if (isNullOrUndefined(node.language[languageKey]))
return;
let namingType = Helper.ToNamingType(node);
if (isNullOrUndefined(namingType)) {
// unsupported modelerfour node for naming type, ignore it for now
return;
}
let style = settings[namingType];
let single = settings.singularize.includes(namingType) === true;
if (Helper.isEmptyString(style)) {
// Only process when naming convention is set
return;
}
let oldName: string = node.language[languageKey]['name'];
let up1 = (n: string) => n.length == 1 ? n.toUpperCase() : n[0].toUpperCase().concat(n.substr(1).toLowerCase());
let op = {};
op[CliConst.NamingStyle.camel] = {
wording: (v: string, i: number) => i === 0 ? v.toLowerCase() : up1(v),
sep: '',
};
op[CliConst.NamingStyle.pascal] = {
wording: (v: string, i: number) => up1(v),
sep: '',
};
op[CliConst.NamingStyle.kebab] = {
wording: (v: string, i: number) => v.toLowerCase(),
sep: '-',
};
op[CliConst.NamingStyle.snake] = {
wording: (v: string, i: number) => v.toLowerCase(),
sep: '_',
};
op[CliConst.NamingStyle.space] = {
wording: (v: string, i: number) => v.toLowerCase(),
sep: ' ',
};
op[CliConst.NamingStyle.upper] = {
wording: (v: string, i: number) => v.toUpperCase(),
sep: '_',
};
// the oldName should be in snake_naming_convention
const SEP = '_';
let newName = oldName.split(SEP).map((v, i) =>
(!isNullOrUndefined(settings.override[v.toLowerCase()]))
? settings.override[v.toLowerCase()]
: op[style].wording(single ? Helper.singularize(settings, v) : v, i)
).join(op[style].sep);
node.language[languageKey]['name'] = newName;
}
public static toYamlSimplified(codeModel: CodeModel): string {
const INDENT = ' ';
const NEW_LINE = '\n';
@ -131,7 +225,7 @@ export class Helper {
v.operations.map(vv => `${tab(2)}- operationName: ${generateCliValue(vv, 3)}` +
`${NEW_LINE}${tab(3)}parameters:${NEW_LINE}`.concat(
vv.request.parameters.map(vvv => `${tab(3)}- parameterName: ${generateCliValue(vvv, 4)}${NEW_LINE}` +
((vvv.protocol.http.in === 'body') ? `${tab(4)}bodySchema: ${vvv.schema.language.default.name}${NEW_LINE}` : ''))
(((!isNullOrUndefined(vvv.protocol?.http?.in)) && vvv.protocol.http.in === 'body') ? `${tab(4)}bodySchema: ${vvv.schema.language.default.name}${NEW_LINE}` : ''))
.join(''))).join(''))).join(''));
s = s + `schemas:${NEW_LINE}` +
`${tab()}objects:${NEW_LINE}` +

Просмотреть файл

@ -1,17 +1,16 @@
import { CodeModel, codeModelSchema, Property, Language, Metadata, Operation, OperationGroup, Parameter, ComplexSchema, ObjectSchema, ChoiceSchema, ChoiceValue, SealedChoiceSchema } from '@azure-tools/codemodel';
import { Session, Host, startSession, Channel } from '@azure-tools/autorest-extension-base';
import { serialize, deserialize } from '@azure-tools/codegen';
import { values, items, length, Dictionary } from '@azure-tools/linq';
import { values, items, length, Dictionary, keys } from '@azure-tools/linq';
import { isNullOrUndefined } from 'util';
import { CliCommonSchema, CliConst, LanguageType, M4Node } from '../schema';
import { Helper } from '../helper';
import { pascalCase, EnglishPluralizationService } from '@azure-tools/codegen';
export class CommonNamer {
codeModel: CodeModel
namingConvention: CliCommonSchema.NamingConvention
cliNamingSettings: CliCommonSchema.NamingConvention
defaultNamingSettings: CliCommonSchema.NamingConvention
flag: Set<Metadata>
glossary: string[]
constructor(protected session: Session<CodeModel>) {
this.codeModel = session.model;
@ -19,10 +18,9 @@ export class CommonNamer {
async init() {
// any configuration if necessary
this.namingConvention = await this.session.getValue("naming", {});
if (isNullOrUndefined(this.namingConvention.singularize))
this.namingConvention.singularize = [];
this.glossary = await this.session.getValue("glossary", []);
this.cliNamingSettings = Helper.normalizeNamingSettings(await this.session.getValue("naming.cli", {}));
this.defaultNamingSettings = Helper.normalizeNamingSettings(await this.session.getValue("naming.default", {}));
return this;
}
@ -35,77 +33,7 @@ export class CommonNamer {
this.flag = null;
return this.codeModel;
}
singularize(word: string): string {
let loWord = word.toLowerCase();
if (this.glossary.findIndex(v => v === loWord) >= 0)
return word;
const eps = new EnglishPluralizationService();
eps.addWord('Database', 'Databases');
eps.addWord('database', 'databases');
return eps.singularize(word);
}
/**
* only support Operation, OperationGroup, Parameter, Property, ObjectSchema for now
* @param oldName
* @param metadata
*/
public convertNamingConvention(node: M4Node) {
if (isNullOrUndefined(node.language['cli']))
return;
let namingType = Helper.ToNamingType(node);
if (isNullOrUndefined(namingType)) {
// unsupported modelerfour node for naming type, ignore it
return;
}
let style = this.namingConvention[namingType];
let single = this.namingConvention.singularize.includes(namingType);
if (Helper.isEmptyString(style)) {
// Only process when naming convention is set
return;
}
let oldName: string = node.language['cli']['name'];
let glossary: string[] = node.language['cli']['glossary'];
if (isNullOrUndefined(glossary))
glossary = [];
let getSingleArr = (n: string) => n.split(SEP).map(v => this.singularize(v));
let getPluralArr = (n: string) => n.split(SEP);
let up1 = (n: string) => n.length == 1 ? n.toUpperCase() : n[0].toUpperCase().concat(n.substr(1).toLowerCase());
const SEP = '_';
let newName: string;
switch (style) {
case CliConst.NamingStyle.camel:
newName = (single ? getSingleArr(oldName) : getPluralArr(oldName)).map((v, i) => i === 0 ? v : up1(v)).join('');
break;
case CliConst.NamingStyle.kebab:
newName = single ? getSingleArr(oldName).join('-') : oldName.replace(SEP, '-');
break;
case CliConst.NamingStyle.snake:
newName = single ? getSingleArr(oldName).join('_') : oldName;
break;
case CliConst.NamingStyle.pascal:
newName = (single ? getSingleArr(oldName) : getPluralArr(oldName)).map(v => up1(v)).join('');
break;
case CliConst.NamingStyle.space:
newName = single ? getSingleArr(oldName).join(' ') : oldName.replace(SEP, ' ');
break;
case CliConst.NamingStyle.upper:
newName = single ? getSingleArr(oldName).join('_').toUpperCase() : oldName.toUpperCase();
break;
default:
throw Error(`Unknown name style: ${style}`)
}
node.language['cli']['name'] = newName;
}
getCliName(obj: any) {
if (obj == null || obj.language == null) {
@ -121,7 +49,8 @@ export class CommonNamer {
if (!this.flag.has(obj)) {
this.flag.add(obj);
this.convertNamingConvention(obj);
Helper.applyNamingConvention(this.cliNamingSettings, obj, 'cli');
Helper.applyNamingConvention(this.defaultNamingSettings, obj, 'default');
}
}

Просмотреть файл

@ -109,6 +109,8 @@ export namespace CliCommonSchema {
export interface NamingConvention {
singularize?: NamingType[]
glossary?: string[]
override?: any
parameter?: NamingStyle
operation?: NamingStyle
operationGroup?: NamingStyle

Просмотреть файл

@ -0,0 +1,61 @@
import { assert } from 'chai';
import 'mocha';
import { ActionSet, ActionFormatTable } from '../src/plugins/modifier/cliDirectiveAction';
import { M4Node, CliConst, CliCommonSchema } from '../src/schema';
import { Metadata, OperationGroup, Operation, Parameter, ObjectSchema, Property, ChoiceSchema, ChoiceValue } from "@azure-tools/codemodel";
import { Helper } from '../src/helper';
describe('Test NamingConvention', function () {
it('namingConvention', () => {
let settings: CliCommonSchema.NamingConvention = {
singularize: ['operation', 'operationGroup'],
glossary: ['cats', 'Dogs'],
override: {
autoRest: 'AUTOREST',
AmE: 'AME',
},
parameter: 'camel',
operation: 'pascal',
operationGroup: 'upper',
property: 'kebab',
type: 'snake',
choice: 'space',
choiceValue: 'camel'
};
settings = Helper.normalizeNamingSettings(settings);
let group: OperationGroup = new OperationGroup('cats_food');
Helper.applyNamingConvention(settings, group, 'default');
assert.equal(group.language['default'].name, 'CATS_FOOD');
let op: Operation = new Operation('birds_food', 'desc');
Helper.applyNamingConvention(settings, op, 'default');
assert.equal(op.language['default'].name, 'BirdFood');
let param: Parameter = new Parameter('try_ame_test', 'desc' ,null);
Helper.applyNamingConvention(settings, param, 'default');
assert.equal(param.language['default'].name, 'tryAMETest')
let schema: ObjectSchema = new ObjectSchema('AUTOREST_is_cool', 'desc');
schema.language['cli'] = { name: 'is_autorest_very_cool' };
Helper.applyNamingConvention(settings, schema, 'cli');
assert.equal(schema.language['cli'].name, 'is_AUTOREST_very_cool');
let prop: Property = new Property('hello_world', 'desc', null);
prop.language['cli'] = { name: 'hello_world' };
Helper.applyNamingConvention(settings, prop, 'cli');
assert.equal(prop.language['cli'].name, 'hello-world');
let choice: ChoiceSchema = new ChoiceSchema('name', 'desc');
choice.language['cli'] = { name: 'all_dogs_are_animal' }
Helper.applyNamingConvention(settings, choice, 'cli');
assert.equal(choice.language['cli'].name, 'all dogs are animal');
let value: ChoiceValue = new ChoiceValue('name', 'desc', null);
choice.language['cli'] = { name: 'all_dogs_are_animal' }
Helper.applyNamingConvention(settings, choice, 'cli');
assert.equal(choice.language['cli'].name, 'all dogs are animal');
});
});