1. add test for config
2. support changing naming convention for language->default 3. update doc
This commit is contained in:
Родитель
5c641eebd6
Коммит
929f80f675
131
README.md
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');
|
||||
|
||||
});
|
||||
});
|
Загрузка…
Ссылка в новой задаче