]>
git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/api-viewer/APIViewer.js
3 Ext
.onReady(function() {
4 Ext
.define('pmx-param-schema', {
5 extend
: 'Ext.data.Model',
7 'name', 'type', 'typetext', 'description', 'verbose_description',
8 'enum', 'minimum', 'maximum', 'minLength', 'maxLength',
9 'pattern', 'title', 'requires', 'format', 'default',
10 'disallow', 'extends', 'links',
18 let store
= Ext
.define('pmx-updated-treestore', {
19 extend
: 'Ext.data.TreeStore',
20 model
: Ext
.define('pmx-api-doc', {
21 extend
: 'Ext.data.Model',
23 'path', 'info', 'text',
38 doFilter: function(node
) {
39 this.filterNodes(node
, this.getFilters().getFilterFn(), true);
42 filterNodes: function(node
, filterFn
, parentVisible
) {
45 let match
= filterFn(node
) && (parentVisible
|| (node
.isRoot() && !me
.getRootVisible()));
47 if (node
.childNodes
&& node
.childNodes
.length
) {
48 let bottomUpFiltering
= me
.filterer
=== 'bottomup';
50 for (const child
of node
.childNodes
) {
51 childMatch
= me
.filterNodes(child
, filterFn
, match
|| bottomUpFiltering
) || childMatch
;
53 if (bottomUpFiltering
) {
54 match
= childMatch
|| match
;
58 node
.set("visible", match
, me
._silentOptions
);
64 let render_description = function(value
, metaData
, record
) {
65 let pdef
= record
.data
;
67 value
= pdef
.verbose_description
|| value
;
69 // TODO: try to render asciidoc correctly
71 metaData
.style
= 'white-space:pre-wrap;';
73 return Ext
.htmlEncode(value
);
76 let render_type = function(value
, metaData
, record
) {
77 let pdef
= record
.data
;
79 return pdef
.enum ? 'enum' : pdef
.type
|| 'string';
82 let render_simple_format = function(pdef
, type_fallback
) {
87 return pdef
.enum.join(' | ');
95 if (pdef
.type
=== 'boolean') {
96 return `<true|false>`;
98 if (type_fallback
&& pdef
.type
) {
99 return `<${pdef.type}>`;
104 let render_format = function(value
, metaData
, record
) {
105 let pdef
= record
.data
;
107 metaData
.style
= 'white-space:normal;';
109 if (pdef
.type
=== 'array' && pdef
.items
) {
110 let format
= render_simple_format(pdef
.items
, true);
111 return `[${Ext.htmlEncode(format)}, ...]`;
114 return Ext
.htmlEncode(render_simple_format(pdef
));
117 let real_path = function(path
) {
118 if (!path
.match(/^[/]/)) {
121 return path
.replace(/^.*\/_upgrade_(\/)?/, "/");
124 let permission_text = function(permission
) {
127 if (permission
.user
) {
128 if (!permission
.description
) {
129 if (permission
.user
=== 'world') {
130 permhtml
+= "Accessible without any authentication.";
131 } else if (permission
.user
=== 'all') {
132 permhtml
+= "Accessible by all authenticated users.";
134 permhtml
+= `Only accessible by user "${permission.user}"`;
137 } else if (permission
.check
) {
138 permhtml
+= `<pre>Check: ${Ext.htmlEncode(JSON.stringify(permission.check))}</pre>`;
139 } else if (permission
.userParam
) {
140 permhtml
+= `<div>Check if user matches parameter '${permission.userParam}'`;
141 } else if (permission
.or
) {
142 permhtml
+= "<div>Or<div style='padding-left: 10px;'>";
143 permhtml
+= permission
.or
.map(v
=> permission_text(v
)).join('');
144 permhtml
+= "</div></div>";
145 } else if (permission
.and
) {
146 permhtml
+= "<div>And<div style='padding-left: 10px;'>";
147 permhtml
+= permission
.and
.map(v
=> permission_text(v
)).join('');
148 permhtml
+= "</div></div>";
150 permhtml
+= "Unknown syntax!";
156 let render_docu = function(data
) {
161 Ext
.Array
.each(['GET', 'POST', 'PUT', 'DELETE'], function(method
) {
162 let info
= md
[method
];
164 let endpoint
= real_path(data
.path
);
165 let usage
= `<table><tr><td>HTTP: </td><td>`;
166 usage
+= `${method} /api2/json${endpoint}</td></tr>`;
168 if (typeof cliUsageRenderer
=== 'function') {
169 usage
+= cliUsageRenderer(method
, endpoint
); // eslint-disable-line no-undef
174 title
: 'Description',
175 html
: Ext
.htmlEncode(info
.description
),
185 if (info
.parameters
&& info
.parameters
.properties
) {
186 let pstore
= Ext
.create('Ext.data.Store', {
187 model
: 'pmx-param-schema',
191 groupField
: 'optional',
200 Ext
.Object
.each(info
.parameters
.properties
, function(name
, pdef
) {
207 let groupingFeature
= Ext
.create('Ext.grid.feature.Grouping', {
208 enableGroupingMenu
: false,
209 groupHeaderTpl
: '<tpl if="groupValue">Optional</tpl><tpl if="!groupValue">Required</tpl>',
215 features
: [groupingFeature
],
230 renderer
: render_type
,
235 dataIndex
: 'default',
241 renderer
: render_format
,
245 header
: 'Description',
246 dataIndex
: 'description',
247 renderer
: render_description
,
255 let retinf
= info
.returns
;
256 let rtype
= retinf
.type
;
257 if (!rtype
&& retinf
.items
) {rtype
= 'array';}
258 if (!rtype
) {rtype
= 'object';}
260 let rpstore
= Ext
.create('Ext.data.Store', {
261 model
: 'pmx-param-schema',
265 groupField
: 'optional',
275 if (rtype
=== 'array' && retinf
.items
.properties
) {
276 properties
= retinf
.items
.properties
;
279 if (rtype
=== 'object' && retinf
.properties
) {
280 properties
= retinf
.properties
;
283 Ext
.Object
.each(properties
, function(name
, pdef
) {
290 let groupingFeature
= Ext
.create('Ext.grid.feature.Grouping', {
291 enableGroupingMenu
: false,
292 groupHeaderTpl
: '<tpl if="groupValue">Optional</tpl><tpl if="!groupValue">Obligatory</tpl>',
296 returnhtml
= '<pre>items: ' + Ext
.htmlEncode(JSON
.stringify(retinf
.items
, null, 4)) + '</pre>';
299 if (retinf
.properties
) {
300 returnhtml
= returnhtml
|| '';
301 returnhtml
+= '<pre>properties:' + Ext
.htmlEncode(JSON
.stringify(retinf
.properties
, null, 4)) + '</pre>';
304 let rawSection
= Ext
.create('Ext.panel.Panel', {
305 bodyPadding
: '0px 10px 10px 10px',
312 title
: 'Returns: ' + rtype
,
313 features
: [groupingFeature
],
328 renderer
: render_type
,
333 dataIndex
: 'default',
339 renderer
: render_format
,
343 header
: 'Description',
344 dataIndex
: 'description',
345 renderer
: render_description
,
353 handler: function(btn
) {
354 rawSection
.setVisible(!rawSection
.isVisible());
355 btn
.setText(rawSection
.isVisible() ? 'Hide RAW' : 'Show RAW');
361 sections
.push(rawSection
);
364 if (!data
.path
.match(/\/_upgrade_/)) {
367 if (!info
.permissions
) {
368 permhtml
= "Root only.";
370 if (info
.permissions
.description
) {
371 permhtml
+= "<div style='white-space:pre-wrap;padding-bottom:10px;'>" +
372 Ext
.htmlEncode(info
.permissions
.description
) + "</div>";
374 permhtml
+= permission_text(info
.permissions
);
377 if (info
.allowtoken
!== undefined && !info
.allowtoken
) {
378 permhtml
+= "<br />This API endpoint is not available for API tokens.";
382 title
: 'Required permissions',
399 let ct
= Ext
.getCmp('docview');
400 ct
.setTitle("Path: " + real_path(data
.path
));
406 Ext
.define('Ext.form.SearchField', {
407 extend
: 'Ext.form.field.Text',
408 alias
: 'widget.searchfield',
410 emptyText
: 'Search...',
416 'change': function() {
417 let value
= this.getValue();
418 if (!Ext
.isEmpty(value
)) {
431 let treePanel
= Ext
.create('Ext.tree.Panel', {
432 title
: 'Resource Tree',
435 xtype
: 'searchfield',
441 tooltip
: 'Expand all',
442 tooltipType
: 'title',
443 callback
: tree
=> tree
.expandAll(),
447 tooltip
: 'Collapse all',
448 tooltipType
: 'title',
449 callback
: tree
=> tree
.collapseAll(),
459 selectionchange: function(v
, selections
) {
460 if (!selections
[0]) {return;}
461 let rec
= selections
[0];
462 render_docu(rec
.data
);
463 location
.hash
= '#' + rec
.data
.path
;
468 Ext
.create('Ext.container.Viewport', {
470 renderTo
: Ext
.getBody(),
475 title
: 'Documentation',
485 let deepLink = function() {
486 let path
= window
.location
.hash
.substring(1).replace(/\/\s*$/, '');
487 let endpoint
= store
.findNode('path', path
);
490 treePanel
.getSelectionModel().select(endpoint
);
491 treePanel
.expandPath(endpoint
.getPath());
492 render_docu(endpoint
.data
);
495 window
.onhashchange
= deepLink
;