]>
git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/api-viewer/APIViewer.js
7d990cb3d969b3487e0d9a53ce09dc8587e8ac6d
1 Ext
.onReady(function() {
2 Ext
.define('pmx-param-schema', {
3 extend
: 'Ext.data.Model',
5 'name', 'type', 'typetext', 'description', 'verbose_description',
6 'enum', 'minimum', 'maximum', 'minLength', 'maxLength',
7 'pattern', 'title', 'requires', 'format', 'default',
8 'disallow', 'extends', 'links',
16 let store
= Ext
.define('pmx-updated-treestore', {
17 extend
: 'Ext.data.TreeStore',
18 model
: Ext
.define('pmx-api-doc', {
19 extend
: 'Ext.data.Model',
21 'path', 'info', 'text',
36 doFilter: function(node
) {
37 this.filterNodes(node
, this.getFilters().getFilterFn(), true);
40 filterNodes: function(node
, filterFn
, parentVisible
) {
42 bottomUpFiltering
= me
.filterer
=== 'bottomup',
43 match
= filterFn(node
) && parentVisible
|| (node
.isRoot() && !me
.getRootVisible()),
44 childNodes
= node
.childNodes
,
45 len
= childNodes
&& childNodes
.length
, i
, matchingChildren
;
48 for (i
= 0; i
< len
; ++i
) {
49 matchingChildren
= me
.filterNodes(childNodes
[i
], filterFn
, match
|| bottomUpFiltering
) || matchingChildren
;
51 if (bottomUpFiltering
) {
52 match
= matchingChildren
|| match
;
56 node
.set("visible", match
, me
._silentOptions
);
62 let render_description = function(value
, metaData
, record
) {
63 let pdef
= record
.data
;
65 value
= pdef
.verbose_description
|| value
;
67 // TODO: try to render asciidoc correctly
69 metaData
.style
= 'white-space:pre-wrap;';
71 return Ext
.htmlEncode(value
);
74 let render_type = function(value
, metaData
, record
) {
75 let pdef
= record
.data
;
77 return pdef
.enum ? 'enum' : pdef
.type
|| 'string';
80 let render_simple_format = function(pdef
, type_fallback
) {
81 if (pdef
.typetext
) {return pdef
.typetext
;}
83 if (pdef
.enum) {return pdef
.enum.join(' | ');}
85 if (pdef
.format
) {return pdef
.format
;}
87 if (pdef
.pattern
) {return pdef
.pattern
;}
89 if (pdef
.type
=== 'boolean') {return `<true|false>`;}
91 if (type_fallback
&& pdef
.type
) {return `<${pdef.type}>`;}
94 let render_format = function(value
, metaData
, record
) {
95 let pdef
= record
.data
;
97 metaData
.style
= 'white-space:normal;';
99 if (pdef
.type
=== 'array' && pdef
.items
) {
100 let format
= render_simple_format(pdef
.items
, true);
101 return `[${Ext.htmlEncode(format)}, ...]`;
104 return Ext
.htmlEncode(render_simple_format(pdef
) || '');
107 let real_path = function(path
) {
108 return path
.replace(/^.*\/_upgrade_(\/)?/, "/");
111 let permission_text = function(permission
) {
114 if (permission
.user
) {
115 if (!permission
.description
) {
116 if (permission
.user
=== 'world') {
117 permhtml
+= "Accessible without any authentication.";
118 } else if (permission
.user
=== 'all') {
119 permhtml
+= "Accessible by all authenticated users.";
121 permhtml
+= 'Onyl accessible by user "' +
122 permission
.user
+ '"';
125 } else if (permission
.check
) {
126 permhtml
+= "<pre>Check: " +
127 Ext
.htmlEncode(Ext
.JSON
.encode(permission
.check
)) + "</pre>";
128 } else if (permission
.userParam
) {
129 permhtml
+= `<div>Check if user matches parameter '${permission.userParam}'`;
130 } else if (permission
.or
) {
131 permhtml
+= "<div>Or<div style='padding-left: 10px;'>";
132 Ext
.Array
.each(permission
.or
, function(sub_permission
) {
133 permhtml
+= permission_text(sub_permission
);
135 permhtml
+= "</div></div>";
136 } else if (permission
.and
) {
137 permhtml
+= "<div>And<div style='padding-left: 10px;'>";
138 Ext
.Array
.each(permission
.and
, function(sub_permission
) {
139 permhtml
+= permission_text(sub_permission
);
141 permhtml
+= "</div></div>";
143 //console.log(permission);
144 permhtml
+= "Unknown syntax!";
150 let render_docu = function(data
) {
153 // console.dir(data);
164 Ext
.Array
.each(['GET', 'POST', 'PUT', 'DELETE'], function(method
) {
165 let info
= md
[method
];
169 usage
+= "<table><tr><td>HTTP: </td><td>"
170 + method
+ " " + real_path("/api2/json" + data
.path
) + "</td></tr>";
172 if (typeof cliusage
=== 'function') {
173 usage
+= cliusage(method
, real_path(data
.path
));
178 title
: 'Description',
179 html
: Ext
.htmlEncode(info
.description
),
189 if (info
.parameters
&& info
.parameters
.properties
) {
190 let pstore
= Ext
.create('Ext.data.Store', {
191 model
: 'pmx-param-schema',
195 groupField
: 'optional',
204 Ext
.Object
.each(info
.parameters
.properties
, function(name
, pdef
) {
211 let groupingFeature
= Ext
.create('Ext.grid.feature.Grouping', {
212 enableGroupingMenu
: false,
213 groupHeaderTpl
: '<tpl if="groupValue">Optional</tpl><tpl if="!groupValue">Required</tpl>',
219 features
: [groupingFeature
],
234 renderer
: render_type
,
239 dataIndex
: 'default',
245 renderer
: render_format
,
249 header
: 'Description',
250 dataIndex
: 'description',
251 renderer
: render_description
,
259 let retinf
= info
.returns
;
260 let rtype
= retinf
.type
;
261 if (!rtype
&& retinf
.items
) {rtype
= 'array';}
262 if (!rtype
) {rtype
= 'object';}
264 let rpstore
= Ext
.create('Ext.data.Store', {
265 model
: 'pmx-param-schema',
269 groupField
: 'optional',
279 if (rtype
=== 'array' && retinf
.items
.properties
) {
280 properties
= retinf
.items
.properties
;
283 if (rtype
=== 'object' && retinf
.properties
) {
284 properties
= retinf
.properties
;
287 Ext
.Object
.each(properties
, function(name
, pdef
) {
294 let groupingFeature
= Ext
.create('Ext.grid.feature.Grouping', {
295 enableGroupingMenu
: false,
296 groupHeaderTpl
: '<tpl if="groupValue">Optional</tpl><tpl if="!groupValue">Obligatory</tpl>',
300 returnhtml
= '<pre>items: ' + Ext
.htmlEncode(JSON
.stringify(retinf
.items
, null, 4)) + '</pre>';
303 if (retinf
.properties
) {
304 returnhtml
= returnhtml
|| '';
305 returnhtml
+= '<pre>properties:' + Ext
.htmlEncode(JSON
.stringify(retinf
.properties
, null, 4)) + '</pre>';
308 let rawSection
= Ext
.create('Ext.panel.Panel', {
309 bodyPadding
: '0px 10px 10px 10px',
316 title
: 'Returns: ' + rtype
,
317 features
: [groupingFeature
],
332 renderer
: render_type
,
337 dataIndex
: 'default',
343 renderer
: render_format
,
347 header
: 'Description',
348 dataIndex
: 'description',
349 renderer
: render_description
,
357 handler: function(btn
) {
358 rawSection
.setVisible(!rawSection
.isVisible());
359 btn
.setText(rawSection
.isVisible() ? 'Hide RAW' : 'Show RAW');
365 sections
.push(rawSection
);
368 if (!data
.path
.match(/\/_upgrade_/)) {
371 if (!info
.permissions
) {
372 permhtml
= "Root only.";
374 if (info
.permissions
.description
) {
375 permhtml
+= "<div style='white-space:pre-wrap;padding-bottom:10px;'>" +
376 Ext
.htmlEncode(info
.permissions
.description
) + "</div>";
378 permhtml
+= permission_text(info
.permissions
);
381 if (info
.allowtoken
!== undefined && !info
.allowtoken
) {
382 permhtml
+= "<br />This API endpoint is not available for API tokens.";
386 title
: 'Required permissions',
403 let ct
= Ext
.getCmp('docview');
404 ct
.setTitle("Path: " + real_path(data
.path
));
410 Ext
.define('Ext.form.SearchField', {
411 extend
: 'Ext.form.field.Text',
412 alias
: 'widget.searchfield',
414 emptyText
: 'Search...',
420 'change': function() {
421 let value
= this.getValue();
422 if (!Ext
.isEmpty(value
)) {
435 let tree
= Ext
.create('Ext.tree.Panel', {
436 title
: 'Resource Tree',
439 xtype
: 'searchfield',
445 tooltip
: 'Expand all',
446 tooltipType
: 'title',
447 callback
: (tree
) => tree
.expandAll(),
451 tooltip
: 'Collapse all',
452 tooltipType
: 'title',
453 callback
: (tree
) => tree
.collapseAll(),
463 selectionchange: function(v
, selections
) {
464 if (!selections
[0]) {return;}
465 let rec
= selections
[0];
466 render_docu(rec
.data
);
467 location
.hash
= '#' + rec
.data
.path
;
472 Ext
.create('Ext.container.Viewport', {
474 renderTo
: Ext
.getBody(),
479 title
: 'Documentation',
489 let deepLink = function() {
490 let path
= window
.location
.hash
.substring(1).replace(/\/\s*$/, '');
491 let endpoint
= store
.findNode('path', path
);
494 tree
.getSelectionModel().select(endpoint
);
495 tree
.expandPath(endpoint
.getPath());
496 render_docu(endpoint
.data
);
499 window
.onhashchange
= deepLink
;