From: Jie Han Date: Thu, 22 Aug 2019 12:06:24 +0000 (+0800) Subject: Add pagination for mounted resources of apidocs X-Git-Tag: release/magnesium~41 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=c8f621b97a42a68e42b7b4df49312a9d5048d59d;p=netconf.git Add pagination for mounted resources of apidocs Only show 20 yang resource for one page when there're large amount yang resoures from a mount point such that the explorer would not clash. Change-Id: I7f7180ae7464407933cee49bd4fe8d24db216f77 Signed-off-by: Jie Han --- diff --git a/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/ApiDocServiceImpl.java b/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/ApiDocServiceImpl.java index c9726b0e10..bce08552d8 100644 --- a/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/ApiDocServiceImpl.java +++ b/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/ApiDocServiceImpl.java @@ -35,6 +35,11 @@ import org.opendaylight.netconf.sal.rest.doc.swagger.ResourceList; */ public class ApiDocServiceImpl implements ApiDocService { + public static final int DEFAULT_PAGESIZE = 20; + // Query parameter + private static final String TOTAL_PAGES = "totalPages"; + private static final String PAGE_NUM = "pageNum"; + private final MountPointSwagger mountPointSwaggerDraft02; private final MountPointSwagger mountPointSwaggerRFC8040; private final ApiDocGeneratorDraftO2 apiDocGeneratorDraft02; @@ -108,10 +113,26 @@ public class ApiDocServiceImpl implements ApiDocService { @Override public synchronized Response getMountRootDoc(final String instanceNum, final UriInfo uriInfo) { final ResourceList resourceList; + + if (uriInfo.getQueryParameters().getFirst(TOTAL_PAGES) != null) { + if (isNew(uriInfo)) { + resourceList = mountPointSwaggerRFC8040.getResourceList(uriInfo, Long.parseLong(instanceNum)); + } else { + resourceList = mountPointSwaggerDraft02.getResourceList(uriInfo, Long.parseLong(instanceNum)); + } + int size = resourceList.getApis().size(); + return Response.ok(size % DEFAULT_PAGESIZE == 0 ? size / DEFAULT_PAGESIZE + : size / DEFAULT_PAGESIZE + 1).build(); + } + + final int pageNum = Integer.parseInt(uriInfo.getQueryParameters().getFirst(PAGE_NUM)); + if (isNew(uriInfo)) { - resourceList = mountPointSwaggerRFC8040.getResourceList(uriInfo, Long.parseLong(instanceNum)); + resourceList = mountPointSwaggerRFC8040.getResourceList(uriInfo, Long.parseLong(instanceNum), pageNum, + false); } else { - resourceList = mountPointSwaggerDraft02.getResourceList(uriInfo, Long.parseLong(instanceNum)); + resourceList = mountPointSwaggerDraft02.getResourceList(uriInfo, Long.parseLong(instanceNum), pageNum, + false); } return Response.ok(resourceList).build(); } diff --git a/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/BaseYangSwaggerGenerator.java b/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/BaseYangSwaggerGenerator.java index ce41fa0e10..e1f6b50cfe 100644 --- a/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/BaseYangSwaggerGenerator.java +++ b/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/BaseYangSwaggerGenerator.java @@ -7,6 +7,7 @@ */ package org.opendaylight.netconf.sal.rest.doc.impl; +import static org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.DEFAULT_PAGESIZE; import static org.opendaylight.netconf.sal.rest.doc.util.RestDocgenUtil.resolvePathArgumentsName; import com.fasterxml.jackson.databind.ObjectMapper; @@ -80,33 +81,47 @@ public abstract class BaseYangSwaggerGenerator { public ResourceList getResourceListing(final UriInfo uriInfo) { final SchemaContext schemaContext = schemaService.getGlobalContext(); Preconditions.checkState(schemaContext != null); - return getResourceListing(uriInfo, schemaContext, ""); + return getResourceListing(uriInfo, schemaContext, "", 0, true); + } + + public ResourceList getResourceListing(final UriInfo uriInfo, final SchemaContext schemaContext, + final String context) { + return getResourceListing(uriInfo, schemaContext, context, 0, true); } /** * Return list of modules converted to swagger compliant resource list. */ public ResourceList getResourceListing(final UriInfo uriInfo, final SchemaContext schemaContext, - final String context) { + final String context, final int pageNum, boolean all) { final ResourceList resourceList = createResourceList(); final Set modules = getSortedModules(schemaContext); - final List resources = new ArrayList<>(modules.size()); + final List resources = new ArrayList<>(DEFAULT_PAGESIZE); LOG.info("Modules found [{}]", modules.size()); - + final int start = DEFAULT_PAGESIZE * pageNum; + final int end = start + DEFAULT_PAGESIZE; + int count = 0; for (final Module module : modules) { final String revisionString = module.getQNameModule().getRevision().map(Revision::toString).orElse(null); - final Resource resource = new Resource(); + LOG.debug("Working on [{},{}]...", module.getName(), revisionString); final ApiDeclaration doc = getApiDeclaration(module.getName(), revisionString, uriInfo, schemaContext, context); - if (doc != null) { - resource.setPath(generatePath(uriInfo, module.getName(), revisionString)); - resources.add(resource); + count++; + if (count >= start && count < end || all) { + final Resource resource = new Resource(); + resource.setPath(generatePath(uriInfo, module.getName(), revisionString)); + resources.add(resource); + } + + if (count >= end && !all) { + break; + } } else { LOG.warn("Could not generate doc for {},{}", module.getName(), revisionString); } @@ -125,7 +140,7 @@ public abstract class BaseYangSwaggerGenerator { } public String generatePath(final UriInfo uriInfo, final String name, final String revision) { - final URI uri = uriInfo.getRequestUriBuilder().path(generateCacheKey(name, revision)).build(); + final URI uri = uriInfo.getRequestUriBuilder().replaceQuery("").path(generateCacheKey(name, revision)).build(); return uri.toASCIIString(); } diff --git a/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/mountpoints/MountPointSwagger.java b/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/mountpoints/MountPointSwagger.java index ad79f153fe..fe037cf47f 100644 --- a/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/mountpoints/MountPointSwagger.java +++ b/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/mountpoints/MountPointSwagger.java @@ -101,6 +101,10 @@ public class MountPointSwagger implements DOMMountPointListener, AutoCloseable { } public ResourceList getResourceList(final UriInfo uriInfo, final Long id) { + return getResourceList(uriInfo, id, 0, true); + } + + public ResourceList getResourceList(final UriInfo uriInfo, final Long id, final int pageNum, boolean all) { final YangInstanceIdentifier iid = getInstanceId(id); if (iid == null) { return null; // indicating not found. @@ -115,7 +119,7 @@ public class MountPointSwagger implements DOMMountPointListener, AutoCloseable { dataStores.setPath(swaggerGenerator.generatePath(uriInfo, DATASTORES_LABEL, DATASTORES_REVISION)); resources.add(dataStores); final String urlPrefix = getYangMountUrl(iid); - final ResourceList list = swaggerGenerator.getResourceListing(uriInfo, context, urlPrefix); + final ResourceList list = swaggerGenerator.getResourceListing(uriInfo, context, urlPrefix, pageNum, all); resources.addAll(list.getApis()); list.setApis(resources); return list; diff --git a/restconf/sal-rest-docgen/src/main/resources/18/explorer/index.html b/restconf/sal-rest-docgen/src/main/resources/18/explorer/index.html index ddb047fc86..22c728ab4c 100644 --- a/restconf/sal-rest-docgen/src/main/resources/18/explorer/index.html +++ b/restconf/sal-rest-docgen/src/main/resources/18/explorer/index.html @@ -19,6 +19,8 @@ + @@ -33,6 +35,8 @@ + + @@ -25,15 +27,18 @@ + + @@ -110,7 +116,9 @@
- +
+ +
 
diff --git a/restconf/sal-rest-docgen/src/main/resources/explorer/lib/jquery.simplePagination.js b/restconf/sal-rest-docgen/src/main/resources/explorer/lib/jquery.simplePagination.js new file mode 100644 index 0000000000..1108cdc633 --- /dev/null +++ b/restconf/sal-rest-docgen/src/main/resources/explorer/lib/jquery.simplePagination.js @@ -0,0 +1,398 @@ +/** +* simplePagination.js v1.6 +* A simple jQuery pagination plugin. +* http://flaviusmatis.github.com/simplePagination.js/ +* +* Copyright 2012, Flavius Matis +* Released under the MIT license. +* http://flaviusmatis.github.com/license.html +*/ + +(function($){ + + var methods = { + init: function(options) { + var o = $.extend({ + items: 1, + itemsOnPage: 1, + pages: 0, + displayedPages: 5, + edges: 2, + currentPage: 0, + useAnchors: true, + hrefTextPrefix: '#page-', + hrefTextSuffix: '', + prevText: 'Prev', + nextText: 'Next', + ellipseText: '…', + ellipsePageSet: true, + cssStyle: 'light-theme', + listStyle: '', + labelMap: [], + selectOnClick: true, + nextAtFront: false, + invertPageOrder: false, + useStartEdge : true, + useEndEdge : true, + onPageClick: function(pageNumber, event) { + // Callback triggered when a page is clicked + // Page number is given as an optional parameter + }, + onInit: function() { + // Callback triggered immediately after initialization + } + }, options || {}); + + var self = this; + + o.pages = o.pages ? o.pages : Math.ceil(o.items / o.itemsOnPage) ? Math.ceil(o.items / o.itemsOnPage) : 1; + if (o.currentPage) + o.currentPage = o.currentPage - 1; + else + o.currentPage = !o.invertPageOrder ? 0 : o.pages - 1; + o.halfDisplayed = o.displayedPages / 2; + + this.each(function() { + self.addClass(o.cssStyle + ' simple-pagination').data('pagination', o); + methods._draw.call(self); + }); + + o.onInit(); + + return this; + }, + + selectPage: function(page) { + methods._selectPage.call(this, page - 1); + return this; + }, + + prevPage: function() { + var o = this.data('pagination'); + if (!o.invertPageOrder) { + if (o.currentPage > 0) { + methods._selectPage.call(this, o.currentPage - 1); + } + } else { + if (o.currentPage < o.pages - 1) { + methods._selectPage.call(this, o.currentPage + 1); + } + } + return this; + }, + + nextPage: function() { + var o = this.data('pagination'); + if (!o.invertPageOrder) { + if (o.currentPage < o.pages - 1) { + methods._selectPage.call(this, o.currentPage + 1); + } + } else { + if (o.currentPage > 0) { + methods._selectPage.call(this, o.currentPage - 1); + } + } + return this; + }, + + getPagesCount: function() { + return this.data('pagination').pages; + }, + + setPagesCount: function(count) { + this.data('pagination').pages = count; + }, + + getCurrentPage: function () { + return this.data('pagination').currentPage + 1; + }, + + destroy: function(){ + this.empty(); + return this; + }, + + drawPage: function (page) { + var o = this.data('pagination'); + o.currentPage = page - 1; + this.data('pagination', o); + methods._draw.call(this); + return this; + }, + + redraw: function(){ + methods._draw.call(this); + return this; + }, + + disable: function(){ + var o = this.data('pagination'); + o.disabled = true; + this.data('pagination', o); + methods._draw.call(this); + return this; + }, + + enable: function(){ + var o = this.data('pagination'); + o.disabled = false; + this.data('pagination', o); + methods._draw.call(this); + return this; + }, + + updateItems: function (newItems) { + var o = this.data('pagination'); + o.items = newItems; + o.pages = methods._getPages(o); + this.data('pagination', o); + methods._draw.call(this); + }, + + updateItemsOnPage: function (itemsOnPage) { + var o = this.data('pagination'); + o.itemsOnPage = itemsOnPage; + o.pages = methods._getPages(o); + this.data('pagination', o); + methods._selectPage.call(this, 0); + return this; + }, + + getItemsOnPage: function() { + return this.data('pagination').itemsOnPage; + }, + + _draw: function() { + var o = this.data('pagination'), + interval = methods._getInterval(o), + i, + tagName; + + methods.destroy.call(this); + + tagName = (typeof this.prop === 'function') ? this.prop('tagName') : this.attr('tagName'); + + var $panel = tagName === 'UL' ? this : $('').appendTo(this); + + // Generate Prev link + if (o.prevText) { + methods._appendItem.call(this, !o.invertPageOrder ? o.currentPage - 1 : o.currentPage + 1, {text: o.prevText, classes: 'prev'}); + } + + // Generate Next link (if option set for at front) + if (o.nextText && o.nextAtFront) { + methods._appendItem.call(this, !o.invertPageOrder ? o.currentPage + 1 : o.currentPage - 1, {text: o.nextText, classes: 'next'}); + } + + // Generate start edges + if (!o.invertPageOrder) { + if (interval.start > 0 && o.edges > 0) { + if(o.useStartEdge) { + var end = Math.min(o.edges, interval.start); + for (i = 0; i < end; i++) { + methods._appendItem.call(this, i); + } + } + if (o.edges < interval.start && (interval.start - o.edges != 1)) { + $panel.append('
  • ' + o.ellipseText + '
  • '); + } else if (interval.start - o.edges == 1) { + methods._appendItem.call(this, o.edges); + } + } + } else { + if (interval.end < o.pages && o.edges > 0) { + if(o.useStartEdge) { + var begin = Math.max(o.pages - o.edges, interval.end); + for (i = o.pages - 1; i >= begin; i--) { + methods._appendItem.call(this, i); + } + } + + if (o.pages - o.edges > interval.end && (o.pages - o.edges - interval.end != 1)) { + $panel.append('
  • ' + o.ellipseText + '
  • '); + } else if (o.pages - o.edges - interval.end == 1) { + methods._appendItem.call(this, interval.end); + } + } + } + + // Generate interval links + if (!o.invertPageOrder) { + for (i = interval.start; i < interval.end; i++) { + methods._appendItem.call(this, i); + } + } else { + for (i = interval.end - 1; i >= interval.start; i--) { + methods._appendItem.call(this, i); + } + } + + // Generate end edges + if (!o.invertPageOrder) { + if (interval.end < o.pages && o.edges > 0) { + if (o.pages - o.edges > interval.end && (o.pages - o.edges - interval.end != 1)) { + $panel.append('
  • ' + o.ellipseText + '
  • '); + } else if (o.pages - o.edges - interval.end == 1) { + methods._appendItem.call(this, interval.end); + } + if(o.useEndEdge) { + var begin = Math.max(o.pages - o.edges, interval.end); + for (i = begin; i < o.pages; i++) { + methods._appendItem.call(this, i); + } + } + } + } else { + if (interval.start > 0 && o.edges > 0) { + if (o.edges < interval.start && (interval.start - o.edges != 1)) { + $panel.append('
  • ' + o.ellipseText + '
  • '); + } else if (interval.start - o.edges == 1) { + methods._appendItem.call(this, o.edges); + } + + if(o.useEndEdge) { + var end = Math.min(o.edges, interval.start); + for (i = end - 1; i >= 0; i--) { + methods._appendItem.call(this, i); + } + } + } + } + + // Generate Next link (unless option is set for at front) + if (o.nextText && !o.nextAtFront) { + methods._appendItem.call(this, !o.invertPageOrder ? o.currentPage + 1 : o.currentPage - 1, {text: o.nextText, classes: 'next'}); + } + + if (o.ellipsePageSet && !o.disabled) { + methods._ellipseClick.call(this, $panel); + } + + }, + + _getPages: function(o) { + var pages = Math.ceil(o.items / o.itemsOnPage); + return pages || 1; + }, + + _getInterval: function(o) { + return { + start: Math.ceil(o.currentPage > o.halfDisplayed ? Math.max(Math.min(o.currentPage - o.halfDisplayed, (o.pages - o.displayedPages)), 0) : 0), + end: Math.ceil(o.currentPage > o.halfDisplayed ? Math.min(o.currentPage + o.halfDisplayed, o.pages) : Math.min(o.displayedPages, o.pages)) + }; + }, + + _appendItem: function(pageIndex, opts) { + var self = this, options, $link, o = self.data('pagination'), $linkWrapper = $('
  • '), $ul = self.find('ul'); + + pageIndex = pageIndex < 0 ? 0 : (pageIndex < o.pages ? pageIndex : o.pages - 1); + + options = { + text: pageIndex + 1, + classes: '' + }; + + if (o.labelMap.length && o.labelMap[pageIndex]) { + options.text = o.labelMap[pageIndex]; + } + + options = $.extend(options, opts || {}); + + if (pageIndex == o.currentPage || o.disabled) { + if (o.disabled || options.classes === 'prev' || options.classes === 'next') { + $linkWrapper.addClass('disabled'); + } else { + $linkWrapper.addClass('active'); + } + $link = $('' + (options.text) + ''); + } else { + if (o.useAnchors) { + $link = $('' + (options.text) + ''); + } else { + $link = $('' + (options.text) + ''); + } + $link.click(function(event){ + return methods._selectPage.call(self, pageIndex, event); + }); + } + + if (options.classes) { + $link.addClass(options.classes); + } + + $linkWrapper.append($link); + + if ($ul.length) { + $ul.append($linkWrapper); + } else { + self.append($linkWrapper); + } + }, + + _selectPage: function(pageIndex, event) { + var o = this.data('pagination'); + o.currentPage = pageIndex; + if (o.selectOnClick) { + methods._draw.call(this); + } + return o.onPageClick(pageIndex + 1, event); + }, + + + _ellipseClick: function($panel) { + var self = this, + o = this.data('pagination'), + $ellip = $panel.find('.ellipse'); + $ellip.addClass('clickable').parent().removeClass('disabled'); + $ellip.click(function(event) { + if (!o.disable) { + var $this = $(this), + val = (parseInt($this.parent().prev().text(), 10) || 0) + 1; + $this + .html('') + .find('input') + .focus() + .click(function(event) { + // prevent input number arrows from bubbling a click event on $ellip + event.stopPropagation(); + }) + .keyup(function(event) { + var val = $(this).val(); + if (event.which === 13 && val !== '') { + // enter to accept + if ((val>0)&&(val<=o.pages)) + methods._selectPage.call(self, val - 1); + } else if (event.which === 27) { + // escape to cancel + $ellip.empty().html(o.ellipseText); + } + }) + .bind('blur', function(event) { + var val = $(this).val(); + if (val !== '') { + methods._selectPage.call(self, val - 1); + } + $ellip.empty().html(o.ellipseText); + return false; + }); + } + return false; + }); + } + + }; + + $.fn.pagination = function(method) { + + // Method calling logic + if (methods[method] && method.charAt(0) != '_') { + return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); + } else if (typeof method === 'object' || !method) { + return methods.init.apply(this, arguments); + } else { + $.error('Method ' + method + ' does not exist on jQuery.pagination'); + } + + }; + +})(jQuery); diff --git a/restconf/sal-rest-docgen/src/main/resources/explorer/lib/odl/list_mounts.js b/restconf/sal-rest-docgen/src/main/resources/explorer/lib/odl/list_mounts.js index bfd561a21e..f4305336fb 100644 --- a/restconf/sal-rest-docgen/src/main/resources/explorer/lib/odl/list_mounts.js +++ b/restconf/sal-rest-docgen/src/main/resources/explorer/lib/odl/list_mounts.js @@ -12,7 +12,7 @@ var loadMountList = function( dom ) { for( var key in myData ) { list.append( "" + + myData[key].id + ", 0, '" + myData[key].instance + "')\">" + myData[key].instance + ""); } dom.empty(); diff --git a/restconf/sal-rest-docgen/src/main/resources/explorer/lib/odl/page.js b/restconf/sal-rest-docgen/src/main/resources/explorer/lib/odl/page.js new file mode 100644 index 0000000000..5dff488257 --- /dev/null +++ b/restconf/sal-rest-docgen/src/main/resources/explorer/lib/odl/page.js @@ -0,0 +1,20 @@ +//constructs a pagination +var loadPagination= function(url, mountIndex, mountPath, dom_id) { + $.ajax( { + url: url + mountIndex + "?totalPages", + datatype: 'jsonp', + success: function( strData ){ + var totalPages = strData; + $(function() { + $('#light-pagination').pagination({ + pages: totalPages, + cssStyle: 'light-theme', + onPageClick: function(pageNumber, event) { + loadSwagger(url + mountIndex + "?pageNum=" + pageNumber, + dom_id, mountIndex, mountPath); + } + }); + }); + } + } ); +} \ No newline at end of file diff --git a/restconf/sal-rest-docgen/src/test/java/org/opendaylight/controller/sal/rest/doc/impl/DocGenTestHelper.java b/restconf/sal-rest-docgen/src/test/java/org/opendaylight/controller/sal/rest/doc/impl/DocGenTestHelper.java index 0b302547c3..af8670c636 100644 --- a/restconf/sal-rest-docgen/src/test/java/org/opendaylight/controller/sal/rest/doc/impl/DocGenTestHelper.java +++ b/restconf/sal-rest-docgen/src/test/java/org/opendaylight/controller/sal/rest/doc/impl/DocGenTestHelper.java @@ -7,6 +7,7 @@ */ package org.opendaylight.controller.sal.rest.doc.impl; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -121,6 +122,7 @@ public class DocGenTestHelper { final UriInfo info = mock(UriInfo.class); when(info.getRequestUriBuilder()).thenReturn(mockBuilder); + when(mockBuilder.replaceQuery(any())).thenReturn(mockBuilder); when(info.getBaseUri()).thenReturn(uri); return info; }