From 8ab2020575c74b3c6f34344a39d14f2aae2dd394 Mon Sep 17 00:00:00 2001 From: Varun-Kumar-R Date: Tue, 10 Dec 2024 11:35:21 +0000 Subject: [PATCH] Syncing changes from private repo: Tue Dec 10 11:35:21 UTC 2024 --- README.md | 1 + backend/go.mod | 4 +- backend/go.sum | 91 +++++++++++ backend/internal/helper.go | 2 +- backend/internal/login.go | 2 +- backend/internal/model.go | 2 +- backend/main.go | 2 +- backend/scripts/huggingface_installer.sh | 3 +- backend/scripts/presto_installer.sh | 10 +- frontend/package.json | 9 +- frontend/src/App.js | 27 ++-- frontend/src/App.scss | 22 +-- frontend/src/__test__/BenchmarkLogs.test.js | 6 + .../src/__test__/BenchmarkLogsTable.test.js | 49 ++++-- .../src/__test__/ByoReportDetails.test.js | 12 +- .../src/__test__/Configurationdetails.test.js | 18 ++- frontend/src/__test__/Dashboard.test.js | 16 +- frontend/src/__test__/LandingPage.test.js | 49 ++++-- .../src/__test__/MonteReportDetails.test.js | 17 +- .../src/__test__/PrestoReportDetails.test.js | 9 ++ frontend/src/__test__/Support.test.js | 8 +- .../src/__test__/huggingReportDetails.test.js | 20 ++- .../src/__test__/notificationManager.test.js | 75 +++++++++ frontend/src/__test__/toast.test.js | 46 ++---- frontend/src/__test__/utils.js | 23 ++- .../content/BenchmarkLogs/BenchmarkTable.js | 35 ++--- frontend/src/content/Dashboard/byoReport.js | 76 ++++----- .../src/content/Dashboard/huggingReport.js | 35 ++--- .../src/content/Dashboard/monteCarloReport.js | 35 ++--- .../src/content/Dashboard/prestoReport.js | 35 ++--- .../src/content/LandingPage/LandingPage.js | 37 ++--- .../content/component/NotificationManager.js | 33 ++++ frontend/src/content/component/toast.js | 69 +++------ .../configuration/configurationDetails.js | 38 ++--- frontend/src/locale/en/translation.json | 1 + resources/repo_synch.sh | 146 ++++++++++++++++++ template/pull_request_template.md | 26 ++++ 37 files changed, 741 insertions(+), 348 deletions(-) create mode 100644 backend/go.sum create mode 100644 frontend/src/__test__/notificationManager.test.js create mode 100644 frontend/src/content/component/NotificationManager.js create mode 100644 resources/repo_synch.sh create mode 100644 template/pull_request_template.md diff --git a/README.md b/README.md index 47aecb9..b50262c 100644 --- a/README.md +++ b/README.md @@ -69,3 +69,4 @@ running benchmarks, and viewing results. Though the materials provided herein are not supported by the IBM Service organization, your comments are welcomed by the developers, who reserve the right to revise or remove the materials at any time. For reporting a problem, providing suggestions, or comments regarding the IBM Cloud Virtual Servers for VPC Sandbox, users can open a [GitHub issue](https://github.com/IBM-Cloud/sandbox-benchmark-for-vpc/issues). All issues will be addressed as best effort by developers. Please note that there are no warranties of any kind, and there is no service or technical support available for these materials from IBM. As a recommended practice, carefully review any materials before using them. + diff --git a/backend/go.mod b/backend/go.mod index 2f3db30..a9fb2c4 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -1,4 +1,4 @@ -module github.com/IBM-Cloud/sandbox-benchmark-dashboard-for-vpc +module github.ibm.com/workload-eng-services/sandbox go 1.21.6 @@ -7,7 +7,7 @@ require ( github.com/IBM/vpc-go-sdk v0.50.0 github.com/bramvdbogaerde/go-scp v1.4.0 github.com/go-playground/validator/v10 v10.19.0 - github.com/golang-jwt/jwt v3.2.2+incompatible + github.com/golang-jwt/jwt/v4 v4.5.1 github.com/gorilla/mux v1.8.1 github.com/lib/pq v1.10.9 github.com/rs/cors v1.11.0 diff --git a/backend/go.sum b/backend/go.sum new file mode 100644 index 0000000..b0ba626 --- /dev/null +++ b/backend/go.sum @@ -0,0 +1,91 @@ +github.com/IBM/go-sdk-core/v5 v5.16.3 h1:GJI62GNAagX2xeTMpTACIqki5rDVO3YbxzMuIpAXSrQ= +github.com/IBM/go-sdk-core/v5 v5.16.3/go.mod h1:aojBkkq4HXkOYdn7YZ6ve8cjPWHdcB3tt8v0b9Cbac8= +github.com/IBM/vpc-go-sdk v0.50.0 h1:+vnXYK0FXFXYqaS/5/X1XEqH0bbRotkzkerRk21ZEjE= +github.com/IBM/vpc-go-sdk v0.50.0/go.mod h1:iBg9UJY1y/XpkweyP6YH7G6guzKPV8BYDoBMTdPupH4= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/bramvdbogaerde/go-scp v1.4.0 h1:jKMwpwCbcX1KyvDbm/PDJuXcMuNVlLGi0Q0reuzjyKY= +github.com/bramvdbogaerde/go-scp v1.4.0/go.mod h1:on2aH5AxaFb2G0N5Vsdy6B0Ml7k9HuHSwfo1y0QzAbQ= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/go-openapi/errors v0.21.0 h1:FhChC/duCnfoLj1gZ0BgaBmzhJC2SL/sJr8a2vAobSY= +github.com/go-openapi/errors v0.21.0/go.mod h1:jxNTMUxRCKj65yb/okJGEtahVd7uvWnuWfj53bse4ho= +github.com/go-openapi/strfmt v0.22.1 h1:5Ky8cybT4576C6Ffc+8gYji/wRXCo6Ozm8RaWjPI6jc= +github.com/go-openapi/strfmt v0.22.1/go.mod h1:OfVoytIXJasDkkGvkb1Cceb3BPyMOwk1FgmyyEw7NYg= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= +github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= +github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= +github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= +go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/backend/internal/helper.go b/backend/internal/helper.go index 4764451..ba38216 100644 --- a/backend/internal/helper.go +++ b/backend/internal/helper.go @@ -23,7 +23,7 @@ import ( "github.com/IBM/go-sdk-core/v5/core" "github.com/IBM/vpc-go-sdk/vpcv1" scp "github.com/bramvdbogaerde/go-scp" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v4" "golang.org/x/crypto/ssh" ) diff --git a/backend/internal/login.go b/backend/internal/login.go index 4b251f3..b8dc7d1 100644 --- a/backend/internal/login.go +++ b/backend/internal/login.go @@ -8,7 +8,7 @@ import ( "net/http" "time" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v4" "golang.org/x/crypto/bcrypt" ) diff --git a/backend/internal/model.go b/backend/internal/model.go index 18c5b86..e4cc1f3 100644 --- a/backend/internal/model.go +++ b/backend/internal/model.go @@ -4,7 +4,7 @@ import ( "regexp" "github.com/go-playground/validator/v10" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v4" ) // InstanceRequest represents a request for creating a sandbox instance. diff --git a/backend/main.go b/backend/main.go index d54fc43..1488c3c 100644 --- a/backend/main.go +++ b/backend/main.go @@ -7,7 +7,7 @@ import ( "os/user" "strconv" - inst "github.com/IBM-Cloud/sandbox-benchmark-dashboard-for-vpc/internal" + inst "github.ibm.com/workload-eng-services/sandbox/internal" ) func main() { diff --git a/backend/scripts/huggingface_installer.sh b/backend/scripts/huggingface_installer.sh index b61cb1a..3b9b09c 100644 --- a/backend/scripts/huggingface_installer.sh +++ b/backend/scripts/huggingface_installer.sh @@ -17,8 +17,9 @@ virtualenv /home/ubuntu/inference_env . /home/ubuntu/inference_env/bin/activate # Installing Pytorch and transformers -pip3 install torch==2.3.1 -f https://download.pytorch.org/whl/cpu +pip3 install torch==2.3.1 -f https://download.pytorch.org/whl/cpu pip3 install intel_extension_for_pytorch==2.3.0 -f https://developer.intel.com/ipex-whl-stable-cpu + # Creating & locking requirements.txt for pip3 versions cat <<'EOF' > requirements.txt certifi==2024.7.4 diff --git a/backend/scripts/presto_installer.sh b/backend/scripts/presto_installer.sh index 06d7bcd..8190e7e 100644 --- a/backend/scripts/presto_installer.sh +++ b/backend/scripts/presto_installer.sh @@ -56,7 +56,7 @@ su ubuntu -c "cd /home/ubuntu/hadoop-3.2.4/sbin && ./start-all.sh" ## install and configure apache hive su ubuntu -c ' cd /home/ubuntu && \ -wget https://downloads.apache.org/hive/hive-3.1.3/apache-hive-3.1.3-bin.tar.gz && \ +wget https://archive.apache.org/dist/hive/hive-3.1.3/apache-hive-3.1.3-bin.tar.gz && \ tar xzf apache-hive-3.1.3-bin.tar.gz ' @@ -415,11 +415,11 @@ chmod +x application-presto-benchto.yaml; \ chmod +x set_env.sh ' -sed -i -e 's/\(ELIMINATE_CROSS_JOINS\|PARTITIONED\),\?//g' -e 's/\${tpch_[0-9]\+}/\${tpch_10}/g' -e '/tpch_300: tpch_sf300_orc/d; /tpch_1000: tpch_sf1000_orc/d' -e 's/tpch_3000: tpch_sf3000_orc/tpch_10: tpch_10gb_parquet/' -e "s/runs: 6/runs: 2/" /home/ubuntu/presto-0.285.1/presto-benchto-benchmarks/src/main/resources/benchmarks/presto/tpch.yaml +sed -i -e 's/\(ELIMINATE_CROSS_JOINS\|PARTITIONED\),\?//g' -e 's/\${tpch_[0-9]\+}/\${tpch_10}/g' -e '/tpch_300: tpch_sf300_orc/d; /tpch_1000: tpch_sf1000_orc/d' -e 's/tpch_3000: tpch_sf3000_orc/tpch_10: tpch_10gb_parquet/' -e "s/runs: 6/runs: 3/" /home/ubuntu/presto-0.285.1/presto-benchto-benchmarks/src/main/resources/benchmarks/presto/tpch.yaml sed -i -e '/^ 2:/,$d' -e 's/q05, q07, q08, q09, q17, q18, q21/query/' /home/ubuntu/presto-0.285.1/presto-benchto-benchmarks/src/main/resources/benchmarks/presto/tpch.yaml cat <<'EOF' >/home/ubuntu/presto-0.285.1/overrides.yaml -runs: 2 +runs: 3 tpch_medium: tpch_10gb_parquet EOF @@ -529,7 +529,7 @@ pkill nmon ## gather presto benchmark data and utilization metrics -ExecutionTime=`docker ps | grep postgres | awk '{print $1}' | xargs -I {} docker exec {} bash -c 'psql -U postgres -At -c "select sum(executions_mean_duration) from benchmark_runs;"'` +ExecutionTime=`docker ps | grep postgres | awk '{print $1}' | xargs -I {} docker exec {} bash -c 'psql -U postgres -At -c "SELECT AVG(value) FROM measurements WHERE id IN (11, 18) AND name = '\''duration'\'';"'` Memtotal=`cat $script_dir/presto.nmon| grep -e MemTotal | awk '{ print $2}'` MemFree=`cat $script_dir/presto.nmon | grep -e MemFree | awk '{ print $2}'` MemUtil=$((($Memtotal-$MemFree)*100/$Memtotal)) @@ -560,4 +560,4 @@ echo "Query $userInput ExecutionTime(ms): $ExecutionTime" >> $script_dir/output. EOF -sudo chmod 777 /home/ubuntu/presto_runner_metrics.sh \ No newline at end of file +sudo chmod 777 /home/ubuntu/presto_runner_metrics.sh diff --git a/frontend/package.json b/frontend/package.json index 27b8e49..fb8bfa2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,7 +9,7 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", - "axios": "^1.6.2", + "axios": "^1.7.3", "i18next": "^23.8.1", "i18next-browser-languagedetector": "^7.2.0", "i18next-http-backend": "^2.4.2", @@ -52,7 +52,12 @@ "svgo": "^3.2.0", "nth-check": "^2.1.1", "postcss": "^8.4.35", - "follow-redirects": "^1.15.6" + "follow-redirects": "^1.15.6", + "micromatch": "^4.0.7", + "cookie": "^0.7.0", + "rollup": "^2.79.2", + "express": "^4.21.1", + "http-proxy-middleware": "^2.0.7" }, "devDependencies": { "@babel/preset-env": "^7.24.3", diff --git a/frontend/src/App.js b/frontend/src/App.js index bae10f1..bfdbc54 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -10,23 +10,26 @@ import BenchmarkPage from './content/BenchmarkLogs'; import Supportpage from './content/support'; import LoginPage from './login'; import NotFound from './components/NotFound'; +import { NotificationProvider } from './content/component/NotificationManager'; function App() { return (
- - } /> - }> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - + + + } /> + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + +
); diff --git a/frontend/src/App.scss b/frontend/src/App.scss index eda6d29..1a63ee7 100644 --- a/frontend/src/App.scss +++ b/frontend/src/App.scss @@ -64,14 +64,6 @@ body { } } - -.notification-ui { - position: fixed; - right: $spacing-06; - top: 3rem; - z-index: 999; -} - .login-container { height: 100%; @@ -185,8 +177,18 @@ body { .error-notification{ margin-bottom: $spacing-05; } -.hide-notification .notification-ui{ - display: none; +.generic-toast-container { + position: fixed; + right: $spacing-06; + top: 3rem; + z-index: 9999; +} + +.notification-ui { + position: relative; + display: flex; + margin-top: .5rem; + margin-bottom: .5rem; } .session-modal .cds--modal-close-button{ display: none; diff --git a/frontend/src/__test__/BenchmarkLogs.test.js b/frontend/src/__test__/BenchmarkLogs.test.js index 9522964..19ea789 100644 --- a/frontend/src/__test__/BenchmarkLogs.test.js +++ b/frontend/src/__test__/BenchmarkLogs.test.js @@ -2,6 +2,7 @@ import React from "react"; import { render, screen, waitFor } from "@testing-library/react"; import '@testing-library/jest-dom'; import BenchmarkPage from "../content/BenchmarkLogs/benchmark"; +import { useNotification } from "../content/component/NotificationManager"; jest.mock("react-i18next", () => ({ useTranslation: () => ({ @@ -11,8 +12,13 @@ jest.mock("react-i18next", () => ({ jest.mock('../content/api/api', () => ({ })); +jest.mock('../content/component/NotificationManager', () => ({ + useNotification: jest.fn(), +})); + describe("BenchmarkPage component", () => { it("renders without errors", async () => { + useNotification.mockReturnValue(jest.fn()); render(); const benchmarkLogsElement = await screen.findByText('benchmarkLogs'); await waitFor(() => { diff --git a/frontend/src/__test__/BenchmarkLogsTable.test.js b/frontend/src/__test__/BenchmarkLogsTable.test.js index a710ca6..62b412f 100644 --- a/frontend/src/__test__/BenchmarkLogsTable.test.js +++ b/frontend/src/__test__/BenchmarkLogsTable.test.js @@ -4,6 +4,7 @@ import '@testing-library/jest-dom'; import ConfigurationDetails from '../content/BenchmarkLogs/BenchmarkTable'; import * as api from "../content/api/api"; import { benchmarkLogsMockData, benchmarkLogsMockData2 } from './utils'; +import { useNotification } from "../content/component/NotificationManager"; jest.mock('react-i18next', () => ({ useTranslation: () => ({ @@ -25,6 +26,9 @@ jest.mock('../content/api/api', () => ({ .mockRejectedValue(new Error('API Error')), downloadLogsApi: jest.fn(), })); +jest.mock('../content/component/NotificationManager', () => ({ + useNotification: jest.fn(), +})); describe('ConfigurationDetails Component', () => { beforeEach(() => { @@ -39,8 +43,9 @@ describe('ConfigurationDetails Component', () => { expect(benchmarkLogsElement).toBeVisible(); }); it("getInstanceStatus", async () => { + useNotification.mockReturnValue(jest.fn()); render(); - const renewButton = screen.getByRole('button', {name: /renew/i,}); + const renewButton = screen.getByRole('button', { name: /renew/i, }); fireEvent.click(renewButton); await waitFor(() => expect(api.getInstanceStatus).toHaveBeenCalled()); }); @@ -63,12 +68,25 @@ describe('ConfigurationDetails Component', () => { }); it('handle_fetch_errors', async () => { - api.getBenchmarkRunLogs.mockRejectedValue(new Error('Failed to retrieve all benchmark logs')); - const { getByRole, findByText } = render(); - const refreshButton = getByRole('button', { name: 'renew' }); + const addToastMock = jest.fn(); + useNotification.mockReturnValue(addToastMock); + api.getBenchmarkRunLogs.mockRejectedValueOnce(new Error('API Error')); + render(); + const refreshButton = screen.getByRole('button', { name: 'renew' }); fireEvent.click(refreshButton); - const errorMessage = await findByText('Failed to retrieve all benchmark logs'); - expect(errorMessage).toHaveTextContent('Failed to retrieve all benchmark logs'); + await waitFor(() => { + expect(addToastMock).toHaveBeenCalledWith( + expect.objectContaining({ + ariaLabel: "error", + id: "getAllLogsFailed", + kind: "error", + role: "alert", + subtitle: "Failed to retrieve all benchmark logs", + timeout: "", + title: "failed" + }) + ); + }); }); it('should set showAppStatus, showNotification, showNotificationMsg, and showToastContainer correctly', () => { @@ -95,8 +113,8 @@ describe('ConfigurationDetails Component', () => { }); it('test_handle_logs_pagination', async () => { - render(); api.getBenchmarkRunLogs.mockResolvedValueOnce(benchmarkLogsMockData); + render(); await waitFor(async () => { const firstLogName = await screen.findByText("presto-benchmark-wzs2o"); @@ -104,25 +122,22 @@ describe('ConfigurationDetails Component', () => { expect(firstLogName).toBeInTheDocument(); expect(secondLogName).toBeInTheDocument(); }); + api.getBenchmarkRunLogs.mockResolvedValueOnce(benchmarkLogsMockData2); const nextPageButton = await screen.findByText('nextPage'); - const pageInput = await screen.findByText('1'); - fireEvent.click(nextPageButton); - fireEvent.change(pageInput, { target: { value: 2 } }); - api.getBenchmarkRunLogs.mockResolvedValueOnce(benchmarkLogsMockData2); + await waitFor(async () => { - const firstLogName = await screen.findByText("presto-benchmark-plpsc"); - const secondLogName = await screen.findByText("presto-benchmark-ln7r4"); - expect(firstLogName).toBeInTheDocument(); - expect(secondLogName).toBeInTheDocument(); + const firstLogNameNew = await screen.findByText("presto-benchmark-plpsc"); + const secondLogNameNew = await screen.findByText("presto-benchmark-ln7r4"); + expect(firstLogNameNew).toBeInTheDocument(); + expect(secondLogNameNew).toBeInTheDocument(); }); - }); it('calls handleDownloadOpen with the correct cell value when download button is clicked', async () => { - + const { container } = render(); api.getBenchmarkRunLogs.mockResolvedValueOnce(benchmarkLogsMockData); const mockLogText = 'some log data'; diff --git a/frontend/src/__test__/ByoReportDetails.test.js b/frontend/src/__test__/ByoReportDetails.test.js index 3be532b..b943029 100644 --- a/frontend/src/__test__/ByoReportDetails.test.js +++ b/frontend/src/__test__/ByoReportDetails.test.js @@ -12,6 +12,7 @@ import BYOReport from '../content/Dashboard/byoReport'; import { getByoLists, getByo } from '../content/api/api'; import * as api from "../content/api/api"; import { mockGetByoInstanceResponse, mockGetByoRunBenchmarkResponse } from './utils'; +import { useNotification } from "../content/component/NotificationManager"; jest.mock('../components/theme', () => () => false); @@ -20,9 +21,16 @@ jest.mock('../content/api/api', () => ({ getByo: jest.fn(), })); +jest.mock('../content/component/NotificationManager', () => ({ + useNotification: jest.fn(), +})); + describe('ByoReport Component', () => { beforeEach(() => { jest.clearAllMocks(); + getByo.mockResolvedValue(mockGetByoInstanceResponse); + getByoLists.mockResolvedValue(mockGetByoRunBenchmarkResponse); + useNotification.mockReturnValue(jest.fn()); }); afterEach(() => { jest.clearAllMocks(); @@ -57,8 +65,9 @@ describe('ByoReport Component', () => { }); it("renders the component and charts with records when listtest is not empty", async () => { - api.getByoLists.mockResolvedValueOnce(mockGetByoRunBenchmarkResponse); render(); + getByo.mockResolvedValueOnce(mockGetByoInstanceResponse); + getByoLists.mockResolvedValueOnce(mockGetByoRunBenchmarkResponse); await waitFor(() => { expect(api.getByoLists).toHaveBeenCalledWith({ count: 4, @@ -67,6 +76,7 @@ describe('ByoReport Component', () => { }); }); expect(await screen.getByText('sbox-byo-vm2-ihvec')).toBeInTheDocument(); + expect(await screen.getByText('bx3d-8x40')).toBeInTheDocument(); }); }); diff --git a/frontend/src/__test__/Configurationdetails.test.js b/frontend/src/__test__/Configurationdetails.test.js index 620dc49..a5a40e1 100644 --- a/frontend/src/__test__/Configurationdetails.test.js +++ b/frontend/src/__test__/Configurationdetails.test.js @@ -4,6 +4,7 @@ import '@testing-library/jest-dom'; import BenchmarkTable from '../content/configuration/configurationDetails'; import { getAllInstances } from '../content/api/api'; import { mockAllInstanceResponse } from './utils'; +import { useNotification } from "../content/component/NotificationManager"; jest.mock('../content/api/api', () => ({ getAllInstances: jest.fn(), @@ -12,6 +13,9 @@ jest.mock('../components/theme', () => () => false); jest.mock('react-i18next', () => ({ useTranslation: () => ({ t: key => key }), })); +jest.mock('../content/component/NotificationManager', () => ({ + useNotification: jest.fn(), +})); describe('BenchmarkTable', () => { afterEach(() => { @@ -19,6 +23,7 @@ describe('BenchmarkTable', () => { }); it('renders without crashing', async () => { + useNotification.mockReturnValue(jest.fn()); render(); const configElement = await screen.findByText('vsiName'); expect(configElement).toBeVisible(); @@ -42,10 +47,21 @@ describe('BenchmarkTable', () => { }); it('handles API errors gracefully', async () => { + useNotification.mockReturnValue(jest.fn()); getAllInstances.mockRejectedValueOnce(new Error('Failed to retrieve all instances')); + const showNotificationStatus = jest.fn(); + useNotification.mockReturnValue(showNotificationStatus); render(); await waitFor(() => { - expect(screen.getByText('failedRetrieveInstances')).toBeInTheDocument(); + expect(showNotificationStatus).toHaveBeenCalledWith(expect.objectContaining({ + ariaLabel: "error", + id: "allInstancesFailed", + kind: "error", + role: "alert", + subtitle: "failedRetrieveInstances", + timeout: "", + title: "failed" + })); }); }); diff --git a/frontend/src/__test__/Dashboard.test.js b/frontend/src/__test__/Dashboard.test.js index 7cc2d9c..14c2cf3 100644 --- a/frontend/src/__test__/Dashboard.test.js +++ b/frontend/src/__test__/Dashboard.test.js @@ -12,6 +12,7 @@ import '@testing-library/jest-dom'; import '@testing-library/jest-dom/extend-expect'; import RepoPage from '../content/Dashboard/dashboard'; import CommonUIContext from '../content/component/CommonUIContext'; +import { useNotification } from "../content/component/NotificationManager"; jest.mock('../components/theme', () => () => false); @@ -45,7 +46,9 @@ jest.mock('../content/Dashboard/byoReport', () => ({ showPollFlagStatus }) => { showPollFlagStatus && showPollFlagStatus('someFlagStatus'); return
Byo Report
; }); - +jest.mock('../content/component/NotificationManager', () => ({ + useNotification: jest.fn(), +})); describe('RepoPage Component', () => { const setByoState = jest.fn(); afterEach(() => { @@ -59,6 +62,7 @@ describe('RepoPage Component', () => { ); it('renders RepoPage component with tabs', async () => { + useNotification.mockReturnValue(jest.fn()); render(); const monteElement = await screen.findByText('monte.title'); expect(monteElement).toBeVisible(); @@ -69,6 +73,7 @@ describe('RepoPage Component', () => { }); it('renders MonteCarloReport tab panel on clicking Monte Carlo tab', () => { + useNotification.mockReturnValue(jest.fn()); const { getByText, getByTestId } = render(); expect(getByText('monte.title')).toBeInTheDocument(); fireEvent.click(getByText('monte.title')); @@ -77,6 +82,7 @@ describe('RepoPage Component', () => { }); it('renders AiAmxReport tab panel on clicking Hugging tab', () => { + useNotification.mockReturnValue(jest.fn()); const { getByText , getByTestId} = render(); fireEvent.click(getByText('hugging.title')); const subheading = getByTestId('mock-HuggingReport'); @@ -94,12 +100,4 @@ describe('RepoPage Component', () => { expect(byoReports[0]).toBeInTheDocument(); expect(setByoState).toHaveBeenCalledWith('someFlagStatus'); }); - - it('renders MonteCarloReport tab panel with correct subheading', () => { - const { getByText, getByTestId } = render(); - fireEvent.click(getByText('monte.title')); - const subheading = document.querySelector('.landing-page__subheading'); - expect(subheading.textContent).toBe('vsiPerformResult'); - expect(getByTestId('mock-monteCarloReport')).toBeInTheDocument(); - }); }); diff --git a/frontend/src/__test__/LandingPage.test.js b/frontend/src/__test__/LandingPage.test.js index 52b08b5..d8e3652 100644 --- a/frontend/src/__test__/LandingPage.test.js +++ b/frontend/src/__test__/LandingPage.test.js @@ -1,10 +1,10 @@ import React from "react"; import { render, fireEvent, waitFor, screen } from "@testing-library/react"; import '@testing-library/jest-dom'; -import '@testing-library/jest-dom/extend-expect'; import LandingPage from "../content/LandingPage/LandingPage"; import * as api from "../content/api/api"; import { mockMetaData, mockAllInstanceResponse } from "./utils"; +import { useNotification } from "../content/component/NotificationManager"; jest.mock('../components/theme', () => () => false); @@ -34,20 +34,20 @@ jest.mock('@carbon/ibm-products', () => ({ )), })); +jest.mock('../content/component/NotificationManager', () => ({ + useNotification: jest.fn(), +})); + describe("LandingPage", () => { - let showNotificationStatus; beforeEach(() => { - api.getMetadata.mockReset(); - api.getAllInstances.mockReset(); + jest.clearAllMocks(); + useNotification.mockReturnValue(jest.fn()); }); it('renders Landing page', async () => { render(); const landingTitle = await screen.findByText('appTitle'); expect(landingTitle).toBeVisible(); - await waitFor(() => { - expect(screen.getByText('notification')).toBeInTheDocument(); - }); }); test("renders landing page with loading indicator", () => { @@ -77,7 +77,6 @@ describe("LandingPage", () => { await waitFor(() => { expect(api.getMetadata).toHaveBeenCalled(); }); - // we are not showing any Metadata details so i have added except only for api call }); @@ -87,25 +86,45 @@ describe("LandingPage", () => { await waitFor(() => { expect(api.getAllInstances).toHaveBeenCalled(); }); - // we are not showing any instance details so i have added except only for api call }); test("handles API error for getAllInstances and shows notification", async () => { const error = new Error("Failed to fetch instances"); api.getAllInstances.mockRejectedValue(error); + const showNotificationStatus = jest.fn(); + useNotification.mockReturnValue(showNotificationStatus); + render(); await waitFor(() => { - const notification = screen.getByText(/serverError errorLogInfo/i); - expect(notification).toBeInTheDocument(); + expect(showNotificationStatus).toHaveBeenCalledWith(expect.objectContaining({ + ariaLabel: "error", + id: expect.any(String), + kind: "error", + role: "alert", + subtitle: "serverError", + timeout: "", + title: "failed" + })); }); }); test("handles API error for getMetadata and shows notification", async () => { const error = new Error("Failed to fetch metadata"); api.getMetadata.mockRejectedValue(error); + const showNotificationStatus = jest.fn(); + useNotification.mockReturnValue(showNotificationStatus); + render(); - const notification = await screen.findByText("serverError errorLogInfo"); - expect(notification).toBeInTheDocument(); + await waitFor(() => { + expect(showNotificationStatus).toHaveBeenCalledWith(expect.objectContaining({ + ariaLabel: "error", + id: expect.any(String), + kind: "error", + role: "alert", + subtitle: "serverError", + timeout: "", + title: "failed" + })); + }); }); - -}); \ No newline at end of file +}); diff --git a/frontend/src/__test__/MonteReportDetails.test.js b/frontend/src/__test__/MonteReportDetails.test.js index 432ba76..7d1f083 100644 --- a/frontend/src/__test__/MonteReportDetails.test.js +++ b/frontend/src/__test__/MonteReportDetails.test.js @@ -11,12 +11,16 @@ import MonteCarloReport from '../content/Dashboard/monteCarloReport'; import * as api from '../content/api/api'; import MontoCPUReport from "../content/Dashboard/MonteDashboard/CpuReport"; import { mockGetMonteRunBenchmarkResponse } from './utils'; +import { useNotification } from "../content/component/NotificationManager"; jest.mock('../components/theme', () => () => false); jest.mock('../content/api/api', () => ({ getMonteCarloRunLists: jest.fn(), })); +jest.mock('../content/component/NotificationManager', () => ({ + useNotification: jest.fn(), +})); describe('MonteCarloReport Component', () => { afterEach(() => { jest.clearAllMocks(); @@ -26,10 +30,21 @@ describe('MonteCarloReport Component', () => { }); it('displays notification on API error', async () => { + useNotification.mockReturnValue(jest.fn()); api.getMonteCarloRunLists.mockRejectedValueOnce(new Error('API Error')); + const showNotificationStatus = jest.fn(); + useNotification.mockReturnValue(showNotificationStatus); render(); await waitFor(() => { - expect(screen.getByText('failedRetrieveMonteLogs')).toBeInTheDocument(); + expect(showNotificationStatus).toHaveBeenCalledWith(expect.objectContaining({ + ariaLabel: "error", + id: expect.any(String), + kind: "error", + role: "alert", + subtitle: "failedRetrieveMonteLogs", + timeout: "", + title: "failed" + })); }); }); it('fetches monte carlo reports data correctly', async () => { diff --git a/frontend/src/__test__/PrestoReportDetails.test.js b/frontend/src/__test__/PrestoReportDetails.test.js index 6a03583..d43bb0f 100644 --- a/frontend/src/__test__/PrestoReportDetails.test.js +++ b/frontend/src/__test__/PrestoReportDetails.test.js @@ -10,6 +10,7 @@ import '@testing-library/jest-dom'; import * as api from '../content/api/api'; import PrestoReport from '../content/Dashboard/prestoReport'; import { mockGetPrestoRunBenchmarkResponse } from './utils'; +import { useNotification } from "../content/component/NotificationManager"; jest.mock('../components/theme', () => () => false); jest.mock("react-i18next", () => ({ @@ -22,16 +23,23 @@ jest.mock('@carbon/charts-react', () => ({ SimpleBarChart: (props) =>
, })); jest.mock('../content/component/toast', () => (props) =>
); + +jest.mock('../content/component/NotificationManager', () => ({ + useNotification: jest.fn(), +})); + describe('Presto Component', () => { beforeEach(() => { jest.clearAllMocks(); }); it('renders without crashing', () => { + useNotification.mockReturnValue(jest.fn()); render(); expect(screen.getByText('vsiName')).toBeInTheDocument(); }); test('displays loading indicator when data is being fetched', async () => { + useNotification.mockReturnValue(jest.fn()); api.getPrestoRunLists.mockResolvedValueOnce({ ListTest: [] }); render(); await waitFor(() => expect(screen.queryByText('loading')).not.toBeInTheDocument()); @@ -48,6 +56,7 @@ describe('Presto Component', () => { }); it('handles chart correctly', async () => { + useNotification.mockReturnValue(jest.fn()); api.getPrestoRunLists.mockResolvedValueOnce(mockGetPrestoRunBenchmarkResponse); render(); await waitFor(() => expect(screen.queryByText('loading')).not.toBeInTheDocument()); diff --git a/frontend/src/__test__/Support.test.js b/frontend/src/__test__/Support.test.js index 74ccf36..3dc4796 100644 --- a/frontend/src/__test__/Support.test.js +++ b/frontend/src/__test__/Support.test.js @@ -33,12 +33,10 @@ describe('Supportpage Component', () => { expect(await screen.getByText('support.exploreListt1')).toBeVisible(); expect(await screen.getByText('support.exploreListt4')).toBeVisible(); }); - it('displays valid support link', () => { render(); - const supportLinks = screen.getAllByRole('link', { name: /https:\/\/github.com\/IBM-Cloud\/sandbox-benchmark-for-vpc\/issues/ }); - supportLinks.forEach((link) => { - expect(link).toHaveAttribute('href', 'https://github.com/IBM-Cloud/sandbox-benchmark-for-vpc/issues'); - }); + const supportLink = screen.getByRole('link', { name: /sandbox-benchmark-for-vpc\/issues/ }); + expect(supportLink).toHaveAttribute('href', 'https://github.com/IBM-Cloud/sandbox-benchmark-for-vpc/issues'); }); + }); diff --git a/frontend/src/__test__/huggingReportDetails.test.js b/frontend/src/__test__/huggingReportDetails.test.js index 8935d21..8183837 100644 --- a/frontend/src/__test__/huggingReportDetails.test.js +++ b/frontend/src/__test__/huggingReportDetails.test.js @@ -10,6 +10,7 @@ import '@testing-library/jest-dom'; import AiAmxReport from '../content/Dashboard/huggingReport'; import * as api from '../content/api/api'; import { mockGetHuggingInstanceResponse } from './utils'; +import { useNotification } from "../content/component/NotificationManager"; jest.mock("@carbon/react", () => ({ @@ -27,19 +28,23 @@ jest.mock('react-i18next', () => ({ t: key => key }) })); - +jest.mock('../content/component/NotificationManager', () => ({ + useNotification: jest.fn(), +})); describe('HuggingFace Component', () => { beforeEach(() => { jest.clearAllMocks(); }); it('renders loading state', async () => { + useNotification.mockReturnValue(jest.fn()); render(); const hugReport = await screen.findByText('vsiProfile'); expect(hugReport).toBeVisible(); }); it("should display data", async () => { + useNotification.mockReturnValue(jest.fn()); api.getHuggingRunLists.mockResolvedValueOnce(mockGetHuggingInstanceResponse); render(); await waitFor(() => { @@ -47,13 +52,12 @@ describe('HuggingFace Component', () => { }); }); - it("sets setShowNotification to false", () => { - const mockSetShowNotification = jest.fn(); - render( - - ); - mockSetShowNotification(false); - expect(mockSetShowNotification).toHaveBeenCalledWith(false); + it('renders no records message when data is empty', async () => { + api.getHuggingRunLists.mockResolvedValueOnce({ ListTest: [] }); + render(); + await waitFor(() => { + expect(screen.getByText('noRunRecords')).toBeInTheDocument(); + }); }); }); diff --git a/frontend/src/__test__/notificationManager.test.js b/frontend/src/__test__/notificationManager.test.js new file mode 100644 index 0000000..f92aff2 --- /dev/null +++ b/frontend/src/__test__/notificationManager.test.js @@ -0,0 +1,75 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { NotificationProvider, useNotification } from '../content/component/NotificationManager'; +import Notification from '../content/component/toast'; +import { useLocation } from 'react-router-dom'; + +jest.mock('../content/component/toast', () => ({ + __esModule: true, + default: jest.fn(({ toasts }) => ( +
+ {toasts.map(toast => ( +
+ {toast.title} +
+ ))} +
+ )), +})); + +jest.mock('react-router-dom', () => ({ + useLocation: jest.fn(), +})); + +describe('NotificationProvider Component', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('renders children correctly', () => { + render( + +
Child Component
+
+ ); + expect(screen.getByText('Child Component')).toBeInTheDocument(); + }); + + test('provides notification context and adds toast', () => { + const TestComponent = () => { + const addToast = useNotification(); + return ( + + ); + }; + + render( + + + + ); + + fireEvent.click(screen.getByText('Add Toast')); + expect(Notification).toHaveBeenCalledWith( + expect.objectContaining({ + toasts: expect.arrayContaining([ + expect.objectContaining({ id: '1', title: 'Test Toast' }) + ]), + }), + expect.any(Object) + ); + }); + test('renders with empty toasts correctly', () => { + render( + + + + ); + + expect(screen.queryByText('Test Toast')).not.toBeInTheDocument(); + }); + +}); diff --git a/frontend/src/__test__/toast.test.js b/frontend/src/__test__/toast.test.js index e6d7409..6f0da14 100644 --- a/frontend/src/__test__/toast.test.js +++ b/frontend/src/__test__/toast.test.js @@ -3,6 +3,7 @@ import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; import Notification from '../content/component/toast'; import { ToastNotification } from '@carbon/react'; +import { mockToasts } from './utils'; jest.mock('@carbon/react', () => ({ ToastNotification: jest.fn(({ title, subtitle, kind, className }) => ( @@ -16,38 +17,21 @@ jest.mock('@carbon/react', () => ({ describe('Notification Component', () => { test('renders notification with correct props', () => { - render( - {}} - ariaLabel="Notification" - kind="success" - role="status" - subtitle="Notification subtitle" - timeout={5000} - title="Notification Title" - key="notification-key" - /> - ); - expect(ToastNotification).toHaveBeenCalled(); - const [[{ className }]] = ToastNotification.mock.calls; - expect(className).toBe('notification-ui'); + render(); + const relevantCalls = ToastNotification.mock.calls.filter(call => call[0]?.id); + expect(relevantCalls.length).toBe(mockToasts.length); + mockToasts.forEach((toast, index) => { + const callArgs = relevantCalls[index][0]; + expect(callArgs).toBeDefined(); + expect(callArgs.className).toBe('notification-ui'); + expect(callArgs.title).toBe(toast.title); + expect(callArgs.subtitle).toBe(toast.subtitle); + expect(callArgs.kind).toBe(toast.kind); + }); }); test('does not render notification when showToastContainer is false', () => { - render( - {}} - ariaLabel="Notification" - kind="success" - role="status" - subtitle="Notification subtitle" - timeout={5000} - title="Notification Title" - key="notification-key" - /> - ); - const elements = document.getElementsByClassName('notification-ui'); - expect(elements.length).toBe(0); + render(); + const container = document.querySelector('.generic-toast-container'); + expect(container).toBeEmptyDOMElement(); }); }); diff --git a/frontend/src/__test__/utils.js b/frontend/src/__test__/utils.js index f347d27..71a2e5a 100644 --- a/frontend/src/__test__/utils.js +++ b/frontend/src/__test__/utils.js @@ -741,4 +741,25 @@ export const expectedDarkChartOptions = { }, height: '400px', }) -}; \ No newline at end of file +}; + +export const mockToasts = [ + { + id: 'toast-1', + ariaLabel: 'Notification 1', + kind: 'success', + role: 'status', + subtitle: 'Notification subtitle 1', + timeout: 5000, + title: 'Notification Title 1', + }, + { + id: 'toast-2', + ariaLabel: 'Notification 2', + kind: 'error', + role: 'alert', + subtitle: 'Notification subtitle 2', + timeout: 3000, + title: 'Notification Title 2', + }, +]; \ No newline at end of file diff --git a/frontend/src/content/BenchmarkLogs/BenchmarkTable.js b/frontend/src/content/BenchmarkLogs/BenchmarkTable.js index 5ec66ed..82271f5 100644 --- a/frontend/src/content/BenchmarkLogs/BenchmarkTable.js +++ b/frontend/src/content/BenchmarkLogs/BenchmarkTable.js @@ -20,7 +20,7 @@ import { } from "@carbon/react"; import { Download, Renew } from "@carbon/react/icons"; import { getInstanceStatus, getBenchmarkRunLogs, downloadLogsApi } from "../api/api"; -import Notification from "../component/toast"; +import { useNotification } from "../component/NotificationManager"; import { useTranslation } from "react-i18next"; const ConfigurationDetails = () => { @@ -32,10 +32,6 @@ const ConfigurationDetails = () => { const [pageValue, setPageValue] = useState(1); const [pageSizeValue, setPageSizeValue] = useState(10); const [searchTexts, setSearchTexts] = useState(""); - const [showAppStatus, setShowAppStatus] = useState(""); - const [showNotification, setShowNotification] = useState(""); - const [showNotificationMsg, setShowNotificationMsg] = useState({}); - const [showToastContainer, setShowToastContainer] = useState(false); const [isLoading, setIsLoading] = useState(false); const headers = [ @@ -80,22 +76,20 @@ const ConfigurationDetails = () => { } } + const addToast = useNotification(); + function showNotificationStatus(statusKind, status, statusText) { - if (statusKind !== undefined) { - setShowAppStatus(statusKind); + if (statusKind && (statusKind === "error" || statusKind === "success")) { + addToast({ + id: status, + ariaLabel: statusKind, + kind: statusKind, + role: "alert", + subtitle: statusText, + timeout: "", + title: (statusKind === "error" ? (t('failed')) : (t('success'))), + }); } - if (status !== undefined) { - setShowNotification(status); - } - if (statusText !== undefined) { - setShowNotificationMsg(statusText); - } - if ((statusKind !== undefined && statusKind === "error") || (statusKind !== undefined && statusKind === "success")) { - setShowToastContainer(true); - } - }; - const resetShowNotification = () => { - setShowNotification(false); }; const benchmarkLogsList = async (pageNum, searchtxt) => { @@ -184,11 +178,10 @@ const ConfigurationDetails = () => { useEffect(() => { benchmarkLogsList(); - }, [showToastContainer]); + }, []); return ( <> - {({ rows, diff --git a/frontend/src/content/Dashboard/byoReport.js b/frontend/src/content/Dashboard/byoReport.js index f04c20f..ebdf3e3 100644 --- a/frontend/src/content/Dashboard/byoReport.js +++ b/frontend/src/content/Dashboard/byoReport.js @@ -10,51 +10,44 @@ import { } from "@carbon/react"; import { getByoLists, getByo } from "../api/api"; import { useTranslation } from "react-i18next"; -import Notification from "../component/toast"; import CurrentCpuReport from "./ByoDashboard/CurrentCpuReport"; import CurrentMemoryReport from "./ByoDashboard/CurrentMemoryReport"; import useThemeDetector from "../../components/theme"; import errorNotification from "../component/errorNotification"; +import { useNotification } from "../component/NotificationManager"; const BYOReport = (props) => { const { t } = useTranslation(); const [byoReports, setByoReports] = useState({}); const [byoReportLists, setByoReportLists] = useState({}); - const [showAppStatus, setShowAppStatus] = useState(""); - const [showNotification, setShowNotification] = useState(""); - const [showNotificationMsg, setShowNotificationMsg] = useState({}); - const [showToastContainer, setShowToastContainer] = useState(false); const [byoInstanceDetails, setByoInstanceDetails] = useState([]); + const addToast = useNotification(); + function showNotificationStatus(statusKind, status, statusText) { - if (statusKind !== undefined) { - setShowAppStatus(statusKind); - } - if (status !== undefined) { - setShowNotification(status); - } - if (statusText !== undefined) { - setShowNotificationMsg(statusText); + if (statusKind && (statusKind === "error" || statusKind === "success")) { + addToast({ + id: status, + ariaLabel: statusKind, + kind: statusKind, + role: "alert", + subtitle: statusText, + timeout: "", + title: (statusKind === "error" ? (t('failed')) : (t('success'))), + }); } - if ((statusKind !== undefined && statusKind === "error") || (statusKind !== undefined && statusKind === "success")) { - setShowToastContainer(true); - } - }; - - const resetShowNotification = () => { - setShowNotification(false); }; const getByoInstance = async () => { try { - const response = await getByo(); - setByoInstanceDetails(response.instances); - props.showPollFlagStatus(response.byoPollingFlag); + const response = await getByo(); + setByoInstanceDetails(response.instances); + props.showPollFlagStatus(response.byoPollingFlag); } catch (error) { - console.log(error); - errorNotification(error, t('serverError'), "byoGetInstanceFailed", showNotificationStatus); + console.log(error); + errorNotification(error, t('serverError'), "byoGetInstanceFailed", showNotificationStatus); } -}; + }; const getByoReports = async () => { const body = { @@ -88,18 +81,17 @@ const BYOReport = (props) => { useEffect(() => { getByoInstance(); let interval; - if(byoInstanceDetails !== null && byoInstanceDetails !== undefined){ + if (byoInstanceDetails !== null && byoInstanceDetails !== undefined) { getByoReports(); interval = setInterval(getByoReports, 10000); } return () => { clearInterval(interval); }; - }, [showToastContainer, byoInstanceDetails]); + }, [byoInstanceDetails]); return ( <> - @@ -111,19 +103,19 @@ const BYOReport = (props) => { ))} - - {(byoReportLists && byoReportLists.length === 0) && {t('noRunRecords')}} - {byoReports && byoReports.ListTest !== undefined && byoReports.ListTest.map((mrl) => ( - - {mrl.vsiName} - {mrl.vsiuProfile} - {mrl.averageCpuUtilization} - {mrl.averageMemoryUtilization} - {mrl.currentNetworkRxUtilization} | {mrl.currentNetworkTxUtilization} - {mrl.averageIoUtilization} - - ))} - + + {(byoReportLists && byoReportLists.length === 0) && {t('noRunRecords')}} + {byoReports && byoReports.ListTest !== undefined && byoReports.ListTest.map((mrl) => ( + + {mrl.vsiName} + {mrl.vsiuProfile} + {mrl.averageCpuUtilization} + {mrl.averageMemoryUtilization} + {mrl.currentNetworkRxUtilization} | {mrl.currentNetworkTxUtilization} + {mrl.averageIoUtilization} + + ))} +
{(byoReportLists !== null && byoReportLists.length > 0 && byoReportLists !== undefined) && diff --git a/frontend/src/content/Dashboard/huggingReport.js b/frontend/src/content/Dashboard/huggingReport.js index 4132d8a..f23157e 100644 --- a/frontend/src/content/Dashboard/huggingReport.js +++ b/frontend/src/content/Dashboard/huggingReport.js @@ -14,34 +14,28 @@ import { import { getHuggingRunLists } from "../api/api"; import useThemeDetector from "../../components/theme"; import { useTranslation } from "react-i18next"; -import Notification from "../component/toast"; +import { useNotification } from "../component/NotificationManager"; const AiAmxReport = () => { const { t } = useTranslation(); const [aiAmxReports, setAiAmxReports] = useState({}); const [huggingListsValues, setHuggingListsValues] = useState([]); const [loading, setLoading] = useState(false); - const [showAppStatus, setShowAppStatus] = useState(""); - const [showNotification, setShowNotification] = useState(""); - const [showNotificationMsg, setShowNotificationMsg] = useState({}); - const [showToastContainer, setShowToastContainer] = useState(false); + + const addToast = useNotification(); function showNotificationStatus(statusKind, status, statusText) { - if (statusKind !== undefined) { - setShowAppStatus(statusKind); - } - if (status !== undefined) { - setShowNotification(status); - } - if (statusText !== undefined) { - setShowNotificationMsg(statusText); + if (statusKind && (statusKind === "error" || statusKind === "success")) { + addToast({ + id: status, + ariaLabel: statusKind, + kind: statusKind, + role: "alert", + subtitle: statusText, + timeout: "", + title: (statusKind === "error" ? (t('failed')) : (t('success'))), + }); } - if ((statusKind !== undefined && statusKind === "error") || (statusKind !== undefined && statusKind === "success")) { - setShowToastContainer(true); - } - }; - const resetShowNotification = () => { - setShowNotification(false); }; const getAiAmxReports = async () => { @@ -99,11 +93,10 @@ const AiAmxReport = () => { const isDarkTheme = useThemeDetector(); useEffect(() => { getAiAmxReports(); - }, [showToastContainer]); + }, []); return ( <> - diff --git a/frontend/src/content/Dashboard/monteCarloReport.js b/frontend/src/content/Dashboard/monteCarloReport.js index d098cd7..433cd58 100644 --- a/frontend/src/content/Dashboard/monteCarloReport.js +++ b/frontend/src/content/Dashboard/monteCarloReport.js @@ -14,34 +14,28 @@ import { import { getMonteCarloRunLists } from "../api/api"; import useThemeDetector from "../../components/theme"; import { useTranslation } from "react-i18next"; -import Notification from "../component/toast"; +import { useNotification } from "../component/NotificationManager"; const MonteCarloReport = () => { const { t } = useTranslation(); const [monteReports, setMonteReports] = useState({}); const [monteListsValues, setMonteListsValues] = useState([]); const [loading, setLoading] = useState(false); - const [showAppStatus, setShowAppStatus] = useState(""); - const [showNotification, setShowNotification] = useState(""); - const [showNotificationMsg, setShowNotificationMsg] = useState({}); - const [showToastContainer, setShowToastContainer] = useState(false); + + const addToast = useNotification(); function showNotificationStatus(statusKind, status, statusText) { - if (statusKind !== undefined) { - setShowAppStatus(statusKind); - } - if (status !== undefined) { - setShowNotification(status); - } - if (statusText !== undefined) { - setShowNotificationMsg(statusText); + if (statusKind && (statusKind === "error" || statusKind === "success")) { + addToast({ + id: status, + ariaLabel: statusKind, + kind: statusKind, + role: "alert", + subtitle: statusText, + timeout: "", + title: (statusKind === "error" ? (t('failed')) : (t('success'))), + }); } - if ((statusKind !== undefined && statusKind === "error") || (statusKind !== undefined && statusKind === "success")) { - setShowToastContainer(true); - } - }; - const resetShowNotification = () => { - setShowNotification(false); }; const getMonteCarloReports = async () => { @@ -91,11 +85,10 @@ const MonteCarloReport = () => { const isDarkTheme = useThemeDetector(); useEffect(() => { getMonteCarloReports(); - }, [showToastContainer]); + }, []); return ( <> -
diff --git a/frontend/src/content/Dashboard/prestoReport.js b/frontend/src/content/Dashboard/prestoReport.js index 601bf29..6d61ee1 100644 --- a/frontend/src/content/Dashboard/prestoReport.js +++ b/frontend/src/content/Dashboard/prestoReport.js @@ -13,34 +13,28 @@ import { import { getPrestoRunLists } from "../api/api"; import useThemeDetector from "../../components/theme"; import { useTranslation } from "react-i18next"; -import Notification from "../component/toast"; +import { useNotification } from "../component/NotificationManager"; const PrestoReport = () => { const { t } = useTranslation(); const [prestoReports, setPrestoReports] = useState({}); const [prestoListsValues, setPrestoListsValues] = useState([]); const [loading, setLoading] = useState(false); - const [showAppStatus, setShowAppStatus] = useState(""); - const [showNotification, setShowNotification] = useState(""); - const [showNotificationMsg, setShowNotificationMsg] = useState({}); - const [showToastContainer, setShowToastContainer] = useState(false); + + const addToast = useNotification(); function showNotificationStatus(statusKind, status, statusText) { - if (statusKind !== undefined) { - setShowAppStatus(statusKind); - } - if (status !== undefined) { - setShowNotification(status); - } - if (statusText !== undefined) { - setShowNotificationMsg(statusText); + if (statusKind && (statusKind === "error" || statusKind === "success")) { + addToast({ + id: status, + ariaLabel: statusKind, + kind: statusKind, + role: "alert", + subtitle: statusText, + timeout: "", + title: (statusKind === "error" ? (t('failed')) : (t('success'))), + }); } - if ((statusKind !== undefined && statusKind === "error") || (statusKind !== undefined && statusKind === "success")) { - setShowToastContainer(true); - } - }; - const resetShowNotification = () => { - setShowNotification(false); }; const getPrestoReports = async () => { @@ -92,11 +86,10 @@ const PrestoReport = () => { const isDarkTheme = useThemeDetector(); useEffect(() => { getPrestoReports(); - }, [showToastContainer]); + }, []); return ( <> -
diff --git a/frontend/src/content/LandingPage/LandingPage.js b/frontend/src/content/LandingPage/LandingPage.js index 1a1b354..e16c0a9 100644 --- a/frontend/src/content/LandingPage/LandingPage.js +++ b/frontend/src/content/LandingPage/LandingPage.js @@ -12,9 +12,9 @@ import HuggingFaceApp from "./huggingFace/hugging"; import ByoApplication from "./byoApp/byo"; import PrestoApp from "./presto/presto"; import { getMetadata, getAllInstances } from "../api/api"; -import Notification from "../component/toast"; import { useTranslation } from "react-i18next"; import CommonUIContext from "../component/CommonUIContext"; +import { useNotification } from "../component/NotificationManager"; function LandingPage() { const { t } = useTranslation(); @@ -22,13 +22,9 @@ function LandingPage() { const [showModal, setShowModal] = useState(false); const [hideModalChecked, setHideModalChecked] = useState(false); const [metaData, setMetaData] = useState({}); - const [showAppStatus, setShowAppStatus] = useState(""); - const [showNotification, setShowNotification] = useState(""); - const [showNotificationMsg, setShowNotificationMsg] = useState({}); - const [showErrorLogMsg, setShowErrorLogMsg] = useState(""); - const [showToastContainer, setShowToastContainer] = useState(false); const { setByoState } = useContext(CommonUIContext); + const addToast = useNotification(); const handleHideModal = () => { if (hideModalChecked) { @@ -72,24 +68,17 @@ function LandingPage() { }; function showNotificationStatus(statusKind, status, statusText, errorInfoText) { - if (statusKind !== undefined) { - setShowAppStatus(statusKind); + if (statusKind && (statusKind === "error" || statusKind === "success")) { + addToast({ + id: status, + ariaLabel: statusKind, + kind: statusKind, + role: "alert", + subtitle: statusText + (errorInfoText ? ` ${errorInfoText}` : ""), + timeout: (statusKind === "error" ? "" : "10000"), + title: (statusKind === "error" ? (t('failed')) : (t('success'))), + }); } - if (status !== undefined) { - setShowNotification(status); - } - if (statusText !== undefined) { - setShowNotificationMsg(statusText); - } - if (errorInfoText !== undefined) { - setShowErrorLogMsg(errorInfoText); - } - if ((statusKind !== undefined && statusKind === "error") || (statusKind !== undefined && statusKind === "success")) { - setShowToastContainer(true); - } - }; - const resetShowNotification = () => { - setShowNotification(false); }; useEffect(() => { const shouldShowMessage = localStorage.getItem("showModal"); @@ -103,8 +92,6 @@ function LandingPage() { return ( <> - {showAppStatus === "success" && } - {showAppStatus === "error" && } diff --git a/frontend/src/content/component/NotificationManager.js b/frontend/src/content/component/NotificationManager.js new file mode 100644 index 0000000..b2d5f27 --- /dev/null +++ b/frontend/src/content/component/NotificationManager.js @@ -0,0 +1,33 @@ +import React, { useEffect, createContext, useContext, useState } from 'react'; +import Notification from './toast'; +import { useLocation } from 'react-router-dom'; + +const NotificationContext = createContext(); + +export const useNotification = () => { + return useContext(NotificationContext); +}; + +export const NotificationProvider = ({ children }) => { + const [toasts, setToasts] = useState([]); + const location = useLocation(); + + const addToast = (toast) => { + setToasts((prevToasts) => [...prevToasts, toast]); + }; + + const removeToast = (id) => { + setToasts((prevToasts) => prevToasts.filter((toast) => toast.id !== id)); + }; + + useEffect(() => { + setToasts([]); + }, [location]); + + return ( + + {children} + + + ); +}; diff --git a/frontend/src/content/component/toast.js b/frontend/src/content/component/toast.js index 2625595..fbb07bb 100644 --- a/frontend/src/content/component/toast.js +++ b/frontend/src/content/component/toast.js @@ -1,49 +1,22 @@ -import React, { useState, useEffect } from 'react'; +import React from 'react'; import { ToastNotification } from '@carbon/react'; -import { useTranslation } from "react-i18next"; - -export default function Notification(props) { - const { t } = useTranslation(); - const [notification, setNotification] = useState(null); - const [shownMessages, setShownMessages] = useState(new Set()); - const errorMessage = t('serverError'); - const tokenExpiredMessage = "This session has expired. Refresh the session by logging in."; - - useEffect(() => { - if (props.showToastContainer) { - if ((props.subtitle.includes(errorMessage) && shownMessages.has(errorMessage)) || - (props.subtitle.includes(tokenExpiredMessage) && shownMessages.has(tokenExpiredMessage))) { - return; - } else { - showNotification(); - if (props.subtitle.includes(errorMessage)) { - setShownMessages(prev => new Set(prev).add(errorMessage)); - } - if (props.subtitle.includes(tokenExpiredMessage)) { - setShownMessages(prev => new Set(prev).add(tokenExpiredMessage)); - } - props.resetShowNotification(); - } - } - }, [props.showToastContainer, props.subtitle, props.resetShowNotification, errorMessage, tokenExpiredMessage, shownMessages]); - - const showNotification = () => { - const newNotification = ( - - ); - setNotification(newNotification); - }; - - return notification; -} \ No newline at end of file +export default function Notification({ toasts }) { + return ( +
+ {toasts.map(toast => ( + + ))} +
+ ); +} diff --git a/frontend/src/content/configuration/configurationDetails.js b/frontend/src/content/configuration/configurationDetails.js index bae2f80..5f287ce 100644 --- a/frontend/src/content/configuration/configurationDetails.js +++ b/frontend/src/content/configuration/configurationDetails.js @@ -15,18 +15,14 @@ import { } from "@carbon/react"; import { Renew } from "@carbon/icons-react"; import { getAllInstances } from "../api/api"; -import Notification from "../component/toast"; import { useTranslation } from "react-i18next"; +import { useNotification } from "../component/NotificationManager"; const BenchmarkTable = () => { const { t } = useTranslation(); const [instanceDetails, setInstanceDetails] = useState([]); const [instanceStatus, setInstanceStatus] = useState({}); const [isLoading, setisLoading] = useState(false); - const [showAppStatus, setShowAppStatus] = useState(""); - const [showNotification, setShowNotification] = useState(""); - const [showNotificationMsg, setShowNotificationMsg] = useState({}); - const [showToastContainer, setShowToastContainer] = useState(false); const headers = [ { @@ -50,24 +46,19 @@ const BenchmarkTable = () => { key: "vsiStatus", }, ]; - + const addToast = useNotification(); function showNotificationStatus(statusKind, status, statusText) { - if (statusKind !== undefined) { - setShowAppStatus(statusKind); - } - if (status !== undefined) { - setShowNotification(status); - } - if (statusText !== undefined) { - setShowNotificationMsg(statusText); + if (statusKind && (statusKind === "error" || statusKind === "success")) { + addToast({ + id: status, + ariaLabel: statusKind, + kind: statusKind, + role: "alert", + subtitle: statusText, + timeout: "", + title: (statusKind === "error" ? (t('failed')) : (t('success'))), + }); } - if ((statusKind !== undefined && statusKind === "error") || (statusKind !== undefined && statusKind === "success")) { - setShowToastContainer(true); - } - }; - - const resetShowNotification = () => { - setShowNotification(false); }; const getAllInstance = async () => { @@ -94,10 +85,9 @@ const BenchmarkTable = () => { useEffect(() => { getAllInstance(); - }, [showToastContainer]); + }, []); return ( <> - {({ rows, @@ -136,7 +126,7 @@ const BenchmarkTable = () => { {instanceStatus && rows !== null && rows !== undefined && rows.map((row, index) => ( {row.cells.map((cell, cellIndex) => ( - {cell.value} + {cell.value} ))} ))} diff --git a/frontend/src/locale/en/translation.json b/frontend/src/locale/en/translation.json index 9121986..c2db27d 100644 --- a/frontend/src/locale/en/translation.json +++ b/frontend/src/locale/en/translation.json @@ -149,6 +149,7 @@ "failedRetrieveMonteLogs": "Failed to retrieve montecarlo run logs", "FailedRetrieveAllInstances": "Failed to retrieve all instances", "failedRetrieveByoLogs": "Failed to retrieve BYO logs", + "failedRetrievePrestoLogs": "Failed to retrieve presto run logs", "loginFailed": "Login Failed", "error": "Error", "run": "Run", diff --git a/resources/repo_synch.sh b/resources/repo_synch.sh new file mode 100644 index 0000000..782e1f5 --- /dev/null +++ b/resources/repo_synch.sh @@ -0,0 +1,146 @@ +#!/bin/bash + +# Configuration for GitHub user +git config --global user.email "$GIT_USER_EMAIL" +git config --global user.name "$GIT_USER_NAME" + +# Configuration +PRIVATE_REPO_URL="https://${GITHUB_TOKEN}@github.ibm.com/workload-eng-services/sandbox-ui.git" +PUBLIC_REPO_URL="https://${GIT_USER_NAME}:${GITHUB_TOKEN1}@github.com/IBM-Cloud/sandbox-benchmark-dashboard-for-vpc.git" +PRIVATE_REPO_PATH="/workspace/private" +PUBLIC_REPO_PATH="/workspace/public" +BRANCH_NAME="${BRANCH}" + +# Function to check if a command was successful +check_command() { + if [ $? -ne 0 ]; then + echo "Error: $1" + exit 1 + fi +} + +# Ensure the repository paths exist +mkdir -p "$PRIVATE_REPO_PATH" +mkdir -p "$PUBLIC_REPO_PATH" + +# Clone repositories if not already cloned +if [ ! -d "$PRIVATE_REPO_PATH/.git" ]; then + echo "Cloning private repository..." + git clone "$PRIVATE_REPO_URL" "$PRIVATE_REPO_PATH" + check_command "Failed to clone private repository" +fi + +if [ ! -d "$PUBLIC_REPO_PATH/.git" ]; then + echo "Cloning public repository..." + git clone "$PUBLIC_REPO_URL" "$PUBLIC_REPO_PATH" + check_command "Failed to clone public repository" +fi + +# Pull latest changes from the private repo +cd "$PRIVATE_REPO_PATH" +git checkout "$BRANCH_NAME" +check_command "Failed to checkout branch $BRANCH_NAME in private repo" +git pull origin "$BRANCH_NAME" +check_command "Failed to pull changes from private repo" +cd "$PUBLIC_REPO_PATH" + +# Ensure we are on the main branch and update it +git fetch origin +git checkout main +check_command "Failed to checkout main branch in public repo" +git pull origin main +check_command "Failed to pull changes from main branch in public repo" + +# Create or checkout the branch in the public repo +if git show-ref --verify --quiet refs/remotes/origin/"$BRANCH_NAME"; then + # Branch exists on remote, check it out + git checkout "$BRANCH_NAME" + check_command "Failed to checkout branch $BRANCH_NAME in public repo" +else + # Branch does not exist, create it from main + git checkout -b "$BRANCH_NAME" + check_command "Failed to create new branch $BRANCH_NAME from main in public repo" + git push origin "$BRANCH_NAME" + check_command "Failed to push new branch $BRANCH_NAME to public repo" +fi + +# Pull the latest changes from the branch +git pull origin "$BRANCH_NAME" +check_command "Failed to pull changes from branch $BRANCH_NAME in public repo" + +# Copy changes from the private repo to the public repo +cd "$PRIVATE_REPO_PATH" +for item in *; do + if [ -d "$item" ]; then + cp -r "$item" "$PUBLIC_REPO_PATH/" + else + cp "$item" "$PUBLIC_REPO_PATH/" + fi +done + +cd "$PUBLIC_REPO_PATH" +# Remove rsync.sh file if it exists in the public repo +if [ -f "$PUBLIC_REPO_PATH/resources/rsync.sh" ]; then + rm "$PUBLIC_REPO_PATH/resources/rsync.sh" + check_command "Failed to remove rsync.sh from public repo" +fi + +# Add, commit, and push changes to the public repo +git add . +if git diff --cached --quiet; then + echo "No changes to commit." +else + git commit -m "Syncing changes from private repo: $(date)" + check_command "Failed to commit changes to public repo" + git push origin "$BRANCH_NAME" + check_command "Failed to push changes to public repo" + echo "Changes successfully synced from private to public repo!" +fi + +# Set variables +PR_TITLE="Sync changes from ${BRANCH_NAME} to ${MAIN_BRANCH}" +PR_BODY="This pull request syncs changes from branch ${BRANCH_NAME} to the main branch." +REPO="IBM-Cloud/sandbox-benchmark-dashboard-for-vpc" +API_URL="https://api.github.com/repos/$REPO" + +# Create pull request +echo "Creating pull request..." +PR_RESPONSE=$(curl -s -X POST "$API_URL/pulls" \ + -H "Authorization: token $GITHUB_TOKEN1" \ + -H "Accept: application/vnd.github.v3+json" \ + -d "{ + \"title\": \"$PR_TITLE\", + \"body\": \"$PR_BODY\", + \"head\": \"$BRANCH_NAME\", + \"base\": \"$MAIN_BRANCH\" + }") + +# Extract pull request number +PR_NUMBER=$(echo $PR_RESPONSE | jq -r .number) + +if [ -z "$PR_NUMBER" ] || [ "$PR_NUMBER" == "null" ]; then + echo "Failed to create pull request. Response: $PR_RESPONSE" + exit 1 +fi + +echo "Pull request #$PR_NUMBER created successfully." + +# Convert space-separated reviewers to JSON array +REVIEWERS_JSON=$(echo $REVIEWERS | jq -R 'split(" ")') + +# Add reviewers +echo "Adding reviewers..." +REVIEWER_RESPONSE=$(curl -s -X POST "$API_URL/pulls/$PR_NUMBER/requested_reviewers" \ + -H "Authorization: token $GITHUB_TOKEN1" \ + -H "Accept: application/vnd.github.v3+json" \ + -d "{ + \"reviewers\": $REVIEWERS_JSON + }") + +echo "Reviewer addition response: $REVIEWER_RESPONSE" + +echo "PR raised successfully and sent $REVIEWERS to Review" + +#echo "Cleaning up repository directory..." +rm -rf "$PRIVATE_REPO_PATH" +rm -rf "$PUBLIC_REPO_PATH" diff --git a/template/pull_request_template.md b/template/pull_request_template.md new file mode 100644 index 0000000..5c4359c --- /dev/null +++ b/template/pull_request_template.md @@ -0,0 +1,26 @@ +PR Template +--- + +Fill out this template where applicable and as much as possible. For any sections that is not relevant, then answer `N/A`. + +**Description:** +- Provide a brief overview and intent of the changes. +- Describe where the changes were made. +- Artifacts, or screenshots + +**Type of PR:** `bug fix|new feature|documentation update` + +**Issue Reference:** `Link to the issue (doc, git issue, epic, Aha, etc)` + +**Technical Details:** +- Highlight the changes and describe the approach. +- List any dependencies (ie, libraries, packages, etc) + +**Testing:** +- Link to test results docs or repos. +- Is this automated: `Y|N` + - If No, any plans to automate. If yes and plan to automate, then provide open git issue. + +**Documentation** +- Does this require documentation changes: `Y|N` +- If yes, provide link to the open git issue for documentation changes? The new doc git issue should list out docs/links.