mushroom-cli 1.2.0-alpha.22 → 1.2.0-alpha.24

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,18 @@ npx mushroom-cli parameters --no-check-update
132
132
 
133
133
  ## Version
134
134
 
135
+ ### 1.2.0-alpha.24
136
+
137
+ Improvement:
138
+
139
+ - gen-docs
140
+ - Add `JavascriptParamModel`, `JavascriptRequestBodyModel`, `JavascriptResultModel` tab in code snippets of custom methods
141
+ - Correct generate query string and request body at HTTP and cURL tabs in code snippets of custom methods
142
+ - Correct generate param and body at Javascript tab in code snippets of custom methods
143
+ - Correct generate JavascriptModel in code snippets
144
+ - Add docs for `inline` mode of download file and zip file(s), `allowSmallerSize` for thumb
145
+ - Support link for Roles and Code Snippets
146
+
135
147
  ### 1.2.0-alpha.22
136
148
 
137
149
  Improvement:
@@ -2101,6 +2101,7 @@ API.fileExtension().uploadAsync(file, uploadResult -> {
2101
2101
  <h4>Query string</h4>
2102
2102
  <ul>
2103
2103
  <li>id: string, required - Mã của file cần download</li>
2104
+ <li>inline: boolean, optional - Cho biết có download ở chế độ inline hay không. Xem thêm tại <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition" target="_blank">Content-Disposition</a></li>
2104
2105
  </ul>
2105
2106
  </div>
2106
2107
  <div>
@@ -2128,6 +2129,11 @@ Content-Range: byte {realStartIndex}-{realEndIndex}/{totalLength}
2128
2129
  </div>
2129
2130
  <div class="js-driver-snippet">
2130
2131
  <h4>Mẫu javascript</h4>
2132
+ Lấy về link download file
2133
+ <pre>mushroom.$file.getDownloadLink("your file id");</pre>
2134
+ Tùy biến lấy về link download inline mode
2135
+ <pre>mushroom.$file.linkBuilder.download.id("your file id").inline().build();</pre>
2136
+ Download file trên trình duyệt
2131
2137
  <pre>mushroom.$file.download("your file id");</pre>
2132
2138
  </div>
2133
2139
  </div>
@@ -2209,6 +2215,13 @@ Content-Range: byte {realStartIndex}-{realEndIndex}/{totalLength}
2209
2215
  <li>rotate270 - xoay ảnh 270 độ</li>
2210
2216
  </ul>
2211
2217
  </li>
2218
+ <li>
2219
+ o: string, optional, chỉ định các tùy chọn, nhận một trong các giá trị sau, có thể áp dụng nhiều tùy chọn, phân cách nhau bởi dấu phải
2220
+ <ul>
2221
+ <li>allow-smaller-size - cho phép sinh ảnh bé hơn kích thước quy định nếu ảnh gốc bé</li>
2222
+ <li>disable-cache - không sử dụng cache server mà yêu cầu sinh ảnh thumb mới</li>
2223
+ </ul>
2224
+ </li>
2212
2225
  </ul>
2213
2226
  </div>
2214
2227
  <div>
@@ -2233,23 +2246,28 @@ Last-Modified: {datetime}</pre>
2233
2246
  <div class="js-driver-snippet">
2234
2247
  <pre data-language="javascript">const imgSrc = mushroom.$file.linkBuilder.thumb
2235
2248
  .id(fileId) <span class="code-comment">// required
2236
- // .stretch(width, height)
2237
- // .square(size)
2238
- // .zoom(width, height, backgroundColor) // backgroundColor: optional, default: transparent, valid values: transparent | [0-9a-fA-F]{6}
2239
- // .x(width)
2240
- // .y(height)
2241
- // .clip(150, 75, "top", "left")
2242
- // // .clip(width, height, verticalAlign, horizontalAlign)
2243
- // // verticalAlign: optional, default: middle, valid values: top | middle | bottom
2244
- // // horizontalAlign: optional, default: center, valid values: left | center | right
2249
+ // .stretch(width, height) // width: gt 0; height: gt 0
2250
+ // .square(size) // size: gt 0
2251
+ // .zoom(width, height, backgroundColor)
2252
+ // width: gt: 0
2253
+ // height: gt 0
2254
+ // backgroundColor: optional, default: transparent, valid values: transparent | [0-9a-fA-F]{6}
2255
+ // .x(width) // width: gt 0
2256
+ // .y(height) // height: gt 0
2257
+ // .clip(width, height, verticalAlign, horizontalAlign)
2258
+ // width: gt 0
2259
+ // height: gt 0
2260
+ // verticalAlign: optional, default: middle, valid values: top | middle | bottom
2261
+ // horizontalAlign: optional, default: center, valid values: left | center | right
2245
2262
  // .rotate90()
2246
2263
  // .rotate180()
2247
2264
  // .rotate270()
2248
2265
  // .flipVertical()
2249
2266
  // .flipHorizontal()
2250
- // .transparent(50) // value: from 0 to 255
2267
+ // .transparent(value) // value: from 0 to 255
2251
2268
  // .grayscale()
2252
- // .format("png") // outputFormat: source | jpg | png | gif | webp | auto</span>
2269
+ // .allowSmallerSize()
2270
+ // .format(outputFormat) // outputFormat: source | jpg | png | gif | webp | auto</span>
2253
2271
  .build();</pre>
2254
2272
  </div>
2255
2273
  </div>
@@ -2330,6 +2348,7 @@ Tạo hiệu ứng đen trắng: &lt;img src="<span class="root-url">{{rootUrl}}
2330
2348
  <ul>
2331
2349
  <li>id: string, required, danh sách mã các file cần zip, các mã file được phân cách bởi dấu phảy (,)</li>
2332
2350
  <li>name: string, optional, tên file zip khi download. Mặc định là "No name.zip"</li>
2351
+ <li>inline: boolean, optional - Cho biết có download ở chế độ inline hay không. Xem thêm tại <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition" target="_blank">Content-Disposition</a></li>
2333
2352
  </ul>
2334
2353
  </div>
2335
2354
  <div>
@@ -2343,7 +2362,14 @@ Content-Disposition: attachment; filename="{name}"</pre>
2343
2362
  </div>
2344
2363
  <div class="js-driver-snippet">
2345
2364
  <h4>Mẫu javascript</h4>
2346
- <pre>mushroom.$file.zip(["your file id 1", "your file id 2"]);</pre>
2365
+ Lấy về link download zip file
2366
+ <pre>mushroom.$file.getZipLink("your file id 1", "your file id 2");</pre>
2367
+ Tùy biến lấy về link download zip file với chế độ inline
2368
+ <pre>mushroom.$file.linkBuilder.zip.add("your file id 1", "your file id 2").inline().build();</pre>
2369
+ Tùy biến lấy về link download zip file chỉ định tên file
2370
+ <pre>mushroom.$file.linkBuilder.zip.add("your file id 1", "your file id 2").attachment("file-name.zip").build();</pre>
2371
+ Download zip file trên trình duyệt
2372
+ <pre>mushroom.$file.zip("your file id 1", "your file id 2");</pre>
2347
2373
  </div>
2348
2374
  </div>
2349
2375
  </div>
@@ -2689,7 +2715,7 @@ catch (error) {
2689
2715
  </div>
2690
2716
  </div>
2691
2717
  <div class="mushroom-docs-template" id="template-project-entity-action-role">
2692
- <li role="presentation"><a onclick="renderEntityActionRolePage.call(this, '{{entityName}}', '{{actionName}}', '{{grant}}'); activeRoleTab.call(this)" data-role="{{grant}}">{{explainedRole}}</a></li>
2718
+ <li role="presentation"><a onclick="renderEntityActionRolePage.call(this, '{{entityName}}', '{{actionName}}', '{{grant}}'); activeRoleTab.call(this)" data-hash="{{entityName}}.{{actionName}}/{{grant}}" data-role="{{grant}}">{{explainedRole}}</a></li>
2693
2719
  </div>
2694
2720
  <div class="mushroom-docs-template" id="template-project-entity-action-page">
2695
2721
  <div class="tab-page" data-role="{{grant}}"></div>
@@ -4389,12 +4415,8 @@ API.get{{EntityName}}().deleteAsync(arg, count -> {
4389
4415
  Code tham khảo:
4390
4416
  <pre data-language="javascript">try {
4391
4417
  const response = await mushroom.{{entityName}}.{{httpMethod}}Async({<span style="display: {{paramDisplay}}">
4392
- params: {
4393
- <span class="code-comment">// parameters here</span>
4394
- },</span><span style="display: {{bodyDisplay}}">
4395
- body: {
4396
- <span class="code-comment">// request body here</span>
4397
- }</span>
4418
+ params: {{paramModel:html}},</span><span style="display: {{bodyDisplay}}">
4419
+ body: {{bodyModel:html}}</span>
4398
4420
  });
4399
4421
  console.log("Kết quả trả về, response: %o", response);
4400
4422
  } catch (error) {
@@ -4402,6 +4424,16 @@ API.get{{EntityName}}().deleteAsync(arg, count -> {
4402
4424
  }</pre>
4403
4425
  </div>
4404
4426
  </script>
4427
+ <script type="text/mushroom-docs-template" class="mushroom-docs-template" id="template-codeSnippets-generalAction-JavascriptModel">
4428
+ <div class="js-model-snippet">
4429
+ Javascript UNDEFINED model
4430
+ <pre data-language="json">{{undefinedModel:html}}</pre>
4431
+ Javascript VALUE model
4432
+ <pre data-language="json">{{valueModel:html}}</pre>
4433
+ Javascript NULL model
4434
+ <pre data-language="json">{{nullModel:html}}</pre>
4435
+ </div>
4436
+ </script>
4405
4437
  <div class="mushroom-docs-template" id="template-codeSnippets-generalAction-CSharp">
4406
4438
  <div class="csharp-driver-snippet">
4407
4439
  <p>Hỗ trợ: DotNet Framework 4.5+, DotNet Core 3.1+, Unity3D, Mono</p>
@@ -4424,31 +4456,30 @@ catch (Exception ex)
4424
4456
  <script type="text/mushroom-docs-template" class="mushroom-docs-template" id="template-codeSnippets-generalAction-HTTP">
4425
4457
  <div class="http-snippet">
4426
4458
  Code tham khảo:
4427
- <pre data-language="http">POST {{rootUrlPath}}{{entityName}}s/{{httpMethod}}<span class="place-holder">?param1=value1&amp;param2=value2</span> HTTP/1.1
4459
+ <pre data-language="http">POST {{rootUrlPath}}{{entityName}}s/{{httpMethod}}{{queryString}} HTTP/1.1
4428
4460
  Host: <span class="domain">{{domain}}</span>
4429
4461
  Content-Type: application/json
4430
4462
  X-HTTP-Method-Override: {{httpMethod}}<span style="display: {{tokenDisplay}}">
4431
4463
  Token: your token</span>
4432
4464
 
4433
- <span class="place-holder">BODY DATA IF ANY</span></pre>
4465
+ {{bodyModel:html}}</pre>
4434
4466
  </div>
4435
4467
  </script>
4436
4468
  <script type="text/mushroom-docs-template" class="mushroom-docs-template" id="template-codeSnippets-generalAction-cURL">
4437
4469
  <div class="curl-snippet">
4438
4470
  cURL
4439
4471
  <pre data-language="cURL">curl -X POST \
4440
- '<span class="root-url">{{rootUrl}}</span>{{entityName}}s/{{httpMethod}}<span class="place-holder">?param1=value1&amp;param2=value2</span>' \
4472
+ '<span class="root-url">{{rootUrl}}</span>{{entityName}}s/{{httpMethod}}{{queryString}}' \
4441
4473
  -H 'Content-Type: application/json' \
4442
4474
  -H 'X-HTTP-Method-Override: {{httpMethod}}' \<span style="display: {{tokenDisplay}}">
4443
4475
  -H 'Token: {<span>{</span>TOKEN}}' \</span>
4444
- -d 'BODY DATA IF ANY'
4445
- </pre>
4476
+ -d '{{bodyModel:html}}'</pre>
4446
4477
  Shell
4447
4478
  <pre data-language="cURL">curl --request POST \
4448
- --url '<span class="root-url">{{rootUrl}}</span>{{entityName}}s/{{httpMethod}}<span class="place-holder">?param1=value1&amp;param2=value2</span>' \
4479
+ --url '<span class="root-url">{{rootUrl}}</span>{{entityName}}s/{{httpMethod}}{{queryString}}' \
4449
4480
  --header 'X-HTTP-Method-Override: {{httpMethod}}' \<span style="display: {{tokenDisplay}}">
4450
4481
  --header 'Token: {<span>{</span>TOKEN}}' \</span>
4451
- --data 'BODY DATA IF ANY'</pre>
4482
+ --data '{{bodyModel:html}}'</pre>
4452
4483
  </div>
4453
4484
  </script>
4454
4485
  <div class="mushroom-docs-template" id="template-codeSnippets-generalAction-Java">
@@ -4989,7 +5020,7 @@ API.get{{EntityName}}().getViews().{{viewName}}Async(arg, result -> {
4989
5020
  </div>
4990
5021
  <div class="query-string">
4991
5022
  <h4>Query string</h4>
4992
- <pre class="query-string-model"></pre>
5023
+ <ul class="parameters-list"></ul>
4993
5024
  </div>
4994
5025
  <div class="request-body">
4995
5026
  <h4>Request body</h4>
@@ -5025,7 +5056,7 @@ API.get{{EntityName}}().getViews().{{viewName}}Async(arg, result -> {
5025
5056
  </div>
5026
5057
  <div class="mushroom-docs-template" id="template-view-parameters">
5027
5058
  <li>
5028
- <b>{{field}}</b>: {{type}}, {{required}}{{defaultValue}} - {{description}}
5059
+ <b>{{field}}</b>: {{type}}, {{required}}{{defaultValue}}, {{description}}
5029
5060
  </li>
5030
5061
  </div>
5031
5062
  <div class="mushroom-docs-template" id="template-view">
@@ -270,7 +270,7 @@ function formatModel(obj, strIndent, options) {
270
270
  ' ' +
271
271
  comment +
272
272
  `\r\n${nextIndent}`;
273
- result += `]`;
273
+ result += '],';
274
274
  } else if (child.type === 'datetime')
275
275
  result +=
276
276
  `\r\n${nextIndent}"${m}": ` + JSON.stringify(child.defaultValue || 'ISODate_ISO_8601') + ', ' + comment;
@@ -281,7 +281,7 @@ function formatModel(obj, strIndent, options) {
281
281
  else result += `\r\n${nextIndent}"${m}": ` + JSON.stringify(child.defaultValue || child.type) + ', ' + comment;
282
282
  } else if (options.mode === 'model_null') {
283
283
  if (child.type === 'object')
284
- result += '\r\n' + nextIndent + '"' + m + '": ' + comment + formatModel(child.fields, nextIndent, options);
284
+ result += '\r\n' + nextIndent + '"' + m + '": ' + comment + formatModel(child.fields, nextIndent, options) + ',';
285
285
  else if (child.type === 'array') {
286
286
  result += `\r\n${nextIndent}"${m}": [`;
287
287
  if (child.items.type === 'object')
@@ -290,7 +290,7 @@ function formatModel(obj, strIndent, options) {
290
290
  } else result += `\r\n${nextIndent}"${m}": null, ${comment}`;
291
291
  } else if (options.mode === 'model_undefined') {
292
292
  if (child.type === 'object')
293
- result += '\r\n' + nextIndent + '"' + m + '": ' + comment + formatModel(child.fields, nextIndent, options);
293
+ result += '\r\n' + nextIndent + '"' + m + '": ' + comment + formatModel(child.fields, nextIndent, options) + ',';
294
294
  else if (child.type === 'array') {
295
295
  result += `\r\n${nextIndent}"${m}": [`;
296
296
  if (child.items.type === 'object')
@@ -387,7 +387,7 @@ function formatHintModel(obj, strIndent, options) {
387
387
  validation += ', enum(' + encodeHtml(child.items.validValues.join(', ')) + ')';
388
388
  }
389
389
 
390
- var comment = `<span class="code-comment">// ${validation} ${typeof child.description === 'string' && child.description !== '' ? '- ' + encodeHtml(child.description).replace(/\n/g, ' ') : ''}</span>`;
390
+ var comment = options.hideComment ? '' : `<span class="code-comment">// ${validation} ${typeof child.description === 'string' && child.description !== '' ? '- ' + encodeHtml(child.description).replace(/\n/g, ' ') : ''}</span>`;
391
391
 
392
392
  if (options.mode === 'model') {
393
393
  if (child.type === 'object')
@@ -415,7 +415,7 @@ function formatHintModel(obj, strIndent, options) {
415
415
  ' ' +
416
416
  comment +
417
417
  `\r\n${nextIndent}`;
418
- result += `]`;
418
+ result += '],';
419
419
  } else if (child.type === 'datetime')
420
420
  result +=
421
421
  `\r\n${nextIndent}"${m}": ` + JSON.stringify(child.defaultValue || 'ISODate_ISO_8601') + ', ' + comment;
@@ -426,7 +426,7 @@ function formatHintModel(obj, strIndent, options) {
426
426
  else result += `\r\n${nextIndent}"${m}": ` + JSON.stringify(child.defaultValue || child.type) + ', ' + comment;
427
427
  } else if (options.mode === 'model_null') {
428
428
  if (child.type === 'object')
429
- result += '\r\n' + nextIndent + '"' + m + '": ' + comment + formatHintModel(child.fields, nextIndent, options);
429
+ result += '\r\n' + nextIndent + '"' + m + '": ' + comment + formatHintModel(child.fields, nextIndent, options) + ',';
430
430
  else if (child.type === 'array') {
431
431
  result += `\r\n${nextIndent}"${m}": [`;
432
432
  if (child.items.type === 'object')
@@ -435,7 +435,7 @@ function formatHintModel(obj, strIndent, options) {
435
435
  } else result += `\r\n${nextIndent}"${m}": null, ${comment}`;
436
436
  } else if (options.mode === 'model_undefined') {
437
437
  if (child.type === 'object')
438
- result += '\r\n' + nextIndent + '"' + m + '": ' + comment + formatHintModel(child.fields, nextIndent, options);
438
+ result += '\r\n' + nextIndent + '"' + m + '": ' + comment + formatHintModel(child.fields, nextIndent, options) + ",";
439
439
  else if (child.type === 'array') {
440
440
  result += `\r\n${nextIndent}"${m}": [`;
441
441
  if (child.items.type === 'object')
@@ -745,20 +745,28 @@ function renderEntityActionRolePage(entityName, actionName, grant) {
745
745
  domain: getApiDomain(localStorage.selected_root_url),
746
746
  rootUrlPath: getApiPath(localStorage.selected_root_url),
747
747
  };
748
- const snippet = window.codeSnippets[codeSnippetName] ?? {};
748
+ const snippets = window.codeSnippets[codeSnippetName]
749
+ ? Object.keys(window.codeSnippets[codeSnippetName]).map((lang) => {
750
+ return {
751
+ lang: lang,
752
+ html: window.codeSnippets[codeSnippetName][lang]?.call(null, args)
753
+ }
754
+ }).filter(item => item.html)
755
+ : [];
756
+
749
757
  const data = {
750
758
  explainedDenyRoles: explainRoles(action.deny),
751
759
  entityName,
752
- codeSnippetTabHeaders: Object.keys(snippet)
760
+ codeSnippetTabHeaders: snippets
753
761
  .map(
754
- (lang, idx) =>
755
- `<li role="presentation" ${idx ? '' : 'class="active"'}><a data-role="${lang}" onclick="activeRoleTab.call(this)">${lang}</a></li>`,
762
+ (item, idx) =>
763
+ `<li role="presentation" ${idx ? '' : 'class="active"'}><a data-role="${item.lang}" onclick="activeRoleTab.call(this)" data-hash="${entityName}.${actionName}/${grant}/${item.lang}">${item.lang}</a></li>`,
756
764
  )
757
765
  .join(''),
758
- codeSnippetTabPages: Object.keys(snippet)
766
+ codeSnippetTabPages: snippets
759
767
  .map(
760
- (lang, idx) =>
761
- `<div class="tab-page ${lang} ${idx ? '' : 'active'}" data-role="${lang}">${snippet[lang]?.call(null, args) ?? ''}</div>`,
768
+ (item, idx) =>
769
+ `<div class="tab-page ${item.lang} ${idx ? '' : 'active'}" data-role="${item.lang}">${item.html}</div>`,
762
770
  )
763
771
  .join(''),
764
772
  EntityName: args.EntityName,
@@ -1005,9 +1013,16 @@ function renderDeleteMany(args) {}
1005
1013
 
1006
1014
  function renderGeneralAction(args) {
1007
1015
  if (args.action?.codeHint?.delegator?.param?.fields) {
1008
- args.tabPage.querySelector('.query-string-model').innerHTML = formatHintModel(
1009
- args.action.codeHint.delegator.param.fields,
1010
- ).trim();
1016
+ args.tabPage.querySelector('.parameters-list').innerHTML = Object.keys(args.action.codeHint.delegator.param.fields)
1017
+ .map((m) => {
1018
+ const field = args.action.codeHint.delegator.param.fields[m];
1019
+ return renderTemplate('template-view-parameters', {
1020
+ field: m,
1021
+ type: (field.type === 'array' ? 'array of ' : '') + (field.items?.type ?? 'string'),
1022
+ description: field.description ?? '',
1023
+ required: field.required ? 'required' : 'optional',
1024
+ })
1025
+ }).join('');
1011
1026
  } else {
1012
1027
  args.tabPage.querySelector('.query-string')?.remove();
1013
1028
  }
@@ -1022,7 +1037,6 @@ function renderGeneralAction(args) {
1022
1037
  } else {
1023
1038
  args.tabPage.querySelector('.request-body')?.remove();
1024
1039
  }
1025
- console.log('args.action.codeHint.delegator', args.action.codeHint.delegator);
1026
1040
  if (args.action?.codeHint?.delegator?.result?.fields) {
1027
1041
  args.tabPage.querySelector('.response-body-model').innerHTML = formatHintModel(
1028
1042
  args.action.codeHint.delegator.result.fields,
@@ -1036,6 +1050,17 @@ function renderGeneralAction(args) {
1036
1050
  }
1037
1051
  }
1038
1052
 
1053
+ function genQueryString(param) {
1054
+ if (!param) return '';
1055
+ if (!param.fields) return '';
1056
+
1057
+ const queryString = Object.keys(param.fields).map(key => {
1058
+ const type = param.fields[key].type === 'array' ? 'array_of_' + (param.fields[key].items?.type ?? 'value') : param.fields[key].type;
1059
+ return `${key}=${type}`;
1060
+ }).join("&");
1061
+ return queryString ? '?' + queryString : '';
1062
+ }
1063
+
1039
1064
  function renderEntityView(entityName, viewName) {
1040
1065
  if (this.dataset.rendered) return;
1041
1066
  this.dataset.rendered = true;
@@ -1597,7 +1622,33 @@ window.codeSnippets = {
1597
1622
  httpMethod: getHttpMethod(args.actionName),
1598
1623
  paramDisplay: args.action?.codeHint?.delegator?.param?.fields ? 'inline' : 'none',
1599
1624
  bodyDisplay: args.action?.codeHint?.delegator?.requestBody?.fields ? 'inline' : 'none',
1600
- }),
1625
+ paramModel: formatHintModel(args.action.codeHint?.delegator?.param?.fields, ' ', { mode: 'model' }).trim(),
1626
+ bodyModel: formatHintModel(args.action.codeHint?.delegator?.requestBody?.fields, ' ', { mode: 'model' }).trim(),
1627
+ }),
1628
+ JavascriptParamModel: (args) =>
1629
+ args.action?.codeHint?.delegator?.param?.fields
1630
+ ? renderTemplate('template-codeSnippets-generalAction-JavascriptModel', {
1631
+ undefinedModel: formatHintModel(args.action.codeHint.delegator.param.fields, undefined, { mode: 'model_undefined' }),
1632
+ valueModel: formatHintModel(args.action.codeHint.delegator.param.fields, undefined, { mode: 'model' }),
1633
+ nullModel: formatHintModel(args.action.codeHint.delegator.param.fields, undefined, { mode: 'model_null' }),
1634
+ })
1635
+ : '',
1636
+ JavascriptRequestBodyModel: (args) =>
1637
+ args.action?.codeHint?.delegator?.requestBody?.fields
1638
+ ? renderTemplate('template-codeSnippets-generalAction-JavascriptModel', {
1639
+ undefinedModel: formatHintModel(args.action.codeHint.delegator.requestBody.fields, undefined, { mode: 'model_undefined' }),
1640
+ valueModel: formatHintModel(args.action.codeHint.delegator.requestBody.fields, undefined, { mode: 'model' }),
1641
+ nullModel: formatHintModel(args.action.codeHint.delegator.requestBody.fields, undefined, { mode: 'model_null' }),
1642
+ })
1643
+ : '',
1644
+ JavascriptResultModel: (args) =>
1645
+ args.action?.codeHint?.delegator?.result?.fields
1646
+ ? renderTemplate('template-codeSnippets-generalAction-JavascriptModel', {
1647
+ undefinedModel: formatHintModel(args.action.codeHint.delegator.result.fields, undefined, { mode: 'model_undefined' }),
1648
+ valueModel: formatHintModel(args.action.codeHint.delegator.result.fields, undefined, { mode: 'model' }),
1649
+ nullModel: formatHintModel(args.action.codeHint.delegator.result.fields, undefined, { mode: 'model_null' }),
1650
+ })
1651
+ : '',
1601
1652
  CSharp: (args) =>
1602
1653
  renderTemplate('template-codeSnippets-generalAction-CSharp', {
1603
1654
  entityName: args.entityName,
@@ -1609,14 +1660,18 @@ window.codeSnippets = {
1609
1660
  domain: args.domain,
1610
1661
  rootUrlPath: args.rootUrlPath,
1611
1662
  entityName: args.entityName,
1663
+ queryString: genQueryString(args.action.codeHint?.delegator?.param),
1612
1664
  httpMethod: getHttpMethod(args.actionName),
1665
+ bodyModel: formatHintModel(args.action.codeHint?.delegator?.requestBody?.fields, undefined, { mode: 'model', hideComment: true }),
1613
1666
  tokenDisplay: args.action.grant !== '*' ? 'inline' : 'none',
1614
1667
  }),
1615
1668
  cURL: (args) =>
1616
1669
  renderTemplate('template-codeSnippets-generalAction-cURL', {
1617
1670
  rootUrl: localStorage.selected_root_url,
1618
1671
  entityName: args.entityName,
1672
+ queryString: genQueryString(args.action.codeHint?.delegator?.param),
1619
1673
  httpMethod: getHttpMethod(args.actionName),
1674
+ bodyModel: formatHintModel(args.action.codeHint?.delegator?.requestBody?.fields, undefined, { mode: 'model', hideComment: true }),
1620
1675
  tokenDisplay: args.action.grant !== '*' ? 'inline' : 'none',
1621
1676
  }),
1622
1677
  Java: (args) =>
@@ -1767,6 +1822,10 @@ function activeRoleTab() {
1767
1822
  return $(this).data('role') === role;
1768
1823
  })
1769
1824
  .addClass('active');
1825
+ var hash = $(this).data('hash');
1826
+ if (hash) {
1827
+ location.hash = hash;
1828
+ }
1770
1829
  }
1771
1830
 
1772
1831
  function activeFirstChild() {
@@ -1781,7 +1840,7 @@ function activeFirstChild() {
1781
1840
  function gotoHash() {
1782
1841
  if (!location.hash) return;
1783
1842
 
1784
- const m = location.hash.match(/^#(\$[\w-]+|\w+)(\.((views\.)?\w+))?$/);
1843
+ const m = location.hash.match(/^#(\$[\w-]+|\w+)(\.((views\.)?\w+)(\/[\/\w\+\|]+)?)?$/);
1785
1844
  if (!m) return;
1786
1845
 
1787
1846
  const cardSelector = m[1].charAt(0) === '$' ? '#extension-' + m[1].substring(1) : '#entity-' + m[1];
@@ -1820,6 +1879,21 @@ function gotoHash() {
1820
1879
  if (isNaN(stickyTop)) stickyTop = 0;
1821
1880
  }
1822
1881
  window.scrollTo(0, top - stickyTop);
1882
+
1883
+ if (m[5]) {
1884
+ const segs = m[5].substring(1).split('/');
1885
+ const role = segs[0];
1886
+ if (!role) return;
1887
+ const snippet = segs[1];
1888
+
1889
+ const aRole = document.querySelector(`${panelSelector} .panel-body .nav-tabs a[data-role="${role}"]`);
1890
+ if (aRole && aRole.onclick) aRole.onclick();
1891
+
1892
+ if (!snippet) return;
1893
+
1894
+ const aSnippet = document.querySelector(`${panelSelector} .panel-body .tab-pages .tab-page[data-role="${role}"] .nav-tabs a[data-role="${snippet}"]`);
1895
+ if (aSnippet && aSnippet.onclick) aSnippet.onclick();
1896
+ }
1823
1897
  }
1824
1898
 
1825
1899
  function copied() {
@@ -16,9 +16,16 @@ function reserveWhitelist(obj, whitelist) {
16
16
  return obj;
17
17
  }
18
18
  const environmentVariableWhitelist = ['type', 'description'];
19
- function filterEnvironmentVariables(environmentVariables) {
19
+ function filterEnvironmentVariables(environmentVariables, allowRoles) {
20
20
  if (!environmentVariables)
21
21
  return;
22
+ if (!allowRoles.includes('*')) {
23
+ Object.keys(environmentVariables).forEach((envName) => {
24
+ if (environmentVariables[envName].codeHint?.roles?.every((role) => !allowRoles.includes(role))) {
25
+ delete environmentVariables[envName];
26
+ }
27
+ });
28
+ }
22
29
  Object.values(environmentVariables).forEach((env) => reserveWhitelist(env, environmentVariableWhitelist));
23
30
  }
24
31
  const entityWhitelist = ['name', 'description', 'actions', 'views', 'features', 'fields'];
@@ -145,7 +152,9 @@ function markIncludedFields(excludedFields, dict) {
145
152
  if (!Array.isArray(excludedFields))
146
153
  return;
147
154
  for (const m in dict) {
148
- if (!dict[m] && !excludedFields.includes(m))
155
+ if (!dict[m] &&
156
+ !excludedFields.includes(m) &&
157
+ excludedFields.every((excludedField) => !m.startsWith(excludedField + '.')))
149
158
  dict[m] = true;
150
159
  }
151
160
  }
@@ -198,7 +207,18 @@ function filterEntityFields(entity) {
198
207
  case 'updateOne':
199
208
  case 'updateMany':
200
209
  case 'updatePartially':
201
- actions.forEach((action) => (action.exclude = action.exclude?.filter((field) => allowFields.includes(field))));
210
+ actions.forEach((action) => {
211
+ action.exclude = action.exclude?.filter((field) => {
212
+ let idx = 0;
213
+ do {
214
+ idx = field.indexOf('.', idx + 1);
215
+ const prefix = idx === -1 ? field : field.substring(0, idx);
216
+ if (allowFields.includes(prefix))
217
+ return true;
218
+ } while (idx !== -1);
219
+ return false;
220
+ });
221
+ });
202
222
  break;
203
223
  }
204
224
  });
@@ -250,7 +270,8 @@ export default async function genDocs(args, projectName, project, alias, source)
250
270
  throw new Error(`Not found gen docs configuration. Please run 'npx mushroom-cli config ${projectName} gen docs OUTPUT_FILE.html${alias === 'default' ? '' : ' alias ' + alias}' first`);
251
271
  }
252
272
  common.logInfo('Use GenDocEngine v1.2');
253
- const entities = common.getEntities(source, aliasProject.allowRoles);
273
+ const allowRoles = aliasProject.allowRoles ?? ['*'];
274
+ const entities = common.getEntities(source, allowRoles);
254
275
  if (Array.isArray(aliasProject.entities)) {
255
276
  for (const entityName in entities) {
256
277
  if (Object.hasOwnProperty.call(entities, entityName)) {
@@ -260,7 +281,7 @@ export default async function genDocs(args, projectName, project, alias, source)
260
281
  }
261
282
  }
262
283
  }
263
- filterEnvironmentVariables(source.environmentVariables);
284
+ filterEnvironmentVariables(source.environmentVariables, allowRoles);
264
285
  filterEntities(entities);
265
286
  const data = JSON.stringify({
266
287
  info: {
@@ -275,7 +296,7 @@ export default async function genDocs(args, projectName, project, alias, source)
275
296
  },
276
297
  },
277
298
  features: aliasProject.features,
278
- roles: common.filterRoles(source.roles ?? [], aliasProject.allowRoles ?? ['*']),
299
+ roles: common.filterRoles(source.roles ?? [], allowRoles),
279
300
  environmentVariables: source.environmentVariables,
280
301
  entities: entities,
281
302
  });
@@ -259,7 +259,7 @@ export function genTypescriptDriverString(aliasProject, source) {
259
259
  extensionsCodeLines.push('FileExtension.useFile(mushroom);');
260
260
  }
261
261
  }
262
- let code = `import mushroom, { defineAsyncResource, defineAsyncView, fireEvent, createRestfulAsyncFunction, IdType, IMushroom, Mushroom, MushroomRequest, MushroomRequestSetting, MushroomRestfulRequest, MushroomResponse, MushroomResponseDataType, MushroomResourceBase, MushroomListResource, MushroomFindByIdResource, MushroomCreateResource, MushroomBatchCreateResource, MushroomUpdateResource, MushroomBatchUpdateResource, MushroomPartialUpdateResource, MushroomDeleteResource, MushroomBatchDeleteResource, MushroomExtensibleResource, MushroomViewFunction, MushroomError, IMushroomOfflineResponse, MushroomOfflineResponse, MushroomOfflineResponseState } from "mushroomjs";
262
+ let code = `import mushroom, { defineAsyncResource, defineAsyncView, fireEvent, createRestfulAsyncFunction, IdType, IMushroom, Mushroom, MushroomRequest, MushroomRequestSetting, MushroomRestfulRequest, MushroomResponse, MushroomResponseDataType, MushroomResourceBase, MushroomListResource, MushroomCountResource, MushroomFindByIdResource, MushroomCreateResource, MushroomBatchCreateResource, MushroomUpdateResource, MushroomBatchUpdateResource, MushroomPartialUpdateResource, MushroomDeleteResource, MushroomBatchDeleteResource, MushroomExtensibleResource, MushroomViewFunction, MushroomError, IMushroomOfflineResponse, MushroomOfflineResponse, MushroomOfflineResponseState } from "mushroomjs";
263
263
  ${extensionsImportLines.join('\n')}
264
264
  export * from "mushroomjs";
265
265
  export const extensions = {
@@ -316,7 +316,7 @@ export interface CustomMethodBodyRequest<TBody> extends MushroomRequest {
316
316
  case 'findMany': {
317
317
  iApiClassBody += ` & MushroomListResource<${typescriptClassName}>`;
318
318
  if (includeTotal)
319
- iApiClassBody += ` & MushroomCountResource<${typescriptClassName}>`;
319
+ iApiClassBody += ' & MushroomCountResource';
320
320
  break;
321
321
  }
322
322
  case 'findById': {
@@ -439,7 +439,7 @@ export function genDtsString(aliasProject, source) {
439
439
  extensionsTypes.push('FileExtension.IMushroomFile & ');
440
440
  }
441
441
  }
442
- let code = `import { IdType, IMushroom, Mushroom, MushroomRequest, MushroomRequestSetting, MushroomRestfulRequest, MushroomResponse, MushroomResponseDataType, MushroomResourceBase, MushroomListResource, MushroomFindByIdResource, MushroomCreateResource, MushroomBatchCreateResource, MushroomUpdateResource, MushroomBatchUpdateResource, MushroomPartialUpdateResource, MushroomDeleteResource, MushroomBatchDeleteResource, MushroomExtensibleResource, MushroomViewFunction, MushroomError, IMushroomOfflineResponse, MushroomOfflineResponse, MushroomOfflineResponseState } from "mushroomjs";
442
+ let code = `import { IdType, IMushroom, Mushroom, MushroomRequest, MushroomRequestSetting, MushroomRestfulRequest, MushroomResponse, MushroomResponseDataType, MushroomResourceBase, MushroomListResource, MushroomCountResource, MushroomFindByIdResource, MushroomCreateResource, MushroomBatchCreateResource, MushroomUpdateResource, MushroomBatchUpdateResource, MushroomPartialUpdateResource, MushroomDeleteResource, MushroomBatchDeleteResource, MushroomExtensibleResource, MushroomViewFunction, MushroomError, IMushroomOfflineResponse, MushroomOfflineResponse, MushroomOfflineResponseState } from "mushroomjs";
443
443
  ${extensionsImportLines.join('\n')}
444
444
  export * from "mushroomjs";
445
445
  export declare const extensions = {
@@ -495,7 +495,7 @@ export interface CustomMethodBodyRequest<TBody> extends MushroomRequest {
495
495
  case 'findMany': {
496
496
  iApiClassBody += ` & MushroomListResource<${typescriptClassName}>`;
497
497
  if (includeTotal)
498
- iApiClassBody += ` & MushroomCountResource<${typescriptClassName}>`;
498
+ iApiClassBody += ' & MushroomCountResource';
499
499
  break;
500
500
  }
501
501
  case 'findById': {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mushroom-cli",
3
- "version": "1.2.0-alpha.22",
3
+ "version": "1.2.0-alpha.24",
4
4
  "description": "Mushroom CLI",
5
5
  "main": "dist/mr.js",
6
6
  "type": "module",