Fixes 23497: Added Search and Sort Functionality at Schema Level (#23907)

* Feat: Added search functionality for schema tables and stored procedures

* Fix: memoize the searchProps of the table component

* Fix: fixed the table pagination issue

* Fix: Added search funtionality in service page

* Playwright: Added test for service page search

* Fix: Fixed the falling unit tests

* Fix: Fixed code smells

* fix: Fixed the current page reset issue

* fix: removed the getSearchPlaceholderKey util function

* fix: fixed the merge conflicts

* "fix: Added sorter on name column in all the tables"

* fix: Added search field in data models and api endpoint tab

* fix: Added search field in files and spreadsheets table

* playwright: Added e2e test for table search

* fix: fixed the failed unit test

* add new field service.fullyQualifiedName.keyword in index mapping

* fix: fixed the table search and sort playwright

* fix: fixed the api endpoint schema playwright test

* fix: fixed tha table search and sort playwright

* fix: removed serial from the table search and sort tests

---------

Co-authored-by: sonikashah <sonikashah94@gmail.com>
Co-authored-by: Aniket Katkar <aniketkatkar97@gmail.com>
This commit is contained in:
Rohit Jain 2025-11-18 10:51:56 +05:30 committed by GitHub
parent d1d2e69d3d
commit 3a9918d23d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
111 changed files with 2411 additions and 319 deletions

View File

@ -410,7 +410,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -542,7 +542,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -318,7 +318,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -557,7 +557,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text",

View File

@ -356,7 +356,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -538,7 +538,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -413,7 +413,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -350,7 +350,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -175,7 +175,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -175,7 +175,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -277,7 +277,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -534,7 +534,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -479,7 +479,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -151,7 +151,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -425,7 +425,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -175,7 +175,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -242,7 +242,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"
@ -338,7 +345,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -274,7 +274,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"
@ -370,7 +377,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -405,7 +405,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -175,7 +175,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -395,7 +395,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -523,7 +523,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -327,7 +327,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -528,7 +528,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -449,7 +449,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -523,7 +523,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -406,7 +406,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -344,7 +344,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -228,7 +228,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -240,7 +240,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -249,7 +249,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -516,7 +516,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -442,7 +442,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -116,7 +116,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -300,7 +300,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -228,7 +228,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -236,7 +236,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"
@ -332,7 +339,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -244,7 +244,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"
@ -340,7 +347,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -449,7 +449,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -298,7 +298,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -427,7 +427,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -560,7 +560,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -336,7 +336,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -567,7 +567,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text",

View File

@ -373,7 +373,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -556,7 +556,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -424,7 +424,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -362,7 +362,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -174,7 +174,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -174,7 +174,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -296,7 +296,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -552,7 +552,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -497,7 +497,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -164,7 +164,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -447,7 +447,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -174,7 +174,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -260,7 +260,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"
@ -356,7 +363,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -284,7 +284,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"
@ -380,7 +387,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -423,7 +423,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -174,7 +174,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -388,7 +388,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -518,7 +518,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -327,7 +327,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -530,7 +530,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -449,7 +449,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -480,7 +480,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -401,7 +401,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -336,7 +336,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -213,7 +213,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -225,7 +225,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -252,7 +252,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text",

View File

@ -510,7 +510,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -441,7 +441,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text",

View File

@ -121,7 +121,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -296,7 +296,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -213,7 +213,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -222,7 +222,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"
@ -318,7 +325,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -398,7 +398,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text",
@ -498,7 +505,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -338,7 +338,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text",

View File

@ -285,7 +285,14 @@
}
},
"fullyQualifiedName": {
"type": "text"
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"normalizer": "lowercase_normalizer",
"ignore_above": 256
}
}
},
"description": {
"type": "text"

View File

@ -0,0 +1,330 @@
/*
* Copyright 2025 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ApiEndpointClass } from '../../support/entity/ApiEndpointClass';
import { DashboardDataModelClass } from '../../support/entity/DashboardDataModelClass';
import { DirectoryClass } from '../../support/entity/DirectoryClass';
import { EntityTypeEndpoint } from '../../support/entity/Entity.interface';
import { EntityDataClass } from '../../support/entity/EntityDataClass';
import { FileClass } from '../../support/entity/FileClass';
import { SpreadsheetClass } from '../../support/entity/SpreadsheetClass';
import { StoredProcedureClass } from '../../support/entity/StoredProcedureClass';
import { TableClass } from '../../support/entity/TableClass';
import { TopicClass } from '../../support/entity/TopicClass';
import {
getApiContext,
redirectToHomePage,
testTableSearch,
} from '../../utils/common';
import { test } from '../fixtures/pages';
test.beforeEach(async ({ page }) => {
await redirectToHomePage(page);
});
test.describe('Table Search', () => {
test.describe('Services page', () => {
test('Services page should have search functionality', async ({ page }) => {
const service1 = EntityDataClass.databaseService.get();
const service2 = EntityDataClass.storedProcedure1.get().service;
await page.goto('/settings/services/databases');
await testTableSearch(
page,
'database_service_search_index',
service1.name,
service2.name
);
});
});
test.describe('API Collection page', () => {
test('API Collection page should have search functionality', async ({
page,
}) => {
const { afterAction, apiContext } = await getApiContext(page);
const apiEndpoint1 = EntityDataClass.apiEndpoint1.get();
const apiEndpoint2 = new ApiEndpointClass();
apiEndpoint2.service.name = apiEndpoint1.service.name;
apiEndpoint2.apiCollection.name = apiEndpoint1.apiCollection.name;
apiEndpoint2.apiCollection.service = apiEndpoint1.service.name;
apiEndpoint2.entity.apiCollection = `${apiEndpoint1.service.name}.${apiEndpoint1.apiCollection.name}`;
const response = await apiContext.post(
`/api/v1/${EntityTypeEndpoint.API_ENDPOINT}`,
{
data: apiEndpoint2.entity,
}
);
apiEndpoint2.entityResponseData = await response.json();
await page.goto(
`/apiCollection/${apiEndpoint1.apiCollection.fullyQualifiedName}`
);
await testTableSearch(
page,
'api_endpoint_search_index',
apiEndpoint1.entity.name,
apiEndpoint2.entity.name
);
await afterAction();
});
});
test.describe('Database Schema Tables tab', () => {
test('Database Schema Tables tab should have search functionality', async ({
page,
}) => {
const { afterAction, apiContext } = await getApiContext(page);
const table1 = EntityDataClass.table1.get();
const table2 = new TableClass();
table2.service.name = table1.service.name;
table2.database.name = table1.database.name;
table2.database.service = table1.service.name;
table2.schema.name = table1.schema.name;
table2.schema.database = `${table1.service.name}.${table1.database.name}`;
table2.entity.databaseSchema = table1.schema.fullyQualifiedName;
const response = await apiContext.post(
`/api/v1/${EntityTypeEndpoint.Table}`,
{
data: table2.entity,
}
);
table2.entityResponseData = await response.json();
await page.goto(`/databaseSchema/${table1.schema.fullyQualifiedName}`);
await testTableSearch(
page,
'table_search_index',
table1.entity.name,
table2.entity.name
);
await afterAction();
});
});
test.describe('Data Models Table', () => {
test('Data Models Table should have search functionality', async ({
page,
}) => {
const { afterAction, apiContext } = await getApiContext(page);
const dataModel1 = EntityDataClass.dashboardDataModel1.get();
const dataModel2 = new DashboardDataModelClass();
dataModel2.service.name = dataModel1.service.name;
dataModel2.entity.service = dataModel1.service.fullyQualifiedName;
const response = await apiContext.post(
`/api/v1/${EntityTypeEndpoint.DataModel}`,
{
data: dataModel2.entity,
}
);
dataModel2.entityResponseData = await response.json();
await page.goto(
`/service/dashboardServices/${dataModel1.service.name}/data-model`
);
await testTableSearch(
page,
'dashboard_data_model_search_index',
dataModel1.entity.name,
dataModel2.entity.name
);
await afterAction();
});
});
test.describe('Stored Procedure Table', () => {
test('Stored Procedure Table should have search functionality', async ({
page,
}) => {
const { afterAction, apiContext } = await getApiContext(page);
const storedProcedure1 = EntityDataClass.storedProcedure1.get();
const storedProcedure2 = new StoredProcedureClass();
storedProcedure2.service.name = storedProcedure1.service.name;
storedProcedure2.database.name = storedProcedure1.database.name;
storedProcedure2.database.service = storedProcedure1.service.name;
storedProcedure2.schema.name = storedProcedure1.schema.name;
storedProcedure2.schema.database = `${storedProcedure1.service.name}.${storedProcedure1.database.name}`;
storedProcedure2.entity.databaseSchema =
storedProcedure1.schema.fullyQualifiedName;
const response = await apiContext.post(
`/api/v1/${EntityTypeEndpoint.StoreProcedure}`,
{
data: storedProcedure2.entity,
}
);
storedProcedure2.entityResponseData = await response.json();
await page.goto(
`/databaseSchema/${storedProcedure1.schema.fullyQualifiedName}/stored_procedure`
);
await testTableSearch(
page,
'stored_procedure_search_index',
storedProcedure1.entity.name,
storedProcedure2.entity.name
);
await afterAction();
});
});
test.describe('Topics Table', () => {
test('Topics Table should have search functionality', async ({ page }) => {
const { afterAction, apiContext } = await getApiContext(page);
const topic1 = EntityDataClass.topic1.get();
const topic2 = new TopicClass();
topic2.service.name = topic1.service.name;
topic2.entity.service = topic1.service.fullyQualifiedName;
const response = await apiContext.post(
`/api/v1/${EntityTypeEndpoint.Topic}`,
{
data: topic2.entity,
}
);
topic2.entityResponseData = await response.json();
await page.goto(
`/service/messagingServices/${topic1.service.name}/topics`
);
await testTableSearch(
page,
'topic_search_index',
topic1.entity.name,
topic2.entity.name
);
await afterAction();
});
});
test.describe('Drives Service Directories Table', () => {
test('Drives Service Directories Table should have search functionality', async ({
page,
}) => {
const { afterAction, apiContext } = await getApiContext(page);
const directory1 = EntityDataClass.directory1.get();
const directory2 = new DirectoryClass();
directory2.service.name = directory1.service.name;
directory2.entity.service = directory1.service.fullyQualifiedName;
const response = await apiContext.post(
`/api/v1/${EntityTypeEndpoint.Directory}`,
{
data: {
name: directory2.entity.name,
description: directory2.entity.description,
service: directory2.entity.service,
},
}
);
directory2.entityResponseData = await response.json();
await page.goto(
`/service/driveServices/${directory1.service.name}/directories`
);
await testTableSearch(
page,
'directory_search_index',
directory1.entity.name,
directory2.entity.name
);
await afterAction();
});
});
test.describe('Drives Service Files Table', () => {
test('Drives Service Files Table should have search functionality', async ({
page,
}) => {
const { afterAction, apiContext } = await getApiContext(page);
const file1 = EntityDataClass.file1.get();
const file2 = new FileClass();
file2.service.name = file1.service.name;
file2.entity.service = file1.service.fullyQualifiedName;
const response = await apiContext.post(
`/api/v1/${EntityTypeEndpoint.File}`,
{
data: {
name: file2.entity.name,
description: file2.entity.description,
service: file2.entity.service,
},
}
);
file2.entityResponseData = await response.json();
await page.goto(`/service/driveServices/${file1.service.name}/files`);
await testTableSearch(
page,
'file_search_index',
file1.entity.name,
file2.entity.name
);
await afterAction();
});
});
test.describe('Drives Service Spreadsheets Table', () => {
test('Drives Service Spreadsheets Table should have search functionality', async ({
page,
}) => {
const { afterAction, apiContext } = await getApiContext(page);
const spreadsheet1 = EntityDataClass.spreadsheet1.get();
const spreadsheet2 = new SpreadsheetClass();
spreadsheet2.service.name = spreadsheet1.service.name;
spreadsheet2.entity.service = spreadsheet1.service.fullyQualifiedName;
const response = await apiContext.post(
`/api/v1/${EntityTypeEndpoint.Spreadsheet}`,
{
data: {
name: spreadsheet2.entity.name,
description: spreadsheet2.entity.description,
service: spreadsheet2.entity.service,
},
}
);
spreadsheet2.entityResponseData = await response.json();
await page.goto(
`/service/driveServices/${spreadsheet1.service.name}/spreadsheets`
);
await testTableSearch(
page,
'spreadsheet_search_index',
spreadsheet1.entity.name,
spreadsheet2.entity.name
);
await afterAction();
});
});
});

View File

@ -0,0 +1,297 @@
/*
* Copyright 2025 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ApiEndpointClass } from '../../support/entity/ApiEndpointClass';
import { DashboardDataModelClass } from '../../support/entity/DashboardDataModelClass';
import { DatabaseSchemaClass } from '../../support/entity/DatabaseSchemaClass';
import { EntityTypeEndpoint } from '../../support/entity/Entity.interface';
import { EntityDataClass } from '../../support/entity/EntityDataClass';
import { FileClass } from '../../support/entity/FileClass';
import { SpreadsheetClass } from '../../support/entity/SpreadsheetClass';
import { StoredProcedureClass } from '../../support/entity/StoredProcedureClass';
import { TableClass } from '../../support/entity/TableClass';
import { TopicClass } from '../../support/entity/TopicClass';
import {
getApiContext,
redirectToHomePage,
testTableSorting,
} from '../../utils/common';
import { test } from '../fixtures/pages';
test.beforeEach(async ({ page }) => {
await redirectToHomePage(page);
});
test.describe('Table Sorting', () => {
test.describe('Database Schema page', () => {
test('Database Schema page should have sorting on name column', async ({
page,
}) => {
const { afterAction, apiContext } = await getApiContext(page);
const database = EntityDataClass.database.get();
const schema = new DatabaseSchemaClass();
schema.service.name = database.service.name;
schema.database.name = database.entity.name;
schema.database.service = database.service.name;
schema.entity.database = database.entity.fullyQualifiedName;
const schemaResponse = await apiContext.post(
`/api/v1/${EntityTypeEndpoint.DatabaseSchema}`,
{
data: schema.entity,
}
);
schema.entityResponseData = await schemaResponse.json();
await page.goto(`/database/${database.entity.fullyQualifiedName}`);
await testTableSorting(page, 'Name');
await afterAction();
});
});
test('Services page should have sorting on name column', async ({ page }) => {
await page.goto('/settings/services/databases');
await testTableSorting(page, 'Name');
});
test.describe('API Endpoint page', () => {
test('API Endpoint page should have sorting on name column', async ({
page,
}) => {
const { afterAction, apiContext } = await getApiContext(page);
const apiEndpoint1 = EntityDataClass.apiEndpoint1.get();
const apiEndpoint2 = new ApiEndpointClass();
apiEndpoint2.service.name = apiEndpoint1.service.name;
apiEndpoint2.apiCollection.name = apiEndpoint1.apiCollection.name;
apiEndpoint2.apiCollection.service = apiEndpoint1.service.name;
apiEndpoint2.entity.apiCollection = `${apiEndpoint1.service.name}.${apiEndpoint1.apiCollection.name}`;
const apiEndpoint2Response = await apiContext.post(
`/api/v1/${EntityTypeEndpoint.API_ENDPOINT}`,
{
data: apiEndpoint2.entity,
}
);
apiEndpoint2.entityResponseData = await apiEndpoint2Response.json();
await page.goto(
`/apiCollection/${apiEndpoint1.apiCollection.fullyQualifiedName}`
);
await testTableSorting(page, 'Name');
await afterAction();
});
});
test.describe('API Endpoint schema', () => {
test('API Endpoint schema should have sorting on name column', async ({
page,
}) => {
const apiEndpoint = EntityDataClass.apiEndpoint1.get();
await page.goto(`/apiEndpoint/${apiEndpoint.entity.fullyQualifiedName}`);
await testTableSorting(page, 'Name');
});
});
test.describe('Database Schema Tables tab', () => {
test('Database Schema Tables tab should have sorting on name column', async ({
page,
}) => {
const { afterAction, apiContext } = await getApiContext(page);
const table1 = EntityDataClass.table1.get();
const table2 = new TableClass();
table2.service.name = table1.service.name;
table2.database.name = table1.database.name;
table2.database.service = table1.service.name;
table2.schema.name = table1.schema.name;
table2.schema.database = `${table1.service.name}.${table1.database.name}`;
table2.entity.databaseSchema = table1.schema.fullyQualifiedName;
const table2Response = await apiContext.post(
`/api/v1/${EntityTypeEndpoint.Table}`,
{
data: table2.entity,
}
);
table2.entityResponseData = await table2Response.json();
await page.goto(`/databaseSchema/${table1.schema.fullyQualifiedName}`);
await testTableSorting(page, 'Name');
await afterAction();
});
});
test('Data Observability services page should have sorting on name column', async ({
page,
}) => {
await page.goto('/settings/services/dataObservability?tab=pipelines');
await testTableSorting(page, 'Name');
});
test.describe('Data Models Table', () => {
test('Data Models Table should have sorting on name column', async ({
page,
}) => {
const { afterAction, apiContext } = await getApiContext(page);
const dataModel1 = EntityDataClass.dashboardDataModel1.get();
const dataModel2 = new DashboardDataModelClass();
dataModel2.service.name = dataModel1.service.name;
dataModel2.entity.service = dataModel1.service.fullyQualifiedName;
const dataModel2Response = await apiContext.post(
`/api/v1/${EntityTypeEndpoint.DataModel}`,
{
data: dataModel2.entity,
}
);
dataModel2.entityResponseData = await dataModel2Response.json();
await page.goto(
`/service/dashboardServices/${dataModel1.service.name}/data-model`
);
await testTableSorting(page, 'Name');
await afterAction();
});
});
test.describe('Stored Procedure Table', () => {
test('Stored Procedure Table should have sorting on name column', async ({
page,
}) => {
const { afterAction, apiContext } = await getApiContext(page);
const storedProcedure1 = EntityDataClass.storedProcedure1.get();
const storedProcedure2 = new StoredProcedureClass();
storedProcedure2.service.name = storedProcedure1.service.name;
storedProcedure2.database.name = storedProcedure1.database.name;
storedProcedure2.database.service = storedProcedure1.service.name;
storedProcedure2.schema.name = storedProcedure1.schema.name;
storedProcedure2.schema.database = `${storedProcedure1.service.name}.${storedProcedure1.database.name}`;
storedProcedure2.entity.databaseSchema =
storedProcedure1.schema.fullyQualifiedName;
const storedProcedure2Response = await apiContext.post(
`/api/v1/${EntityTypeEndpoint.StoreProcedure}`,
{
data: storedProcedure2.entity,
}
);
storedProcedure2.entityResponseData =
await storedProcedure2Response.json();
await page.goto(
`/databaseSchema/${storedProcedure1.schema.fullyQualifiedName}/stored_procedure`
);
await testTableSorting(page, 'Name');
await afterAction();
});
});
test.describe('Topics Table', () => {
test('Topics Table should have sorting on name column', async ({
page,
}) => {
const { afterAction, apiContext } = await getApiContext(page);
const topic1 = EntityDataClass.topic1.get();
const topic2 = new TopicClass();
topic2.service.name = topic1.service.name;
topic2.entity.service = topic1.service.fullyQualifiedName;
const topic2Response = await apiContext.post(
`/api/v1/${EntityTypeEndpoint.Topic}`,
{
data: topic2.entity,
}
);
topic2.entityResponseData = await topic2Response.json();
await page.goto(
`/service/messagingServices/${topic1.service.name}/topics`
);
await testTableSorting(page, 'Name');
await afterAction();
});
});
test.describe('Drives Service Files Table', () => {
test('Drives Service Files Table should have sorting on name column', async ({
page,
}) => {
const { afterAction, apiContext } = await getApiContext(page);
const file1 = EntityDataClass.file1.get();
const file2 = new FileClass();
file2.service.name = file1.service.name;
file2.entity.service = file1.service.fullyQualifiedName;
const file2Response = await apiContext.post(
`/api/v1/${EntityTypeEndpoint.File}`,
{
data: {
name: file2.entity.name,
description: file2.entity.description,
service: file2.entity.service,
},
}
);
file2.entityResponseData = await file2Response.json();
await page.goto(`/service/driveServices/${file1.service.name}/files`);
await testTableSorting(page, 'Name');
await afterAction();
});
});
test.describe('Drives Service Spreadsheets Table', () => {
test('Drives Service Spreadsheets Table should have sorting on name column', async ({
page,
}) => {
const { afterAction, apiContext } = await getApiContext(page);
const spreadsheet1 = EntityDataClass.spreadsheet1.get();
const spreadsheet2 = new SpreadsheetClass();
spreadsheet2.service.name = spreadsheet1.service.name;
spreadsheet2.entity.service = spreadsheet1.service.fullyQualifiedName;
const spreadsheet2Response = await apiContext.post(
`/api/v1/${EntityTypeEndpoint.Spreadsheet}`,
{
data: {
name: spreadsheet2.entity.name,
description: spreadsheet2.entity.description,
service: spreadsheet2.entity.service,
},
}
);
spreadsheet2.entityResponseData = await spreadsheet2Response.json();
await page.goto(
`/service/driveServices/${spreadsheet1.service.name}/spreadsheets`
);
await testTableSorting(page, 'Name');
await afterAction();
});
});
});

View File

@ -116,7 +116,7 @@ export class ApiEndpointClass extends EntityClass {
};
this.apiEndpointName = `pw-api-endpoint-${uuid()}`;
this.fqn = `${this.service.name}.${this.apiCollection.name}.${this.apiEndpointName}`;
this.fqn = `${this.service.name}.${this.apiCollection.name}.${this.apiEndpointName}.requestSchema`;
this.children = [
{
@ -223,7 +223,7 @@ export class ApiEndpointClass extends EntityClass {
this.serviceType = ServiceTypes.API_SERVICES;
this.type = 'ApiEndpoint';
this.childrenTabId = 'schema';
this.childrenSelectorId = this.children[0].name;
this.childrenSelectorId = this.children[0].fullyQualifiedName;
}
async create(apiContext: APIRequestContext) {

View File

@ -186,9 +186,13 @@ export class StoredProcedureClass extends EntityClass {
public set(data: {
entity: ResponseDataWithServiceType;
service: ResponseDataType;
database: ResponseDataWithServiceType;
schema: ResponseDataWithServiceType;
}): void {
this.entityResponseData = data.entity;
this.serviceResponseData = data.service;
this.databaseResponseData = data.database;
this.schemaResponseData = data.schema;
}
async visitEntityPage(page: Page) {

View File

@ -74,6 +74,10 @@ export class ApiServiceClass extends EntityClass {
return this.entityResponseData;
}
set(data: ResponseDataType) {
this.entityResponseData = data;
}
async visitEntityPage(page: Page) {
await visitServiceDetailsPage(
page,

View File

@ -133,6 +133,10 @@ export class DashboardServiceClass extends EntityClass {
return this.entityResponseData;
}
set(data: ResponseDataType) {
this.entityResponseData = data;
}
async visitEntityPage(page: Page) {
await visitServiceDetailsPage(
page,

View File

@ -89,6 +89,10 @@ export class DatabaseServiceClass extends EntityClass {
return this.entityResponseData;
}
set(data: ResponseDataType) {
this.entityResponseData = data;
}
async visitEntityPage(page: Page) {
await visitServiceDetailsPage(
page,

View File

@ -92,6 +92,10 @@ export class DriveServiceClass extends EntityClass {
return this.entityResponseData;
}
set(data: ResponseDataType) {
this.entityResponseData = data;
}
async visitEntityPage(page: Page) {
await visitServiceDetailsPage(
page,

View File

@ -79,6 +79,10 @@ export class MessagingServiceClass extends EntityClass {
return this.entityResponseData;
}
set(data: ResponseDataType) {
this.entityResponseData = data;
}
async visitEntityPage(page: Page) {
await visitServiceDetailsPage(
page,

View File

@ -708,3 +708,64 @@ export const testPaginationNavigation = async (
expect(paginationTextContent).toMatch(/2\s*of\s*\d+/);
};
export const testTableSorting = async (page: Page, columnHeader: string) => {
await waitForAllLoadersToDisappear(page);
await page.waitForLoadState('networkidle');
const header = page.locator(`th:has-text("${columnHeader}")`).first();
const visibleRowSelector = `tbody tr:not([aria-hidden="true"])`;
const getFirstCellValue = async () => {
const firstCell = page.locator(`${visibleRowSelector} td`).first();
await firstCell.waitFor({ state: 'visible' });
return (await firstCell.textContent())?.trim();
};
const rowCount = await page.locator(visibleRowSelector).count();
if (rowCount <= 1) {
return;
}
const initialValue = await getFirstCellValue();
await header.click();
await header.click();
const afterFirstClickValue = await getFirstCellValue();
expect(afterFirstClickValue).not.toBe(initialValue);
await header.click();
const afterSecondClickValue = await getFirstCellValue();
expect(afterSecondClickValue).not.toBe(afterFirstClickValue);
};
export const testTableSearch = async (
page: Page,
searchIndex: string,
searchTerm: string,
notVisibleText: string
) => {
await waitForAllLoadersToDisappear(page);
await page.waitForLoadState('networkidle');
await expect(page.getByText(searchTerm).first()).toBeVisible();
await expect(page.getByText(notVisibleText).first()).toBeVisible();
const waitForSearchResponse = page.waitForResponse(
`/api/v1/search/query?q=*index=${searchIndex}*`
);
await page.getByTestId('searchbar').fill(searchTerm);
await waitForSearchResponse;
await waitForAllLoadersToDisappear(page);
await page.waitForLoadState('networkidle');
await expect(page.getByText(searchTerm).first()).toBeVisible();
await expect(page.getByText(notVisibleText).first()).not.toBeVisible();
};

View File

@ -35,7 +35,7 @@ import {
import { APISchema } from '../../../generated/type/apiSchema';
import { TagLabel } from '../../../generated/type/tagLabel';
import { useApplicationStore } from '../../../hooks/useApplicationStore';
import { getEntityName } from '../../../utils/EntityUtils';
import { getColumnSorter, getEntityName } from '../../../utils/EntityUtils';
import { getVersionedSchema } from '../../../utils/SchemaVersionUtils';
import { columnFilterIcon } from '../../../utils/TableColumn.util';
import {
@ -285,6 +285,7 @@ const APIEndpointSchema: FC<APIEndpointSchemaProps> = ({
key: TABLE_COLUMNS_KEYS.NAME,
fixed: 'left',
width: 220,
sorter: getColumnSorter<Field, 'name'>('name'),
render: renderSchemaName,
},
{
@ -417,7 +418,7 @@ const APIEndpointSchema: FC<APIEndpointSchemaProps> = ({
}
key={viewType}
pagination={false}
rowKey="name"
rowKey="fullyQualifiedName"
scroll={TABLE_SCROLL_VALUE}
size="small"
staticVisibleColumns={COMMON_STATIC_TABLE_VISIBLE_COLUMNS}

View File

@ -14,12 +14,14 @@
import { Switch, Typography } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import { AxiosError } from 'axios';
import { isUndefined } from 'lodash';
import { isEmpty, isUndefined } from 'lodash';
import QueryString from 'qs';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import {
INITIAL_PAGING_VALUE,
PAGE_SIZE,
PAGE_SIZE_BASE,
pagingObject,
} from '../../../../constants/constants';
@ -30,15 +32,25 @@ import {
TABLE_COLUMNS_KEYS,
} from '../../../../constants/TableKeys.constants';
import { EntityType } from '../../../../enums/entity.enum';
import { SearchIndex } from '../../../../enums/search.enum';
import { Include } from '../../../../generated/type/include';
import { Paging } from '../../../../generated/type/paging';
import { usePaging } from '../../../../hooks/paging/usePaging';
import useCustomLocation from '../../../../hooks/useCustomLocation/useCustomLocation';
import { useFqn } from '../../../../hooks/useFqn';
import { useTableFilters } from '../../../../hooks/useTableFilters';
import { ServicePageData } from '../../../../pages/ServiceDetailsPage/ServiceDetailsPage.interface';
import { getDataModels } from '../../../../rest/dashboardAPI';
import { searchQuery } from '../../../../rest/searchAPI';
import { buildSchemaQueryFilter } from '../../../../utils/DatabaseSchemaDetailsUtils';
import { commonTableFields } from '../../../../utils/DatasetDetailsUtils';
import { getEntityName } from '../../../../utils/EntityUtils';
import {
getColumnSorter,
getEntityName,
highlightSearchText,
} from '../../../../utils/EntityUtils';
import { getEntityDetailsPath } from '../../../../utils/RouterUtils';
import { stringToHTML } from '../../../../utils/StringsUtils';
import {
dataProductTableObject,
domainTableObject,
@ -57,6 +69,7 @@ const DataModelTable = ({
handleShowDeleted,
}: DataModelTableProps) => {
const { t } = useTranslation();
const location = useCustomLocation();
const { fqn } = useFqn();
const [dataModels, setDataModels] = useState<Array<ServicePageData>>();
const {
@ -70,6 +83,50 @@ const DataModelTable = ({
showPagination,
} = usePaging();
const [isLoading, setIsLoading] = useState(true);
const { setFilters } = useTableFilters({});
const searchValue = useMemo(() => {
const param = location.search;
const searchData = QueryString.parse(
param.startsWith('?') ? param.substring(1) : param
);
return searchData.dataModel as string | undefined;
}, [location.search]);
const searchDataModels = useCallback(
async (searchValue: string, pageNumber = INITIAL_PAGING_VALUE) => {
setIsLoading(true);
handlePageChange(pageNumber, {
cursorType: null,
cursorValue: undefined,
});
try {
const response = await searchQuery({
query: '',
pageNumber,
pageSize: PAGE_SIZE,
queryFilter: buildSchemaQueryFilter(
'service.fullyQualifiedName.keyword',
fqn,
searchValue
),
searchIndex: SearchIndex.DASHBOARD_DATA_MODEL,
includeDeleted: showDeleted,
trackTotalHits: true,
});
const data = response.hits.hits.map((model) => model._source);
const total = response.hits.total.value;
setDataModels(data);
handlePagingChange({ total });
} catch (error) {
showErrorToast(error as AxiosError);
} finally {
setIsLoading(false);
}
},
[fqn, showDeleted, handlePagingChange]
);
const tableColumn: ColumnsType<ServicePageData> = useMemo(
() => [
@ -78,6 +135,7 @@ const DataModelTable = ({
dataIndex: TABLE_COLUMNS_KEYS.NAME,
key: TABLE_COLUMNS_KEYS.NAME,
width: 300,
sorter: getColumnSorter<ServicePageData, 'name'>('name'),
render: (_, record: ServicePageData) => {
const dataModelDisplayName = getEntityName(record);
@ -90,7 +148,9 @@ const DataModelTable = ({
EntityType.DASHBOARD_DATA_MODEL,
record.fullyQualifiedName || ''
)}>
{dataModelDisplayName}
{stringToHTML(
highlightSearchText(dataModelDisplayName, searchValue)
)}
</Link>
</div>
);
@ -123,7 +183,7 @@ const DataModelTable = ({
...dataProductTableObject<ServicePageData>(),
...tagTableObject<ServicePageData>(),
],
[]
[searchValue, t]
);
const fetchDashboardsDataModel = useCallback(
@ -154,7 +214,10 @@ const DataModelTable = ({
cursorType,
currentPage,
}) => {
if (cursorType) {
if (searchValue) {
searchDataModels(searchValue, currentPage);
handlePageChange(currentPage);
} else if (cursorType) {
fetchDashboardsDataModel({ [cursorType]: paging[cursorType] });
handlePageChange(
currentPage,
@ -164,6 +227,19 @@ const DataModelTable = ({
}
};
const onDataModelSearch = useCallback(
(value: string) => {
setFilters({ dataModel: isEmpty(value) ? undefined : value });
if (value) {
searchDataModels(value);
} else {
fetchDashboardsDataModel();
handlePageChange(INITIAL_PAGING_VALUE);
}
},
[searchDataModels, fetchDashboardsDataModel]
);
const handleShowDeletedChange = (checked: boolean) => {
handleShowDeleted(checked);
handlePageChange(
@ -183,12 +259,25 @@ const DataModelTable = ({
}
}, [pageSize, showDeleted, pagingCursor]);
const searchProps = useMemo(
() => ({
placeholder: t('label.search-for-type', {
type: t('label.data-model'),
}),
typingInterval: 500,
searchValue: searchValue,
onSearch: onDataModelSearch,
}),
[onDataModelSearch, searchValue, t]
);
return (
<Table
columns={tableColumn}
customPaginationProps={{
currentPage,
isLoading,
isNumberBased: Boolean(searchValue),
pageSize,
paging,
pagingHandler: handleDataModelPageChange,
@ -218,6 +307,7 @@ const DataModelTable = ({
pagination={false}
rowKey="id"
scroll={TABLE_SCROLL_VALUE}
searchProps={searchProps}
size="small"
staticVisibleColumns={COMMON_STATIC_TABLE_VISIBLE_COLUMNS}
/>

View File

@ -21,6 +21,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
INITIAL_PAGING_VALUE,
INITIAL_TABLE_FILTERS,
PAGE_SIZE,
} from '../../../../constants/constants';
import { DATABASE_SCHEMAS_DUMMY_DATA } from '../../../../constants/Database.constants';
@ -41,20 +42,22 @@ import { Paging } from '../../../../generated/type/paging';
import { usePaging } from '../../../../hooks/paging/usePaging';
import useCustomLocation from '../../../../hooks/useCustomLocation/useCustomLocation';
import { useFqn } from '../../../../hooks/useFqn';
import { useTableFilters } from '../../../../hooks/useTableFilters';
import {
getDatabaseSchemas,
patchDatabaseSchemaDetails,
} from '../../../../rest/databaseAPI';
import { searchQuery } from '../../../../rest/searchAPI';
import { buildSchemaQueryFilter } from '../../../../utils/DatabaseSchemaDetailsUtils';
import { commonTableFields } from '../../../../utils/DatasetDetailsUtils';
import { getBulkEditButton } from '../../../../utils/EntityBulkEdit/EntityBulkEditUtils';
import {
getColumnSorter,
getEntityBulkEditPath,
highlightSearchText,
} from '../../../../utils/EntityUtils';
import { t } from '../../../../utils/i18next/LocalUtil';
import { getEntityDetailsPath } from '../../../../utils/RouterUtils';
import { getTermQuery } from '../../../../utils/SearchUtils';
import { stringToHTML } from '../../../../utils/StringsUtils';
import {
dataProductTableObject,
@ -83,8 +86,12 @@ export const DatabaseSchemaTable = ({
const { permissions } = usePermissionProvider();
const [schemas, setSchemas] = useState<DatabaseSchema[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [showDeletedSchemas, setShowDeletedSchemas] = useState<boolean>(false);
const { data } = useGenericContext<Database>();
const { filters: tableFilters, setFilters } = useTableFilters(
INITIAL_TABLE_FILTERS
);
const { showDeletedTables: showDeletedSchemas } = tableFilters;
const { deleted: isDatabaseDeleted } = data ?? {};
@ -143,50 +150,42 @@ export const DatabaseSchemaTable = ({
[pageSize, decodedDatabaseFQN, showDeletedSchemas]
);
const searchSchema = async (
searchValue: string,
pageNumber = INITIAL_PAGING_VALUE
) => {
setIsLoading(true);
try {
handlePageChange(INITIAL_PAGING_VALUE, {
const searchSchema = useCallback(
async (searchValue: string, pageNumber = INITIAL_PAGING_VALUE) => {
setIsLoading(true);
handlePageChange(pageNumber, {
cursorType: null,
cursorValue: undefined,
});
const response = await searchQuery({
query: '',
pageNumber,
pageSize: PAGE_SIZE,
queryFilter: getTermQuery(
{ 'database.fullyQualifiedName': decodedDatabaseFQN },
'must',
undefined,
searchValue
? {
wildcardShouldQueries: {
'name.keyword': `*${searchValue}*`,
'description.keyword': `*${searchValue}*`,
},
}
: undefined
),
searchIndex: SearchIndex.DATABASE_SCHEMA,
includeDeleted: showDeletedSchemas,
trackTotalHits: true,
});
const data = response.hits.hits.map((schema) => schema._source);
const total = response.hits.total.value;
setSchemas(data);
handlePagingChange({ total });
} catch (error) {
showErrorToast(error as AxiosError);
} finally {
setIsLoading(false);
}
};
try {
const response = await searchQuery({
query: '',
pageNumber,
pageSize: PAGE_SIZE,
queryFilter: buildSchemaQueryFilter(
'database.fullyQualifiedName',
decodedDatabaseFQN,
searchValue
),
searchIndex: SearchIndex.DATABASE_SCHEMA,
includeDeleted: showDeletedSchemas,
trackTotalHits: true,
});
const data = response.hits.hits.map((schema) => schema._source);
const total = response.hits.total.value;
setSchemas(data);
handlePagingChange({ total });
} catch (error) {
showErrorToast(error as AxiosError);
} finally {
setIsLoading(false);
}
},
[decodedDatabaseFQN, showDeletedSchemas, handlePagingChange]
);
const handleShowDeletedSchemas = useCallback((value: boolean) => {
setShowDeletedSchemas(value);
setFilters({ showDeletedTables: value });
handlePageChange(INITIAL_PAGING_VALUE, {
cursorType: null,
cursorValue: undefined,
@ -210,18 +209,18 @@ export const DatabaseSchemaTable = ({
[paging, fetchDatabaseSchema, searchSchema, searchValue]
);
const onSchemaSearch = (value: string) => {
navigate({
search: QueryString.stringify({
schema: isEmpty(value) ? undefined : value,
}),
});
if (value) {
searchSchema(value);
} else {
fetchDatabaseSchema();
}
};
const onSchemaSearch = useCallback(
(value: string) => {
setFilters({ schema: isEmpty(value) ? undefined : value });
if (value) {
searchSchema(value);
} else {
fetchDatabaseSchema();
handlePageChange(INITIAL_PAGING_VALUE);
}
},
[setFilters, searchSchema, fetchDatabaseSchema]
);
const handleDisplayNameUpdate = useCallback(
async (data: EntityName, id?: string) => {
@ -256,6 +255,7 @@ export const DatabaseSchemaTable = ({
dataIndex: TABLE_COLUMNS_KEYS.NAME,
key: TABLE_COLUMNS_KEYS.NAME,
width: 250,
sorter: getColumnSorter<DatabaseSchema, 'name'>('name'),
render: (_, record: DatabaseSchema) => (
<DisplayName
displayName={stringToHTML(
@ -336,6 +336,18 @@ export const DatabaseSchemaTable = ({
pagingCursor,
]);
const searchProps = useMemo(
() => ({
placeholder: t('label.search-for-type', {
type: t('label.schema'),
}),
typingInterval: 500,
searchValue: searchValue,
onSearch: onSchemaSearch,
}),
[onSchemaSearch, searchValue]
);
return (
<Table
columns={schemaTableColumns}
@ -377,14 +389,7 @@ export const DatabaseSchemaTable = ({
pagination={false}
rowKey="id"
scroll={TABLE_SCROLL_VALUE}
searchProps={{
placeholder: t('label.search-for-type', {
type: t('label.schema'),
}),
searchValue: searchValue,
typingInterval: 500,
onSearch: onSchemaSearch,
}}
searchProps={searchProps}
size="small"
staticVisibleColumns={COMMON_STATIC_TABLE_VISIBLE_COLUMNS}
/>

View File

@ -10,6 +10,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { PagingWithoutTotal } from 'Models';
import { Dispatch, SetStateAction } from 'react';
import { File } from '../../../../generated/entity/data/file';
import { UsePagingInterface } from '../../../../hooks/paging/usePaging';
import { PagingHandlerParams } from '../../../common/NextPrevious/NextPrevious.interface';
@ -21,4 +23,8 @@ export interface FilesTableProps {
handlePageChange: (data: PagingHandlerParams) => void;
files: File[];
isLoading: boolean;
fetchFiles: (paging?: PagingWithoutTotal) => void;
setFiles: Dispatch<SetStateAction<File[]>>;
setIsLoading: Dispatch<SetStateAction<boolean>>;
serviceFqn: string;
}

View File

@ -12,12 +12,15 @@
*/
import { Switch, Typography } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import { isUndefined } from 'lodash';
import { useMemo } from 'react';
import { AxiosError } from 'axios';
import { isEmpty, isUndefined } from 'lodash';
import QueryString from 'qs';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import {
INITIAL_PAGING_VALUE,
PAGE_SIZE,
PAGE_SIZE_BASE,
} from '../../../../constants/constants';
import { TABLE_SCROLL_VALUE } from '../../../../constants/Table.constants';
@ -27,10 +30,21 @@ import {
TABLE_COLUMNS_KEYS,
} from '../../../../constants/TableKeys.constants';
import { EntityType } from '../../../../enums/entity.enum';
import { SearchIndex } from '../../../../enums/search.enum';
import useCustomLocation from '../../../../hooks/useCustomLocation/useCustomLocation';
import { useTableFilters } from '../../../../hooks/useTableFilters';
import { ServicePageData } from '../../../../pages/ServiceDetailsPage/ServiceDetailsPage.interface';
import { getEntityName } from '../../../../utils/EntityUtils';
import { searchQuery } from '../../../../rest/searchAPI';
import { buildSchemaQueryFilter } from '../../../../utils/DatabaseSchemaDetailsUtils';
import {
getColumnSorter,
getEntityName,
highlightSearchText,
} from '../../../../utils/EntityUtils';
import { getEntityDetailsPath } from '../../../../utils/RouterUtils';
import { stringToHTML } from '../../../../utils/StringsUtils';
import { tagTableObject } from '../../../../utils/TableColumn.util';
import { showErrorToast } from '../../../../utils/ToastUtils';
import ErrorPlaceHolder from '../../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
import RichTextEditorPreviewerNew from '../../../common/RichTextEditor/RichTextEditorPreviewNew';
import Table from '../../../common/Table/Table';
@ -43,8 +57,70 @@ function FilesTable({
handlePageChange,
files,
isLoading,
setFiles,
setIsLoading,
serviceFqn,
fetchFiles,
}: Readonly<FilesTableProps>) {
const { t } = useTranslation();
const location = useCustomLocation();
const { setFilters } = useTableFilters({});
const searchValue = useMemo(() => {
const param = location.search;
const searchData = QueryString.parse(
param.startsWith('?') ? param.substring(1) : param
);
return searchData.file as string | undefined;
}, [location.search]);
const searchFiles = useCallback(
async (searchValue: string, pageNumber = INITIAL_PAGING_VALUE) => {
setIsLoading(true);
paging.handlePageChange(pageNumber, {
cursorType: null,
cursorValue: undefined,
});
try {
const response = await searchQuery({
query: '',
pageNumber,
pageSize: PAGE_SIZE,
queryFilter: buildSchemaQueryFilter(
'service.fullyQualifiedName.keyword',
serviceFqn,
searchValue
),
searchIndex: SearchIndex.FILE_SEARCH_INDEX,
includeDeleted: showDeleted,
trackTotalHits: true,
});
const data = response.hits.hits.map((file) => file._source);
const total = response.hits.total.value;
setFiles(data);
paging.handlePagingChange({ total });
} catch (error) {
showErrorToast(error as AxiosError);
} finally {
setIsLoading(false);
}
},
[serviceFqn, showDeleted, paging, setFiles, setIsLoading]
);
const onFileSearch = useCallback(
(value: string) => {
setFilters({ file: isEmpty(value) ? undefined : value });
if (value) {
searchFiles(value);
} else {
fetchFiles();
paging.handlePageChange(INITIAL_PAGING_VALUE);
}
},
[searchFiles, paging]
);
const tableColumn: ColumnsType<ServicePageData> = useMemo(
() => [
@ -53,6 +129,7 @@ function FilesTable({
dataIndex: TABLE_COLUMNS_KEYS.NAME,
key: TABLE_COLUMNS_KEYS.NAME,
width: 300,
sorter: getColumnSorter<ServicePageData, 'name'>('name'),
render: (_, record: ServicePageData) => {
const fileDisplayName = getEntityName(record);
@ -65,7 +142,9 @@ function FilesTable({
EntityType.FILE,
record.fullyQualifiedName || ''
)}>
{fileDisplayName}
{stringToHTML(
highlightSearchText(fileDisplayName, searchValue)
)}
</Link>
</div>
);
@ -89,7 +168,7 @@ function FilesTable({
},
...tagTableObject<ServicePageData>(),
],
[]
[searchValue, t]
);
const handleShowDeletedChange = (checked: boolean) => {
@ -98,12 +177,25 @@ function FilesTable({
paging.handlePageSizeChange(PAGE_SIZE_BASE);
};
const searchProps = useMemo(
() => ({
placeholder: t('label.search-for-type', {
type: t('label.file'),
}),
typingInterval: 500,
searchValue: searchValue,
onSearch: onFileSearch,
}),
[onFileSearch, searchValue, t]
);
return (
<Table
columns={tableColumn}
customPaginationProps={{
currentPage: paging.currentPage,
isLoading,
isNumberBased: Boolean(searchValue),
pageSize: paging.pageSize,
paging: paging.paging,
pagingHandler: handlePageChange,
@ -133,6 +225,7 @@ function FilesTable({
pagination={false}
rowKey="id"
scroll={TABLE_SCROLL_VALUE}
searchProps={searchProps}
size="small"
staticVisibleColumns={COMMON_STATIC_TABLE_VISIBLE_COLUMNS}
/>

View File

@ -10,6 +10,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { PagingWithoutTotal } from 'Models';
import { Dispatch, SetStateAction } from 'react';
import { Spreadsheet } from '../../../../generated/entity/data/spreadsheet';
import { UsePagingInterface } from '../../../../hooks/paging/usePaging';
import { PagingHandlerParams } from '../../../common/NextPrevious/NextPrevious.interface';
@ -21,4 +23,8 @@ export interface SpreadsheetsTableProps {
handlePageChange: (data: PagingHandlerParams) => void;
spreadsheets: Spreadsheet[];
isLoading: boolean;
setSpreadsheets: Dispatch<SetStateAction<Spreadsheet[]>>;
setIsLoading: Dispatch<SetStateAction<boolean>>;
serviceFqn: string;
fetchSpreadsheets: (paging?: PagingWithoutTotal) => void;
}

View File

@ -12,12 +12,15 @@
*/
import { Switch, Typography } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import { isUndefined } from 'lodash';
import { useMemo } from 'react';
import { AxiosError } from 'axios';
import { isEmpty, isUndefined } from 'lodash';
import QueryString from 'qs';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import {
INITIAL_PAGING_VALUE,
PAGE_SIZE,
PAGE_SIZE_BASE,
} from '../../../../constants/constants';
import { TABLE_SCROLL_VALUE } from '../../../../constants/Table.constants';
@ -27,10 +30,21 @@ import {
TABLE_COLUMNS_KEYS,
} from '../../../../constants/TableKeys.constants';
import { EntityType } from '../../../../enums/entity.enum';
import { SearchIndex } from '../../../../enums/search.enum';
import useCustomLocation from '../../../../hooks/useCustomLocation/useCustomLocation';
import { useTableFilters } from '../../../../hooks/useTableFilters';
import { ServicePageData } from '../../../../pages/ServiceDetailsPage/ServiceDetailsPage.interface';
import { getEntityName } from '../../../../utils/EntityUtils';
import { searchQuery } from '../../../../rest/searchAPI';
import { buildSchemaQueryFilter } from '../../../../utils/DatabaseSchemaDetailsUtils';
import {
getColumnSorter,
getEntityName,
highlightSearchText,
} from '../../../../utils/EntityUtils';
import { getEntityDetailsPath } from '../../../../utils/RouterUtils';
import { stringToHTML } from '../../../../utils/StringsUtils';
import { tagTableObject } from '../../../../utils/TableColumn.util';
import { showErrorToast } from '../../../../utils/ToastUtils';
import ErrorPlaceHolder from '../../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
import RichTextEditorPreviewerNew from '../../../common/RichTextEditor/RichTextEditorPreviewNew';
import Table from '../../../common/Table/Table';
@ -43,8 +57,72 @@ function SpreadsheetsTable({
handlePageChange,
spreadsheets,
isLoading,
setSpreadsheets,
setIsLoading,
serviceFqn,
fetchSpreadsheets,
}: Readonly<SpreadsheetsTableProps>) {
const { t } = useTranslation();
const location = useCustomLocation();
const { setFilters } = useTableFilters({});
const searchValue = useMemo(() => {
const param = location.search;
const searchData = QueryString.parse(
param.startsWith('?') ? param.substring(1) : param
);
return searchData.spreadsheet as string | undefined;
}, [location.search]);
const searchSpreadsheets = useCallback(
async (searchValue: string, pageNumber = INITIAL_PAGING_VALUE) => {
setIsLoading(true);
paging.handlePageChange(pageNumber, {
cursorType: null,
cursorValue: undefined,
});
try {
const response = await searchQuery({
query: '',
pageNumber,
pageSize: PAGE_SIZE,
queryFilter: buildSchemaQueryFilter(
'service.fullyQualifiedName.keyword',
serviceFqn,
searchValue
),
searchIndex: SearchIndex.SPREADSHEET_SEARCH_INDEX,
includeDeleted: showDeleted,
trackTotalHits: true,
});
const data = response.hits.hits.map(
(spreadsheet) => spreadsheet._source
);
const total = response.hits.total.value;
setSpreadsheets(data);
paging.handlePagingChange({ total });
} catch (error) {
showErrorToast(error as AxiosError);
} finally {
setIsLoading(false);
}
},
[serviceFqn, showDeleted, paging, setSpreadsheets, setIsLoading]
);
const onSpreadsheetSearch = useCallback(
(value: string) => {
setFilters({ spreadsheet: isEmpty(value) ? undefined : value });
if (value) {
searchSpreadsheets(value);
} else {
fetchSpreadsheets();
paging.handlePageChange(INITIAL_PAGING_VALUE);
}
},
[searchSpreadsheets, paging]
);
const tableColumn: ColumnsType<ServicePageData> = useMemo(
() => [
@ -53,6 +131,7 @@ function SpreadsheetsTable({
dataIndex: TABLE_COLUMNS_KEYS.NAME,
key: TABLE_COLUMNS_KEYS.NAME,
width: 300,
sorter: getColumnSorter<ServicePageData, 'name'>('name'),
render: (_, record: ServicePageData) => {
const spreadsheetDisplayName = getEntityName(record);
@ -65,7 +144,9 @@ function SpreadsheetsTable({
EntityType.SPREADSHEET,
record.fullyQualifiedName || ''
)}>
{spreadsheetDisplayName}
{stringToHTML(
highlightSearchText(spreadsheetDisplayName, searchValue)
)}
</Link>
</div>
);
@ -89,7 +170,7 @@ function SpreadsheetsTable({
},
...tagTableObject<ServicePageData>(),
],
[]
[searchValue, t]
);
const handleShowDeletedChange = (checked: boolean) => {
@ -98,6 +179,18 @@ function SpreadsheetsTable({
paging.handlePageSizeChange(PAGE_SIZE_BASE);
};
const searchProps = useMemo(
() => ({
placeholder: t('label.search-for-type', {
type: t('label.spreadsheet'),
}),
typingInterval: 500,
searchValue: searchValue,
onSearch: onSpreadsheetSearch,
}),
[onSpreadsheetSearch, searchValue, t]
);
return (
<Table
columns={tableColumn}
@ -106,6 +199,7 @@ function SpreadsheetsTable({
isLoading,
pageSize: paging.pageSize,
paging: paging.paging,
isNumberBased: Boolean(searchValue),
pagingHandler: handlePageChange,
onShowSizeChange: paging.handlePageSizeChange,
showPagination: paging.showPagination,
@ -133,6 +227,7 @@ function SpreadsheetsTable({
pagination={false}
rowKey="id"
scroll={TABLE_SCROLL_VALUE}
searchProps={searchProps}
size="small"
staticVisibleColumns={COMMON_STATIC_TABLE_VISIBLE_COLUMNS}
/>

View File

@ -46,6 +46,7 @@ import {
getEpochMillisForPastDays,
} from '../../../../../utils/date-time/DateTimeUtils';
import {
getColumnSorter,
getEntityName,
highlightSearchText,
} from '../../../../../utils/EntityUtils';
@ -325,6 +326,7 @@ function IngestionListTable({
dataIndex: 'name',
key: 'name',
fixed: 'left' as FixedType,
sorter: getColumnSorter<IngestionPipeline, 'name'>('name'),
render: customRenderNameField ?? renderNameField(searchText),
},
...(showDescriptionCol

View File

@ -43,7 +43,11 @@ import { DatabaseServiceSearchSource } from '../../../interface/search.interface
import { ServicesType } from '../../../interface/service.interface';
import { getServices, searchService } from '../../../rest/serviceAPI';
import { getServiceLogo } from '../../../utils/CommonUtils';
import { getEntityName, highlightSearchText } from '../../../utils/EntityUtils';
import {
getColumnSorter,
getEntityName,
highlightSearchText,
} from '../../../utils/EntityUtils';
import { checkPermission } from '../../../utils/PermissionsUtils';
import {
getAddServicePath,
@ -327,6 +331,7 @@ const Services = ({ serviceName }: ServicesProps) => {
dataIndex: TABLE_COLUMNS_KEYS.NAME,
key: TABLE_COLUMNS_KEYS.NAME,
width: 200,
sorter: getColumnSorter<ServicesType, 'name'>('name'),
render: (name, record) => (
<div className="d-flex gap-2 items-center">
{getServiceLogo(record.serviceType || '', 'w-4')}

View File

@ -35,7 +35,7 @@ import {
} from '../../../generated/entity/data/topic';
import { TagLabel, TagSource } from '../../../generated/type/tagLabel';
import { useFqn } from '../../../hooks/useFqn';
import { getEntityName } from '../../../utils/EntityUtils';
import { getColumnSorter, getEntityName } from '../../../utils/EntityUtils';
import { getVersionedSchema } from '../../../utils/SchemaVersionUtils';
import { columnFilterIcon } from '../../../utils/TableColumn.util';
import {
@ -294,6 +294,7 @@ const TopicSchemaFields: FC<TopicSchemaFieldsProps> = ({
key: TABLE_COLUMNS_KEYS.NAME,
fixed: 'left',
width: 220,
sorter: getColumnSorter<Field, 'name'>('name'),
render: renderSchemaName,
},
{

Some files were not shown because too many files have changed in this diff Show More