]>
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 return path
.replace(/^.*\/_upgrade_(\/)?/, "/");
121 let permission_text = function(permission
) {
124 if (permission
.user
) {
125 if (!permission
.description
) {
126 if (permission
.user
=== 'world') {
127 permhtml
+= "Accessible without any authentication.";
128 } else if (permission
.user
=== 'all') {
129 permhtml
+= "Accessible by all authenticated users.";
131 permhtml
+= `Only accessible by user "${permission.user}"`;
134 } else if (permission
.check
) {
135 permhtml
+= `<pre>Check: ${Ext.htmlEncode(JSON.stringify(permission.check))}</pre>`;
136 } else if (permission
.userParam
) {
137 permhtml
+= `<div>Check if user matches parameter '${permission.userParam}'`;
138 } else if (permission
.or
) {
139 permhtml
+= "<div>Or<div style='padding-left: 10px;'>";
140 permhtml
+= permission
.or
.map(v
=> permission_text(v
)).join('');
141 permhtml
+= "</div></div>";
142 } else if (permission
.and
) {
143 permhtml
+= "<div>And<div style='padding-left: 10px;'>";
144 permhtml
+= permission
.and
.map(v
=> permission_text(v
)).join('');
145 permhtml
+= "</div></div>";
147 permhtml
+= "Unknown syntax!";
153 let render_docu = function(data
) {
158 Ext
.Array
.each(['GET', 'POST', 'PUT', 'DELETE'], function(method
) {
159 let info
= md
[method
];
161 let endpoint
= real_path(data
.path
);
162 let usage
= `<table><tr><td>HTTP: </td><td>`;
163 usage
+= `${method} /api2/json/${endpoint}</td></tr>`;
165 if (typeof cliUsageRenderer
=== 'function') {
166 usage
+= cliUsageRenderer(method
, endpoint
); // eslint-disable-line no-undef
171 title
: 'Description',
172 html
: Ext
.htmlEncode(info
.description
),
182 if (info
.parameters
&& info
.parameters
.properties
) {
183 let pstore
= Ext
.create('Ext.data.Store', {
184 model
: 'pmx-param-schema',
188 groupField
: 'optional',
197 Ext
.Object
.each(info
.parameters
.properties
, function(name
, pdef
) {
204 let groupingFeature
= Ext
.create('Ext.grid.feature.Grouping', {
205 enableGroupingMenu
: false,
206 groupHeaderTpl
: '<tpl if="groupValue">Optional</tpl><tpl if="!groupValue">Required</tpl>',
212 features
: [groupingFeature
],
227 renderer
: render_type
,
232 dataIndex
: 'default',
238 renderer
: render_format
,
242 header
: 'Description',
243 dataIndex
: 'description',
244 renderer
: render_description
,
252 let retinf
= info
.returns
;
253 let rtype
= retinf
.type
;
254 if (!rtype
&& retinf
.items
) {rtype
= 'array';}
255 if (!rtype
) {rtype
= 'object';}
257 let rpstore
= Ext
.create('Ext.data.Store', {
258 model
: 'pmx-param-schema',
262 groupField
: 'optional',
272 if (rtype
=== 'array' && retinf
.items
.properties
) {
273 properties
= retinf
.items
.properties
;
276 if (rtype
=== 'object' && retinf
.properties
) {
277 properties
= retinf
.properties
;
280 Ext
.Object
.each(properties
, function(name
, pdef
) {
287 let groupingFeature
= Ext
.create('Ext.grid.feature.Grouping', {
288 enableGroupingMenu
: false,
289 groupHeaderTpl
: '<tpl if="groupValue">Optional</tpl><tpl if="!groupValue">Obligatory</tpl>',
293 returnhtml
= '<pre>items: ' + Ext
.htmlEncode(JSON
.stringify(retinf
.items
, null, 4)) + '</pre>';
296 if (retinf
.properties
) {
297 returnhtml
= returnhtml
|| '';
298 returnhtml
+= '<pre>properties:' + Ext
.htmlEncode(JSON
.stringify(retinf
.properties
, null, 4)) + '</pre>';
301 let rawSection
= Ext
.create('Ext.panel.Panel', {
302 bodyPadding
: '0px 10px 10px 10px',
309 title
: 'Returns: ' + rtype
,
310 features
: [groupingFeature
],
325 renderer
: render_type
,
330 dataIndex
: 'default',
336 renderer
: render_format
,
340 header
: 'Description',
341 dataIndex
: 'description',
342 renderer
: render_description
,
350 handler: function(btn
) {
351 rawSection
.setVisible(!rawSection
.isVisible());
352 btn
.setText(rawSection
.isVisible() ? 'Hide RAW' : 'Show RAW');
358 sections
.push(rawSection
);
361 if (!data
.path
.match(/\/_upgrade_/)) {
364 if (!info
.permissions
) {
365 permhtml
= "Root only.";
367 if (info
.permissions
.description
) {
368 permhtml
+= "<div style='white-space:pre-wrap;padding-bottom:10px;'>" +
369 Ext
.htmlEncode(info
.permissions
.description
) + "</div>";
371 permhtml
+= permission_text(info
.permissions
);
374 if (info
.allowtoken
!== undefined && !info
.allowtoken
) {
375 permhtml
+= "<br />This API endpoint is not available for API tokens.";
379 title
: 'Required permissions',
396 let ct
= Ext
.getCmp('docview');
397 ct
.setTitle("Path: " + real_path(data
.path
));
403 Ext
.define('Ext.form.SearchField', {
404 extend
: 'Ext.form.field.Text',
405 alias
: 'widget.searchfield',
407 emptyText
: 'Search...',
413 'change': function() {
414 let value
= this.getValue();
415 if (!Ext
.isEmpty(value
)) {
428 let treePanel
= Ext
.create('Ext.tree.Panel', {
429 title
: 'Resource Tree',
432 xtype
: 'searchfield',
438 tooltip
: 'Expand all',
439 tooltipType
: 'title',
440 callback
: tree
=> tree
.expandAll(),
444 tooltip
: 'Collapse all',
445 tooltipType
: 'title',
446 callback
: tree
=> tree
.collapseAll(),
456 selectionchange: function(v
, selections
) {
457 if (!selections
[0]) {return;}
458 let rec
= selections
[0];
459 render_docu(rec
.data
);
460 location
.hash
= '#' + rec
.data
.path
;
465 Ext
.create('Ext.container.Viewport', {
467 renderTo
: Ext
.getBody(),
472 title
: 'Documentation',
482 let deepLink = function() {
483 let path
= window
.location
.hash
.substring(1).replace(/\/\s*$/, '');
484 let endpoint
= store
.findNode('path', path
);
487 treePanel
.getSelectionModel().select(endpoint
);
488 treePanel
.expandPath(endpoint
.getPath());
489 render_docu(endpoint
.data
);
492 window
.onhashchange
= deepLink
;