mushroom-cli 1.2.0-alpha.17 → 1.2.0-alpha.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -132,6 +132,14 @@ npx mushroom-cli parameters --no-check-update
132
132
 
133
133
  ## Version
134
134
 
135
+ ### 1.2.0-alpha.19
136
+
137
+ Improvement:
138
+
139
+ - gen-ts:
140
+ - generate type for custom methods of action
141
+ - generate description for fields, entities
142
+
135
143
  ### 1.2.0-alpha.17
136
144
 
137
145
  Improvement:
@@ -38,9 +38,6 @@ function getCSharpTypeFromMushroomType(type) {
38
38
  switch (type) {
39
39
  case 'string':
40
40
  return 'string';
41
- case 'object':
42
- case 'array':
43
- return '';
44
41
  case 'boolean':
45
42
  return 'boolean';
46
43
  case 'integer':
@@ -52,33 +49,73 @@ function getCSharpTypeFromMushroomType(type) {
52
49
  case 'IdRef':
53
50
  case 'AutoId':
54
51
  return 'IdValue';
52
+ default:
53
+ return 'any';
55
54
  }
56
- return 'UNKNOWN_TYPE_' + type;
57
55
  }
58
56
  function generateTypescriptClass(objectDefination, className, options) {
59
57
  let childrenCode = '';
60
58
  let code = `export interface ${className} {\n`;
61
59
  for (const fieldName in objectDefination) {
62
- const optionalMark = options?.checkRequired && objectDefination[fieldName].required
63
- ? ''
64
- : ' | null | undefined';
65
- if (objectDefination[fieldName].type == 'object') {
60
+ const field = objectDefination[fieldName];
61
+ if (!field)
62
+ continue;
63
+ if (field.description) {
64
+ code += ` /**\n * ${field.description.replace(/\r/, '').split('\n').map(line => line.includes('*/') ? line.replace(/\*\//g, "* /") : line).join('\n * ')}\n */\n`;
65
+ }
66
+ const optionalMark = options?.checkRequired && (field.required || field.validation?.required) ? '' : '?';
67
+ const nullType = optionalMark ? ' | null' : '';
68
+ if (field.type == 'object') {
66
69
  const fieldClassName = className + '_' + toTypeScriptClassName(fieldName);
67
- code += ` ${fieldName}: ${fieldClassName}${optionalMark};\n`;
68
- childrenCode += generateTypescriptClass(objectDefination[fieldName].fields ?? {}, fieldClassName);
70
+ code += ` ${fieldName}${optionalMark}: ${fieldClassName}${nullType};\n`;
71
+ childrenCode += generateTypescriptClass(field.fields ?? {}, fieldClassName, options);
69
72
  }
70
- else if (objectDefination[fieldName].type == 'array') {
71
- if (objectDefination[fieldName].items?.type == 'object') {
73
+ else if (field.type == 'keyValue') {
74
+ const valueNullType = options?.checkRequired && (field.values.required || field.values.validation?.required) ? '' : ' | null | undefined';
75
+ if (field.values?.type == 'object') {
72
76
  const fieldClassName = className + '_' + toTypeScriptClassName(fieldName);
73
- code += ` ${fieldName}: ${fieldClassName}[]${optionalMark};\n`;
74
- childrenCode += generateTypescriptClass(objectDefination[fieldName].items?.fields ?? {}, fieldClassName);
77
+ code += ` ${fieldName}${optionalMark}: Record<string, ${fieldClassName}${valueNullType}>${nullType};\n`;
78
+ childrenCode += generateTypescriptClass(field.values.fields ?? {}, fieldClassName, options);
79
+ }
80
+ else if (field.values?.type == 'array') {
81
+ const itemNullType = options?.checkRequired && (field.values.items?.required || field.values.items?.validation?.required) ? '' : ' | null | undefined';
82
+ if (field.values.items?.type == 'object') {
83
+ const fieldClassName = className + '_' + toTypeScriptClassName(fieldName);
84
+ code += ` ${fieldName}${optionalMark}: Record<string, Array<${fieldClassName}${itemNullType}>${valueNullType}>${nullType};\n`;
85
+ childrenCode += generateTypescriptClass(field.values.items.fields ?? {}, fieldClassName, options);
86
+ }
87
+ else {
88
+ code += ` ${fieldName}${optionalMark}: Record<string, Array<${getCSharpTypeFromMushroomType(field.values.items?.type ?? 'string')}${itemNullType}>${valueNullType}>${nullType};\n`;
89
+ }
75
90
  }
76
91
  else {
77
- code += ` ${fieldName}: ${getCSharpTypeFromMushroomType(objectDefination[fieldName].items?.type ?? '')}[]${optionalMark};\n`;
92
+ code += ` ${fieldName}${optionalMark}: Record<string, ${getCSharpTypeFromMushroomType(field.values?.type ?? 'string')}${valueNullType}>${nullType};\n`;
93
+ }
94
+ }
95
+ else if (field.type == 'array') {
96
+ const itemNullType = options?.checkRequired && (field.items.required || field.items.validation?.required) ? '' : ' | null | undefined';
97
+ if (field.items?.type == 'object') {
98
+ const fieldClassName = className + '_' + toTypeScriptClassName(fieldName);
99
+ code += ` ${fieldName}${optionalMark}: Array<${fieldClassName}${itemNullType}>${nullType};\n`;
100
+ childrenCode += generateTypescriptClass(field.items.fields ?? {}, fieldClassName, options);
101
+ }
102
+ else if (field.items?.type == 'keyValue') {
103
+ const valueNullType = options?.checkRequired && (field.items.values?.required || field.items.values?.validation?.required) ? '' : ' | null | undefined';
104
+ if (field.items.values?.type == 'object') {
105
+ const fieldClassName = className + '_' + toTypeScriptClassName(fieldName);
106
+ code += ` ${fieldName}${optionalMark}: Array<Record<string, ${fieldClassName}${valueNullType}>${itemNullType}>${nullType};\n`;
107
+ childrenCode += generateTypescriptClass(field.items.values.fields ?? {}, fieldClassName, options);
108
+ }
109
+ else {
110
+ code += ` ${fieldName}${optionalMark}: Array<Record<string, ${getCSharpTypeFromMushroomType(field.items.type ?? 'string')}${valueNullType}>${itemNullType}>${nullType};\n`;
111
+ }
112
+ }
113
+ else {
114
+ code += ` ${fieldName}${optionalMark}: Array<${getCSharpTypeFromMushroomType(field.items?.type ?? 'string')}${itemNullType}>${nullType};\n`;
78
115
  }
79
116
  }
80
117
  else {
81
- code += ` ${fieldName}: ${getCSharpTypeFromMushroomType(objectDefination[fieldName].type)}${optionalMark};\n`;
118
+ code += ` ${fieldName}${optionalMark}: ${getCSharpTypeFromMushroomType(field.type ?? 'string')}${nullType};\n`;
82
119
  }
83
120
  }
84
121
  code += '}\n\n';
@@ -88,12 +125,75 @@ function generateTypescriptClassFromViewParameters(parameters) {
88
125
  if (Array.isArray(parameters)) {
89
126
  return ('{\n' +
90
127
  parameters
91
- .map((p) => ` ${p.field}: ${getCSharpTypeFromMushroomType(p.type || 'string')}${p.isArray ? '[]' : ''}${p.validation?.required && p.defaultValue === undefined ? '' : ' | null | undefined'};`)
128
+ .map((p) => ` ${p.field}${p.validation?.required && p.defaultValue === undefined ? '' : '?'}: ${getCSharpTypeFromMushroomType(p.type || 'string')}${p.isArray ? '[]' : ''}${p.validation?.required && p.defaultValue === undefined ? '' : ' | null'};`)
92
129
  .join('\n') +
93
130
  '\n}');
94
131
  }
95
132
  return '{}';
96
133
  }
134
+ function generateTopCustomClass(config, entityName, methodName, grantRole, postfix, code) {
135
+ const isKeyValue = config.type == 'keyValue';
136
+ const isArray = isKeyValue ? config.values?.type == 'array' : config.type == 'array';
137
+ const fields = isKeyValue ? (isArray ? config.values?.items?.fields : config.values?.fields) : (isArray ? config.items?.fields : config.fields);
138
+ const type = (isKeyValue ? (isArray ? config.values?.items?.type : config.values?.type) : (isArray ? config.items?.type : config.type)) ?? 'string';
139
+ let inputClassName;
140
+ if (type == 'object') {
141
+ if (fields) {
142
+ const roleIndicator = grantRole ? '_' + toTypeScriptClassName(grantRole) + '_' : '';
143
+ inputClassName = `${toTypeScriptClassName(entityName)}_${toTypeScriptClassName(methodName)}${roleIndicator}${postfix}`;
144
+ code.classesCode += generateTypescriptClass(fields, inputClassName, {
145
+ checkRequired: true
146
+ });
147
+ }
148
+ else {
149
+ inputClassName = 'any';
150
+ }
151
+ }
152
+ else
153
+ inputClassName = getCSharpTypeFromMushroomType(type);
154
+ return isKeyValue ? (isArray ? 'Record<string, ' + inputClassName + '[]>' : 'Record<string, ' + inputClassName + '>') : (isArray ? inputClassName + '[]' : inputClassName);
155
+ }
156
+ function generateCustomMethodCode(entityName, actionName, actions) {
157
+ const result = { classesCode: '', methodCode: '' };
158
+ if (!actionName || !actionName.startsWith('_raw_http_method_') || !actions || !actions.length)
159
+ return result;
160
+ actions = actions.filter(action => action.enabled);
161
+ if (!actions.length)
162
+ return result;
163
+ const methodName = actionName.substring('_raw_http_method_'.length);
164
+ let inputTypes = new Set();
165
+ let outputTypes = new Set();
166
+ actions.forEach(action => {
167
+ const codeHintDelegator = action?.codeHint?.delegator;
168
+ if (!codeHintDelegator)
169
+ return;
170
+ if (codeHintDelegator.result && !codeHintDelegator.isBinaryResponse) {
171
+ const className = generateTopCustomClass(codeHintDelegator.result, entityName, methodName, actions.length > 1 ? action.grant : '', 'Result', result);
172
+ outputTypes.add(className);
173
+ }
174
+ else {
175
+ outputTypes.add('any');
176
+ }
177
+ if (codeHintDelegator.param && codeHintDelegator.requestBody) {
178
+ let paramClassName = generateTopCustomClass(codeHintDelegator.param, entityName, methodName, actions.length > 1 ? action.grant : '', 'ParamRequest', result);
179
+ const bodyClassName = generateTopCustomClass(codeHintDelegator.requestBody, entityName, methodName, actions.length > 1 ? action.grant : '', 'Request', result);
180
+ inputTypes.add(`CustomMethodRequest<${paramClassName}, ${bodyClassName}>`);
181
+ }
182
+ else if (codeHintDelegator.param) {
183
+ const className = generateTopCustomClass(codeHintDelegator.param, entityName, methodName, actions.length > 1 ? action.grant : '', 'ParamRequest', result);
184
+ inputTypes.add(`CustomMethodParamsRequest<${className}>`);
185
+ }
186
+ else if (codeHintDelegator.requestBody) {
187
+ const className = generateTopCustomClass(codeHintDelegator.requestBody, entityName, methodName, actions.length > 1 ? action.grant : '', 'Request', result);
188
+ inputTypes.add(`CustomMethodBodyRequest<${className}>`);
189
+ }
190
+ else {
191
+ inputTypes.add('MushroomRequest');
192
+ }
193
+ });
194
+ result.methodCode = ` ${methodName}Async: (request: ${inputTypes.size ? [...inputTypes].join(' | ') : 'MushroomRequest'}) => Promise<MushroomResponse<${outputTypes.size ? [...outputTypes].join(' | ') : 'any'}>>;\n`;
195
+ return result;
196
+ }
97
197
  export function genTypescriptDriverString(aliasProject, source) {
98
198
  if (!aliasProject) {
99
199
  return '';
@@ -127,6 +227,19 @@ export const extensions = {
127
227
  export type ISODateValue = string | Date;
128
228
  export type IdValue = ${source.idType == 'AutoIncInt' || source.idType == 'AutoIncLong' ? 'number' : 'string'};
129
229
 
230
+ export interface CustomMethodRequest<TParam extends {}, TBody> extends MushroomRequest {
231
+ params?: TParam;
232
+ body?: TBody;
233
+ }
234
+
235
+ export interface CustomMethodParamsRequest<TParam extends {}> extends MushroomRequest {
236
+ params?: TParam;
237
+ }
238
+
239
+ export interface CustomMethodBodyRequest<TBody> extends MushroomRequest {
240
+ body?: TBody;
241
+ }
242
+
130
243
  `;
131
244
  let iApiClassBody = '';
132
245
  for (const entityName in entities) {
@@ -134,6 +247,9 @@ export type IdValue = ${source.idType == 'AutoIncInt' || source.idType == 'AutoI
134
247
  const typescriptClassName = toTypeScriptClassName(entityName);
135
248
  code += generateTypescriptClass(entity.fields, typescriptClassName);
136
249
  const actions = {};
250
+ if (entity.description) {
251
+ iApiClassBody += ` /**\n * ${entity.description.replace(/\r/, '').split('\n').map(line => line.includes('*/') ? line.replace(/\*\//g, "* /") : line).join('\n * ')}\n */\n`;
252
+ }
137
253
  iApiClassBody += ` ${entityName}: MushroomResourceBase`;
138
254
  let customMethodCode = '';
139
255
  for (const actionName in entity.actions) {
@@ -195,7 +311,9 @@ export type IdValue = ${source.idType == 'AutoIncInt' || source.idType == 'AutoI
195
311
  }
196
312
  default: {
197
313
  if (actionName.indexOf('_raw_http_method_') === 0) {
198
- customMethodCode += ` ${actionName.substring('_raw_http_method_'.length)}Async: (rawRequest: MushroomRequest) => Promise<MushroomResponse<any>>;\n`;
314
+ const customCode = generateCustomMethodCode(entityName, actionName, entity.actions[actionName]);
315
+ customMethodCode += customCode.methodCode;
316
+ code += customCode.classesCode;
199
317
  break;
200
318
  }
201
319
  else {
@@ -206,9 +324,7 @@ export type IdValue = ${source.idType == 'AutoIncInt' || source.idType == 'AutoI
206
324
  }
207
325
  }
208
326
  if (customMethodCode) {
209
- code += `export interface ${typescriptClassName}$CustomMethodResource {
210
- ${customMethodCode}}
211
- `;
327
+ code += `export interface ${typescriptClassName}$CustomMethodResource {\n${customMethodCode}}\n`;
212
328
  iApiClassBody += ` & ${typescriptClassName}$CustomMethodResource`;
213
329
  }
214
330
  const views = {};
@@ -291,6 +407,19 @@ export declare const extensions = {
291
407
  export declare type ISODateValue = string | Date;
292
408
  export declare type IdValue = ${source.idType == 'AutoIncInt' || source.idType == 'AutoIncLong' ? 'number' : 'string'};
293
409
 
410
+ export interface CustomMethodRequest<TParam extends {}, TBody> extends MushroomRequest {
411
+ params?: TParam;
412
+ body?: TBody;
413
+ }
414
+
415
+ export interface CustomMethodParamsRequest<TParam extends {}> extends MushroomRequest {
416
+ params?: TParam;
417
+ }
418
+
419
+ export interface CustomMethodBodyRequest<TBody> extends MushroomRequest {
420
+ body?: TBody;
421
+ }
422
+
294
423
  `;
295
424
  let iApiClassBody = '';
296
425
  for (const entityName in entities) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mushroom-cli",
3
- "version": "1.2.0-alpha.17",
3
+ "version": "1.2.0-alpha.19",
4
4
  "description": "Mushroom CLI",
5
5
  "main": "dist/mr.js",
6
6
  "type": "module",