Skip to content

Issue 2049 : Use TokenPaginatedTable for Component Search#438

Open
sahibamittal wants to merge 6 commits intomainfrom
issue-2049-update-component-search-api-integration
Open

Issue 2049 : Use TokenPaginatedTable for Component Search#438
sahibamittal wants to merge 6 commits intomainfrom
issue-2049-update-component-search-api-integration

Conversation

@sahibamittal
Copy link
Copy Markdown
Collaborator

@sahibamittal sahibamittal commented Mar 23, 2026

Description

Replace the legacy bootstrap-table with TokenPaginatedTable and update table props/behaviour to use token-based pagination.

Addressed Issue

Issue: DependencyTrack/hyades#2049
Backend: DependencyTrack/hyades-apiserver#1867

Additional Details

image image

Checklist

Replace the legacy bootstrap-table with TokenPaginatedTable and update table props/behaviour to use token-based pagination.
@sahibamittal sahibamittal requested a review from nscuro March 26, 2026 14:39
@sahibamittal sahibamittal requested a review from nscuro March 31, 2026 16:15
@codacy-production
Copy link
Copy Markdown

codacy-production bot commented Mar 31, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

TIP This summary will be updated as you push new changes. Give us feedback

@nscuro
Copy link
Copy Markdown
Member

nscuro commented Apr 14, 2026

Not sure if that was an issue before, but when clicking the search button I'm getting these warnings:
image

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrates the Portfolio Component Search view from legacy bootstrap-table server pagination to the shared TokenPaginatedTable component, aligning the frontend with the backend’s token-based pagination API (Issue #2049 / hyades-apiserver#1867).

Changes:

  • Replaced <bootstrap-table> usage with <token-paginated-table> and switched to /api/v2/components with query-param based filtering + sorting.
  • Updated search UI/logic (notably HASH search now includes hash-type selection) and refactored query param construction to an object-based approach.
  • Simplified hashes_short_desc translations across multiple locales.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/views/portfolio/components/ComponentSearch.vue Migrates component search results to TokenPaginatedTable; updates filters, sorting, and HASH search UI.
src/i18n/locales/en.json Simplifies hashes_short_desc string.
src/i18n/locales/de.json Simplifies hashes_short_desc string.
src/i18n/locales/es.json Simplifies hashes_short_desc string.
src/i18n/locales/fr.json Simplifies hashes_short_desc string.
src/i18n/locales/hi.json Simplifies hashes_short_desc string.
src/i18n/locales/it.json Simplifies hashes_short_desc string.
src/i18n/locales/ja.json Simplifies hashes_short_desc string.
src/i18n/locales/pl.json Simplifies hashes_short_desc string.
src/i18n/locales/pt.json Simplifies hashes_short_desc string.
src/i18n/locales/pt-BR.json Simplifies hashes_short_desc string.
src/i18n/locales/ru.json Simplifies hashes_short_desc string.
src/i18n/locales/uk-UA.json Simplifies hashes_short_desc string.
src/i18n/locales/zh.json Simplifies hashes_short_desc string.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


return params;
},
performSearch: function () {
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

performSearch() can be triggered via the Enter key handlers even when isSearchDisabled is true (e.g., HASH search without hashType), which will apply incomplete filters and potentially issue invalid/ineffective requests. Add a guard in performSearch() to no-op when isSearchDisabled is true (or remove the Enter handlers / validate required fields inside createQueryParams).

Suggested change
performSearch: function () {
performSearch: function () {
if (this.isSearchDisabled) {
return;
}

Copilot uses AI. Check for mistakes.
Comment on lines +158 to +163
if (this.coordinatesGroup)
params.group = common.trimToNull(this.coordinatesGroup);
if (this.coordinatesName)
params.name = common.trimToNull(this.coordinatesName);
if (this.coordinatesVersion)
params.version = common.trimToNull(this.coordinatesVersion);
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the COORDINATES branch, you check if (this.coordinatesGroup) before trimming. Whitespace-only input is truthy, so common.trimToNull() can yield null, and common.setQueryParams() will serialize it as the literal string "null" (e.g., group=null). Trim first and only add the param if the trimmed value is non-null.

Suggested change
if (this.coordinatesGroup)
params.group = common.trimToNull(this.coordinatesGroup);
if (this.coordinatesName)
params.name = common.trimToNull(this.coordinatesName);
if (this.coordinatesVersion)
params.version = common.trimToNull(this.coordinatesVersion);
let group = common.trimToNull(this.coordinatesGroup);
if (group) params.group = group;
let name = common.trimToNull(this.coordinatesName);
if (name) params.name = name;
let version = common.trimToNull(this.coordinatesVersion);
if (version) params.version = version;

Copilot uses AI. Check for mistakes.
`<a href="${url}">${xssFilters.inHTMLData(value)}</a>`
: `<a href="${url}">${xssFilters.inHTMLData(value)}</a>`;
return (
`<a href="${dependencyGraphUrl}"<i class="fa fa-sitemap" aria-hidden="true" style="float:right; padding-top: 4px; cursor:pointer" data-toggle="tooltip" data-placement="bottom" title="Show in dependency graph"></i></a> ` +
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The component name column formatter generates invalid HTML: the dependency graph link is missing a > after the href attribute (<a href="..."<i ...>). This will break rendering/click behavior and may interfere with tooltips/events. Fix the anchor tag markup so it is valid HTML.

Suggested change
`<a href="${dependencyGraphUrl}"<i class="fa fa-sitemap" aria-hidden="true" style="float:right; padding-top: 4px; cursor:pointer" data-toggle="tooltip" data-placement="bottom" title="Show in dependency graph"></i></a> ` +
`<a href="${dependencyGraphUrl}"><i class="fa fa-sitemap" aria-hidden="true" style="float:right; padding-top: 4px; cursor:pointer" data-toggle="tooltip" data-placement="bottom" title="Show in dependency graph"></i></a> ` +

Copilot uses AI. Check for mistakes.
Comment on lines +26 to +30
placeholder="Select hash type"
v-model="hashType"
:options="hashTypes"
class="mr-2"
></b-form-select>
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<b-form-select> does not support a placeholder prop (BootstrapVue expects a null option like { value: null, text: '…', disabled: true } to emulate a placeholder). As written, the placeholder will be ignored and hashType may remain null while the UI shows the first option, keeping the Search button disabled. Add an explicit placeholder option (or initialize hashType to a valid default) so the model and UI state are consistent.

Suggested change
placeholder="Select hash type"
v-model="hashType"
:options="hashTypes"
class="mr-2"
></b-form-select>
v-model="hashType"
:options="hashTypes"
class="mr-2"
>
<b-form-select-option :value="null" disabled>Select hash type</b-form-select-option>
</b-form-select>

Copilot uses AI. Check for mistakes.
Comment on lines +394 to 409
tableOptions: {
showColumns: true,
showRefresh: true,
pagination: true,
silentSort: false,
toolbar: '#componentSearchToolbar',
sidePagination: 'server',
queryParamsType: 'pageSize',
pageList: '[10, 25, 50, 100]',
pageSize:
localStorage &&
localStorage.getItem('ComponentSearchPageSize') !== null
? Number(localStorage.getItem('ComponentSearchPageSize'))
: 10,
sortName:
localStorage &&
localStorage.getItem('ComponentSearchSortName') !== null
? localStorage.getItem('ComponentSearchSortName')
: undefined,
sortOrder:
localStorage &&
localStorage.getItem('ComponentSearchSortOrder') !== null
? localStorage.getItem('ComponentSearchSortOrder')
: undefined,
icons: {
refresh: 'fa-refresh',
},
//toolbar: '#componentSearchToolbar',
responseHandler: function (res, xhr) {
res.total = xhr.getResponseHeader('X-Total-Count');
return res;
},
url: `${this.$api.BASE_URL}/${this.$api.URL_COMPONENT}/identity`,
onPageChange: (number, size) => {
if (localStorage) {
localStorage.setItem('ComponentSearchPageSize', size.toString());
}
},
onColumnSwitch: (field, checked) => {
if (localStorage) {
localStorage.setItem(
'ComponentSearchShow' + common.capitalize(field),
checked.toString(),
);
}
},
sortName: 'name',
sortOrder: 'asc',
customSort: () => {},
onSort: (name, order) => {
if (localStorage) {
localStorage.setItem('ComponentSearchSortName', name);
localStorage.setItem('ComponentSearchSortOrder', order);
}
this.sortBy = name;
this.sortDirection = order;
},
},
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This migration drops column visibility / sort preference persistence for the ComponentSearch table: the previous onColumnSwitch localStorage writes and loadUserPreferencesForBootstrapTable hook are no longer connected, so user column toggles won't be restored (see loadUserPreferencesForBootstrapTable in src/shared/utils.js:102-129). If TokenPaginatedTable doesn't handle this internally, consider wiring the same persistence behavior via bootstrap-table events (or enhancing TokenPaginatedTable to apply preferences).

Copilot uses AI. Check for mistakes.
Comment on lines +150 to +152
mounted() {
this.changeSearchUrl = true;
},
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changeSearchUrl is now only assigned (beforeMount/mounted) but no longer read anywhere, and tableBaseUrl is declared but unused. Similarly, onPreBody() is no longer wired to any table event after switching to TokenPaginatedTable. Removing these dead fields/methods will avoid confusion and make it clearer how initial searches and URL state are intended to work.

Copilot uses AI. Check for mistakes.
Comment on lines +212 to +220
tableDataBaseUrl() {
if (!this.appliedFilters) return null;
const url = `${this.$api.BASE_URL}/api/v2/components`;
const queryParams = { ...this.appliedFilters };
const sortBy = this.sortBy || 'name';
const sortDirection = this.sortDirection || 'asc';
queryParams.sort_by = sortBy;
queryParams.sort_direction = sortDirection.toUpperCase();
return common.setQueryParams(url, queryParams);
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TokenPaginatedTable calls loadPage(this.baseUrl) on mount and whenever baseUrl changes, but tableDataBaseUrl() returns null until appliedFilters is set. This will result in runtime errors (e.g., url.startsWith in common.setQueryParams) and the table never loading for deep-linked searches (since performSearch() is no longer invoked during initial route-hash parsing). Consider rendering the table only when a non-empty base URL exists (e.g., v-if), and/or initializing appliedFilters during beforeMount/mounted when the route hash provides initial search values.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants