diff --git a/.env.development b/.env.development index 1451cfd..2f1d4e1 100644 --- a/.env.development +++ b/.env.development @@ -1,3 +1 @@ REACT_APP_USE_AUTHENTICATION=false - -REACT_APP_SRV_STUDY_URI=study-server diff --git a/README.md b/README.md index 3491c20..07acc6a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ This template setup the authentication mechanism and provides a configured empty To customize this repository for an app, search and replace the string `XXX` with the name of the app. For example, GridXXX -> GridFoo, gridXXX-app -> gridfoo-app. -Create a new view in study-server and replace `yyy` with the new token in rest api `src/rest/study.ts`. +Create a new view in study-server and replace `yyy` with the new token in rest api `src/services/index.ts`. ## Typescript config diff --git a/package-lock.json b/package-lock.json index 26c7c0b..ec43880 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,6 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", - "@gridsuite/commons-ui": "0.63.0", "@hookform/resolvers": "^3.3.4", "@mui/icons-material": "^5.15.14", "@mui/lab": "^5.0.0-alpha.169", @@ -36,6 +35,7 @@ }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@gridsuite/commons-ui": "file:../_frontend/commons-ui/gridsuite-commons-ui-0.63.4.tgz", "@types/core-js": "^2.5.8", "@types/eslint-config-prettier": "^6.11.3", "@types/jest": "^27.5.2", @@ -2753,9 +2753,11 @@ } }, "node_modules/@gridsuite/commons-ui": { - "version": "0.63.0", - "resolved": "https://registry.npmjs.org/@gridsuite/commons-ui/-/commons-ui-0.63.0.tgz", - "integrity": "sha512-+hO2HUv2D/rd1UX5BBw7O/FDbpUXrjCm/fVSAeBT/Ml72CROhva458aBPczkjb9sg/lxV8U3L2tRAwhyuuI/iQ==", + "version": "0.63.4", + "resolved": "file:../_frontend/commons-ui/gridsuite-commons-ui-0.63.4.tgz", + "integrity": "sha512-3vvFWa40RY81G57P5H8zUUbAhUwcd1ayB1NWWZnHRvizq+0woRcw9qiy+bTdnt0mA7qznq9a5+IMcQU2bOPQPw==", + "dev": true, + "license": "MPL-2.0", "dependencies": { "@react-querybuilder/dnd": "^7.2.0", "@react-querybuilder/material": "^7.2.0", @@ -2771,6 +2773,7 @@ "react-dnd-html5-backend": "^16.0.1", "react-querybuilder": "^7.2.0", "react-virtualized": "^9.22.5", + "type-fest": "^4.21.0", "uuid": "^9.0.1" }, "engines": { @@ -2796,6 +2799,7 @@ "react-intl": "^6.6.4", "react-papaparse": "^4.1.0", "react-router-dom": "^6.22.3", + "reconnecting-websocket": "^4.4.0", "yup": "^1.4.0" } }, @@ -2803,10 +2807,23 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", + "dev": true, "engines": { "node": ">=6" } }, + "node_modules/@gridsuite/commons-ui/node_modules/type-fest": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.23.0.tgz", + "integrity": "sha512-ZiBujro2ohr5+Z/hZWHESLz3g08BBdrdLMieYFULJO+tWc437sn8kQsWLJoZErY8alNhxre9K4p3GURAG11n+w==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@hookform/resolvers": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.3.4.tgz", @@ -4690,22 +4707,26 @@ "node_modules/@react-dnd/asap": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", - "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==", + "dev": true }, "node_modules/@react-dnd/invariant": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", - "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==", + "dev": true }, "node_modules/@react-dnd/shallowequal": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", - "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==", + "dev": true }, "node_modules/@react-querybuilder/dnd": { "version": "7.5.2", "resolved": "https://registry.npmjs.org/@react-querybuilder/dnd/-/dnd-7.5.2.tgz", "integrity": "sha512-7rMeZ6gFQow2jpz0exMQSq/f3MZEL5OuiWW6YOkDuFfJWPR7ijXV6Cz0x7TiFS7jbTD5Axr44u7nFCWx06hZeg==", + "dev": true, "peerDependencies": { "react": ">=18", "react-dnd": ">=14.0.0", @@ -4717,6 +4738,7 @@ "version": "7.5.2", "resolved": "https://registry.npmjs.org/@react-querybuilder/material/-/material-7.5.2.tgz", "integrity": "sha512-wWjwKjaD5LIX9Lta3QE/smd9lYEWzIAR7A93XyHHwv8BtP1EQoZgjKogNCSuQZhfkrTT5U1n9g4uORCUk7Z9ZQ==", + "dev": true, "peerDependencies": { "@emotion/react": "^11", "@emotion/styled": "^11", @@ -5329,6 +5351,7 @@ "version": "5.3.14", "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.14.tgz", "integrity": "sha512-LxJ4iEFcpqc6METwp9f6BV6VVc43m6MfH0VqFosHvrUgfXiFe6ww7R3itkOQ+TCK6Y+Iv/+RnnvtRZnkc5Kc9g==", + "dev": true, "peer": true, "dependencies": { "@types/node": "*" @@ -6124,12 +6147,14 @@ "version": "31.3.2", "resolved": "https://registry.npmjs.org/ag-grid-community/-/ag-grid-community-31.3.2.tgz", "integrity": "sha512-GxqFRD0OcjaVRE1gwLgoP0oERNPH8Lk8wKJ1txulsxysEQ5dZWHhiIoXXSiHjvOCVMkK/F5qzY6HNrn6VeDMTQ==", + "dev": true, "peer": true }, "node_modules/ag-grid-react": { "version": "31.3.2", "resolved": "https://registry.npmjs.org/ag-grid-react/-/ag-grid-react-31.3.2.tgz", "integrity": "sha512-SFHN05bsXp901rIT00Fa6iQLCtyavoJiKaXEDUtAU5LMu+GTkjs/FPQBQ8754omgdDFr4NsS3Ri6QbqBne3rug==", + "dev": true, "peer": true, "dependencies": { "ag-grid-community": "31.3.2", @@ -6545,6 +6570,7 @@ "version": "3.3.4", "resolved": "https://registry.npmjs.org/autosuggest-highlight/-/autosuggest-highlight-3.3.4.tgz", "integrity": "sha512-j6RETBD2xYnrVcoV1S5R4t3WxOlWZKyDQjkwnggDPSjF5L4jV98ZltBpvPvbkM1HtoSe5o+bNrTHyjPbieGeYA==", + "dev": true, "dependencies": { "remove-accents": "^0.4.2" } @@ -6911,6 +6937,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, "funding": [ { "type": "github", @@ -7607,7 +7634,8 @@ "node_modules/crypto-js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "dev": true }, "node_modules/crypto-random-string": { "version": "2.0.0", @@ -8310,6 +8338,7 @@ "version": "16.0.1", "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "dev": true, "dependencies": { "@react-dnd/asap": "^5.0.1", "@react-dnd/invariant": "^4.0.1", @@ -8320,6 +8349,7 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dev": true, "dependencies": { "@babel/runtime": "^7.9.2" } @@ -9647,7 +9677,8 @@ "node_modules/file-saver": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", - "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==", + "dev": true }, "node_modules/filelist": { "version": "1.0.4", @@ -14688,6 +14719,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "dev": true, "engines": { "node": ">=18" } @@ -14836,7 +14868,8 @@ "node_modules/localized-countries": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/localized-countries/-/localized-countries-2.0.0.tgz", - "integrity": "sha512-17CwJ/oetI5cB6KmPa+Cxw/8vunawUouVv5MFxYqsVzryXeR5u/MF32m3SBaHxP4EqYPOePV+3jx7xxYZnAJpQ==" + "integrity": "sha512-17CwJ/oetI5cB6KmPa+Cxw/8vunawUouVv5MFxYqsVzryXeR5u/MF32m3SBaHxP4EqYPOePV+3jx7xxYZnAJpQ==", + "dev": true }, "node_modules/locate-path": { "version": "6.0.0", @@ -14966,7 +14999,8 @@ "node_modules/memoize-one": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "dev": true }, "node_modules/merge-descriptors": { "version": "1.0.1", @@ -15343,6 +15377,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/numeric-quantity/-/numeric-quantity-2.0.1.tgz", "integrity": "sha512-+Bt2X6YxM5bg8XIBl76NVeG2eL0Y5VQRoyz6GLYrZXW/TDh7We+tGeX4/WZWhaVGOg5ZjNBEOZt9a86slMhOJA==", + "dev": true, "engines": { "node": ">=16" } @@ -15505,6 +15540,7 @@ "version": "1.11.5", "resolved": "https://registry.npmjs.org/oidc-client/-/oidc-client-1.11.5.tgz", "integrity": "sha512-LcKrKC8Av0m/KD/4EFmo9Sg8fSQ+WFJWBrmtWd+tZkNn3WT/sQG3REmPANE9tzzhbjW6VkTNy4xhAXCfPApAOg==", + "dev": true, "dependencies": { "acorn": "^7.4.1", "base64-js": "^1.5.1", @@ -15517,6 +15553,7 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -15677,6 +15714,7 @@ "version": "5.4.1", "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==", + "dev": true, "peer": true }, "node_modules/param-case": { @@ -17505,6 +17543,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/react-csv-downloader/-/react-csv-downloader-3.1.0.tgz", "integrity": "sha512-ART4CNMU89Y5OZQp9gR3IUlVt541djWwRfokQY+CYsGQjHNZFJAk6FvgOwLUNG0o/CxeUdexYjnMMFinMv4Xjg==", + "dev": true, "dependencies": { "file-saver": "^2.0.2" }, @@ -17625,6 +17664,7 @@ "version": "16.0.1", "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "dev": true, "dependencies": { "@react-dnd/invariant": "^4.0.1", "@react-dnd/shallowequal": "^4.0.1", @@ -17654,6 +17694,7 @@ "version": "16.0.1", "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", + "dev": true, "dependencies": { "dnd-core": "^16.0.1" } @@ -17724,12 +17765,14 @@ "node_modules/react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "dev": true }, "node_modules/react-papaparse": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/react-papaparse/-/react-papaparse-4.4.0.tgz", "integrity": "sha512-xTEwHZYJ+1dh9mQDQjjwJXmWyX20DdZ52u+ddw75V+Xm5qsjXSvWmC7c8K82vRwMjKAOH2S9uFyGpHEyEztkUQ==", + "dev": true, "peer": true, "dependencies": { "@types/papaparse": "^5.3.9", @@ -17744,6 +17787,7 @@ "version": "7.5.0", "resolved": "https://registry.npmjs.org/react-querybuilder/-/react-querybuilder-7.5.0.tgz", "integrity": "sha512-tWrwLf2dPkI1TYluW6Oy/Tif/Z0X8OBoDXkh2C/oloAItPJEmKAcInuIYOlEKDVK6KM6lHJsGNBmApuMWY5c/g==", + "dev": true, "dependencies": { "@reduxjs/toolkit": "^2.2.5", "clsx": "^2.1.1", @@ -17759,6 +17803,7 @@ "version": "2.2.6", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.6.tgz", "integrity": "sha512-kH0r495c5z1t0g796eDQAkYbEQ3a1OLYN9o8jQQVZyKyw367pfRGS+qZLkHYvFHiUUdafpoSlQ2QYObIApjPWA==", + "dev": true, "dependencies": { "immer": "^10.0.3", "redux": "^5.0.1", @@ -17782,6 +17827,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, "engines": { "node": ">=6" } @@ -17790,6 +17836,7 @@ "version": "10.1.1", "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", + "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -17799,6 +17846,7 @@ "version": "9.1.2", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz", "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==", + "dev": true, "dependencies": { "@types/use-sync-external-store": "^0.0.3", "use-sync-external-store": "^1.0.0" @@ -17821,6 +17869,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "dev": true, "peerDependencies": { "redux": "^5.0.0" } @@ -17828,7 +17877,8 @@ "node_modules/react-querybuilder/node_modules/reselect": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", - "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "dev": true }, "node_modules/react-redux": { "version": "8.1.3", @@ -18027,6 +18077,7 @@ "version": "9.22.5", "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.5.tgz", "integrity": "sha512-YqQMRzlVANBv1L/7r63OHa2b0ZsAaDp1UhVNEdUaXI8A5u6hTpA5NYtUueLH2rFuY/27mTGIBl7ZhqFKzw18YQ==", + "dev": true, "dependencies": { "@babel/runtime": "^7.7.2", "clsx": "^1.0.4", @@ -18290,7 +18341,8 @@ "node_modules/remove-accents": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.4.tgz", - "integrity": "sha512-EpFcOa/ISetVHEXqu+VwI96KZBmq+a8LJnGkaeFw45epGlxIZz5dhEEnNZMsQXgORu3qaMoLX4qJCzOik6ytAg==" + "integrity": "sha512-EpFcOa/ISetVHEXqu+VwI96KZBmq+a8LJnGkaeFw45epGlxIZz5dhEEnNZMsQXgORu3qaMoLX4qJCzOik6ytAg==", + "dev": true }, "node_modules/renderkid": { "version": "3.0.0", @@ -20466,6 +20518,7 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" diff --git a/package.json b/package.json index 6ccfedb..4f8a634 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", - "@gridsuite/commons-ui": "0.63.0", "@hookform/resolvers": "^3.3.4", "@mui/icons-material": "^5.15.14", "@mui/lab": "^5.0.0-alpha.169", @@ -82,6 +81,7 @@ }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@gridsuite/commons-ui": "file:../_frontend/commons-ui/gridsuite-commons-ui-0.63.4.tgz", "@types/core-js": "^2.5.8", "@types/eslint-config-prettier": "^6.11.3", "@types/jest": "^27.5.2", diff --git a/src/components/app-top-bar.tsx b/src/components/app-top-bar.tsx index a77bcfd..ec5be73 100644 --- a/src/components/app-top-bar.tsx +++ b/src/components/app-top-bar.tsx @@ -6,17 +6,24 @@ */ import React, { FunctionComponent, useCallback, useEffect, useState } from 'react'; -import { LIGHT_THEME, logout, TopBar, UserManagerState } from '@gridsuite/commons-ui'; import Parameters, { useParameterState } from './parameters'; -import { APP_NAME, PARAM_LANGUAGE, PARAM_THEME } from '../utils/config-params'; +import { APP_NAME } from '../utils/config-params'; import { useDispatch, useSelector } from 'react-redux'; -import { fetchAppsAndUrls, fetchVersion, MetadataJson } from '../utils/rest-api'; -import { getServersInfos } from '../rest/study'; +import { appsMetadataSrv, studySrv } from '../services'; import { useNavigate } from 'react-router-dom'; import { ReactComponent as PowsyblLogo } from '../images/powsybl_logo.svg'; import AppPackage from '../../package.json'; import { AppState } from '../redux/reducer'; import { AppDispatch } from '../redux/store'; +import { + AppMetadataCommon, + LIGHT_THEME, + logout, + PARAM_LANGUAGE, + PARAM_THEME, + TopBar, + UserManagerState, +} from '@gridsuite/commons-ui'; export type AppTopBarProps = { user?: AppState['user']; @@ -27,7 +34,7 @@ const AppTopBar: FunctionComponent = (props) => { const dispatch = useDispatch(); - const [appsAndUrls, setAppsAndUrls] = useState([]); + const [appsAndUrls, setAppsAndUrls] = useState([]); const theme = useSelector((state: AppState) => state[PARAM_THEME]); @@ -38,14 +45,23 @@ const AppTopBar: FunctionComponent = (props) => { const [showParameters, setShowParameters] = useState(false); const displayParameters = useCallback(() => setShowParameters(true), []); const hideParameters = useCallback(() => setShowParameters(false), []); + const onLogoutClick = useCallback( + () => logout(dispatch, props.userManager.instance), + [dispatch, props.userManager.instance] + ); useEffect(() => { - if (props.user !== null) { - fetchAppsAndUrls().then((res) => { + if (props.user !== undefined) { + appsMetadataSrv.fetchAppsMetadata().then((res) => { setAppsAndUrls(res); }); } }, [props.user]); + const globalVersionFetcher = useCallback( + () => appsMetadataSrv.fetchVersion().then((res) => res?.deployVersion ?? 'unknown'), + [] + ); + const additionalModulesFetcher = useCallback(() => studySrv.getServersInfos('yyy'), []); return ( <> @@ -62,12 +78,12 @@ const AppTopBar: FunctionComponent = (props) => { appVersion={AppPackage.version} appLicense={AppPackage.license} onParametersClick={displayParameters} - onLogoutClick={() => logout(dispatch, props.userManager.instance)} + onLogoutClick={onLogoutClick} onLogoClick={() => navigate('/', { replace: true })} - user={props.user ?? undefined} + user={props.user} appsAndUrls={appsAndUrls} - globalVersionPromise={() => fetchVersion().then((res) => res?.deployVersion ?? 'unknown')} - additionalModulesPromise={getServersInfos} + globalVersionPromise={globalVersionFetcher} + additionalModulesPromise={additionalModulesFetcher} onThemeClick={handleChangeTheme} theme={themeLocal} onLanguageClick={handleChangeLanguage} diff --git a/src/components/app-wrapper.tsx b/src/components/app-wrapper.tsx index c9ec50f..4f06d8a 100644 --- a/src/components/app-wrapper.tsx +++ b/src/components/app-wrapper.tsx @@ -17,6 +17,7 @@ import { LIGHT_THEME, login_en, login_fr, + PARAM_THEME, SnackbarProvider, top_bar_en, top_bar_fr, @@ -29,7 +30,6 @@ import messages_fr from '../translations/fr.json'; import messages_plugins_en from '../plugins/translations/en.json'; import messages_plugins_fr from '../plugins/translations/fr.json'; import { store } from '../redux/store'; -import { PARAM_THEME } from '../utils/config-params'; import { IntlConfig } from 'react-intl/src/types'; import { AppState } from '../redux/reducer'; diff --git a/src/components/app.tsx b/src/components/app.tsx index 983e728..ab2ecd9 100644 --- a/src/components/app.tsx +++ b/src/components/app.tsx @@ -13,26 +13,23 @@ import { Box, Typography } from '@mui/material'; import { AuthenticationRouter, CardErrorBoundary, + COMMON_APP_NAME, + ConfigParameters, + getComputedLanguage, + getErrorMessage, getPreLoginPath, initializeAuthenticationDev, initializeAuthenticationProd, + PARAM_LANGUAGE, + PARAM_THEME, useSnackMessage, } from '@gridsuite/commons-ui'; import { selectComputedLanguage, selectLanguage, selectTheme } from '../redux/actions'; import { AppState } from '../redux/reducer'; -import { - ConfigParameters, - connectNotificationsWsUpdateConfig, - fetchConfigParameter, - fetchConfigParameters, - fetchIdpSettings, - fetchValidateUser, -} from '../utils/rest-api'; -import { APP_NAME, COMMON_APP_NAME, PARAM_LANGUAGE, PARAM_THEME } from '../utils/config-params'; -import { getComputedLanguage } from '../utils/language'; +import { appLocalSrv, configNotificationSrv, configSrv, userAdminSrv } from '../services'; +import { APP_NAME } from '../utils/config-params'; import AppTopBar, { AppTopBarProps } from './app-top-bar'; import ReconnectingWebSocket from 'reconnecting-websocket'; -import { getErrorMessage } from '../utils/error'; import { AppDispatch } from '../redux/store'; const App: FunctionComponent = () => { @@ -73,11 +70,12 @@ const App: FunctionComponent = () => { ); const connectNotificationsUpdateConfig = useCallback((): ReconnectingWebSocket => { - const ws = connectNotificationsWsUpdateConfig(); + const ws = configNotificationSrv.connectNotificationsWsUpdateConfig(APP_NAME); ws.onmessage = function (event) { let eventData = JSON.parse(event.data); if (eventData.headers?.parameterName) { - fetchConfigParameter(eventData.headers.parameterName) + configSrv + .fetchConfigParameter(eventData.headers.parameterName) .then((param) => updateParams([param])) .catch((error) => snackError({ @@ -116,8 +114,8 @@ const App: FunctionComponent = () => { ? initializeAuthenticationProd( dispatch, initialMatchSilentRenewCallbackUrl != null, - fetchIdpSettings, - fetchValidateUser, + appLocalSrv.fetchIdpSettings, + userAdminSrv.fetchValidateUser, initialMatchSigninCallbackUrl != null ) : initializeAuthenticationDev( @@ -141,8 +139,9 @@ const App: FunctionComponent = () => { }, [initialMatchSigninCallbackUrl, initialMatchSilentRenewCallbackUrl, dispatch]); useEffect(() => { - if (user !== null) { - fetchConfigParameters(COMMON_APP_NAME) + if (user !== undefined) { + configSrv + .fetchConfigParameters(COMMON_APP_NAME) .then((params) => updateParams(params)) .catch((error) => snackError({ @@ -151,7 +150,8 @@ const App: FunctionComponent = () => { }) ); - fetchConfigParameters(APP_NAME) + configSrv + .fetchConfigParameters(APP_NAME) .then((params) => updateParams(params)) .catch((error) => snackError({ @@ -169,7 +169,7 @@ const App: FunctionComponent = () => { <> - {user !== null ? ( + {user !== undefined ? ( (paramName: K): [AppStat const handleChangeParamLocalState = useCallback( (value: AppState[K]) => { setParamLocalState(value); - updateConfigParameter(paramName, value as string) //TODO how to check/cast? + configSrv + .updateConfigParameter(paramName, value as string) //TODO how to check/cast? .catch((error) => { setParamLocalState(paramGlobalState); snackError({ diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index 7b288b8..b8ba40c 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -1,8 +1,29 @@ -/** - * Copyright (c) 2024, RTE (http://www.rte-france.com) +/* + * Copyright © 2024, RTE (http://www.rte-france.com) * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /// + +type EnvDev = { + REACT_APP_USE_AUTHENTICATION: true; + REACT_APP_SRV_STUDY_URI: string; +}; + +type EnvProd = { + REACT_APP_USE_AUTHENTICATION: false; + REACT_APP_API_GATEWAY: string; + REACT_APP_WS_GATEWAY: string; +}; + +type EnvCompile = EnvProd | EnvDev; + +declare global { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + namespace NodeJS { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface ProcessEnv extends EnvCompile {} + } +} diff --git a/src/redux/actions.ts b/src/redux/actions.ts index 7dc9e9d..9aba0f9 100644 --- a/src/redux/actions.ts +++ b/src/redux/actions.ts @@ -5,8 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { GsTheme } from '@gridsuite/commons-ui'; -import { PARAM_LANGUAGE } from '../utils/config-params'; +import { GsTheme, PARAM_LANGUAGE } from '@gridsuite/commons-ui'; import { Action } from 'redux'; import { AppState } from './reducer'; diff --git a/src/redux/local-storage.ts b/src/redux/local-storage.ts deleted file mode 100644 index 6de769e..0000000 --- a/src/redux/local-storage.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2020, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import { DARK_THEME, GsLang, GsLangUser, GsTheme, LANG_SYSTEM } from '@gridsuite/commons-ui'; -import { getComputedLanguage } from '../utils/language'; -import { APP_NAME } from '../utils/config-params'; - -const LOCAL_STORAGE_THEME_KEY = (APP_NAME + '_THEME').toUpperCase(); -const LOCAL_STORAGE_LANGUAGE_KEY = (APP_NAME + '_LANGUAGE').toUpperCase(); - -export function getLocalStorageTheme() { - return (localStorage.getItem(LOCAL_STORAGE_THEME_KEY) as GsTheme) || DARK_THEME; -} - -export function saveLocalStorageTheme(theme: GsTheme): void { - localStorage.setItem(LOCAL_STORAGE_THEME_KEY, theme); -} - -export function getLocalStorageLanguage() { - return (localStorage.getItem(LOCAL_STORAGE_LANGUAGE_KEY) as GsLang) || LANG_SYSTEM; -} - -export function saveLocalStorageLanguage(language: GsLang): void { - localStorage.setItem(LOCAL_STORAGE_LANGUAGE_KEY, language); -} - -export function getLocalStorageComputedLanguage(): GsLangUser { - return getComputedLanguage(getLocalStorageLanguage()); -} diff --git a/src/redux/reducer.ts b/src/redux/reducer.ts index 65220fb..0319e27 100644 --- a/src/redux/reducer.ts +++ b/src/redux/reducer.ts @@ -6,24 +6,23 @@ */ import { createReducer, Draft } from '@reduxjs/toolkit'; -import { - getLocalStorageComputedLanguage, - getLocalStorageLanguage, - getLocalStorageTheme, - saveLocalStorageTheme, -} from './local-storage'; import { ComputedLanguageAction, LanguageAction, SELECT_COMPUTED_LANGUAGE, SELECT_THEME, ThemeAction } from './actions'; import { AuthenticationActions, AuthenticationRouterErrorAction, AuthenticationRouterErrorState, - CommonStoreState, + getLocalStorageComputedLanguage, + getLocalStorageLanguage, + getLocalStorageTheme, GsLang, GsLangUser, GsTheme, LOGOUT_ERROR, LogoutErrorAction, + PARAM_LANGUAGE, + PARAM_THEME, RESET_AUTHENTICATION_ROUTER_ERROR, + saveLocalStorageTheme, SHOW_AUTH_INFO_LOGIN, ShowAuthenticationRouterLoginAction, SIGNIN_CALLBACK_ERROR, @@ -35,10 +34,12 @@ import { UserAction, UserValidationErrorAction, } from '@gridsuite/commons-ui'; -import { PARAM_LANGUAGE, PARAM_THEME } from '../utils/config-params'; import { ReducerWithInitialState } from '@reduxjs/toolkit/dist/createReducer'; +import { User } from 'oidc-client'; +import { APP_NAME } from '../utils/config-params'; -export type AppState = CommonStoreState & { +export type AppState = { + user: User | undefined; computedLanguage: GsLangUser; [PARAM_THEME]: GsTheme; [PARAM_LANGUAGE]: GsLang; @@ -50,15 +51,15 @@ export type AppState = CommonStoreState & { const initialState: AppState = { // authentication - user: null, + user: undefined, signInCallbackError: null, authenticationRouterError: null, showAuthenticationRouterLogin: false, // params - [PARAM_THEME]: getLocalStorageTheme(), - [PARAM_LANGUAGE]: getLocalStorageLanguage(), - computedLanguage: getLocalStorageComputedLanguage(), + [PARAM_THEME]: getLocalStorageTheme(APP_NAME), + [PARAM_LANGUAGE]: getLocalStorageLanguage(APP_NAME), + computedLanguage: getLocalStorageComputedLanguage(APP_NAME), }; export type Actions = AuthenticationActions | ThemeAction | LanguageAction | ComputedLanguageAction; @@ -68,7 +69,7 @@ export type AppStateKey = keyof AppState; export const reducer: ReducerWithInitialState = createReducer(initialState, (builder) => { builder.addCase(SELECT_THEME, (state: Draft, action: ThemeAction) => { state.theme = action.theme; - saveLocalStorageTheme(state.theme); + saveLocalStorageTheme(APP_NAME, state.theme); }); builder.addCase(USER, (state: Draft, action: UserAction) => { diff --git a/src/redux/store.ts b/src/redux/store.ts index 89ab5af..a529a3c 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -6,6 +6,12 @@ */ import { createStore, Store } from 'redux'; import { Actions, AppState, reducer } from './reducer'; +import { initCommonServices } from '@gridsuite/commons-ui'; +import { APP_NAME } from '../utils/config-params'; export const store: Store = createStore(reducer); export type AppDispatch = typeof store.dispatch; + +export function getUser() { + return store.getState().user; +} diff --git a/src/rest/study.ts b/src/rest/study.ts deleted file mode 100644 index 9395b7d..0000000 --- a/src/rest/study.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) 2023, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -import { GridSuiteModule } from '@gridsuite/commons-ui'; -import { backendFetchJson } from '../utils/rest-api'; -import { getErrorMessage } from '../utils/error'; - -const API_URL = - '/api/' + - (process.env.REACT_APP_USE_AUTHENTICATION === 'true' - ? `${process.env.REACT_APP_API_GATEWAY}/study/v1` - : `${process.env.REACT_APP_SRV_STUDY_URI}/v1`); - -export function getServersInfos() { - return backendFetchJson(`${API_URL}/servers/about?view=yyy`, { - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - cache: 'default', - }).catch((error) => { - console.error(`Error while fetching the servers infos : ${getErrorMessage(error)}`); - throw error; - }) as Promise; -} diff --git a/src/services/app-local.ts b/src/services/app-local.ts new file mode 100644 index 0000000..564acb0 --- /dev/null +++ b/src/services/app-local.ts @@ -0,0 +1,19 @@ +/* + * Copyright © 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +import { AppLocalComSvc, Env } from '@gridsuite/commons-ui'; + +export type EnvJson = Env & typeof import('../../public/env.json'); + +export default class AppLocalSvc extends AppLocalComSvc { + public constructor() { + super(); + } + + public async fetchEnv() { + return (await super.fetchEnv()) as EnvJson; + } +} diff --git a/src/services/index.ts b/src/services/index.ts new file mode 100644 index 0000000..71620bb --- /dev/null +++ b/src/services/index.ts @@ -0,0 +1,51 @@ +/* + * Copyright © 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { + AppsMetadataComSvc, + ConfigComSvc, + ConfigNotificationComSvc, + DirectoryComSvc, + ExploreComSvc, + setCommonServices, + StudyComSvc, + UrlString, + UserAdminComSvc, +} from '@gridsuite/commons-ui'; +import AppLocalSvc from './app-local'; +import { getUser } from '../redux/store'; +import { APP_NAME } from '../utils/config-params'; + +export type { EnvJson } from './app-local'; + +// If you want to use user-admin-server in dev mode you must avoid passing through gateway +// and use the user-admin-server directly. SetupProxy should allow this. +// const PREFIX_USER_ADMIN_SERVER_QUERIES = +// process.env.REACT_APP_API_PREFIX + +// (process.env.REACT_APP_USE_AUTHENTICATION === 'true' +// ? `${process.env.REACT_APP_API_GATEWAY}/user-admin` +// : process.env.REACT_APP_USER_ADMIN_URI); + +export const appLocalSrv = new AppLocalSvc(), + appsMetadataSrv = new AppsMetadataComSvc(appLocalSrv), + configSrv = new ConfigComSvc(APP_NAME, getUser, process.env.REACT_APP_API_GATEWAY as UrlString), + configNotificationSrv = new ConfigNotificationComSvc(getUser, process.env.REACT_APP_WS_GATEWAY as UrlString), + directorySrv = new DirectoryComSvc(getUser, process.env.REACT_APP_API_GATEWAY as UrlString), + exploreSrv = new ExploreComSvc(getUser, process.env.REACT_APP_API_GATEWAY as UrlString), + studySrv = new StudyComSvc(getUser, process.env.REACT_APP_API_GATEWAY as UrlString), + userAdminSrv = new UserAdminComSvc(getUser, process.env.REACT_APP_API_GATEWAY as UrlString); + +setCommonServices( + appLocalSrv, + appsMetadataSrv, + configSrv, + configNotificationSrv, + directorySrv, + exploreSrv, + studySrv, + userAdminSrv +); diff --git a/src/setupProxy.js b/src/setupProxy.js index 4b47f9b..7b1b15b 100644 --- a/src/setupProxy.js +++ b/src/setupProxy.js @@ -18,9 +18,5 @@ module.exports = function (app) { ws: true, }) ); - app.use( - createProxyMiddleware('http://localhost:5001/api/study-server', { - pathRewrite: { '^/api/study-server/': '/' }, - }) - ); + // TODO: redirect localhost:9000/api/gateway/study directly to http://localhost:5001/ }; diff --git a/src/utils/config-params.ts b/src/utils/config-params.ts index 177a13f..bf6eaf7 100644 --- a/src/utils/config-params.ts +++ b/src/utils/config-params.ts @@ -5,22 +5,4 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -export const COMMON_APP_NAME = 'common'; export const APP_NAME = 'XXX'; - -export const PARAM_THEME = 'theme'; -export const PARAM_LANGUAGE = 'language'; - -const COMMON_CONFIG_PARAMS_NAMES = new Set([PARAM_THEME, PARAM_LANGUAGE]); - -export type AppConfigParameter = typeof PARAM_THEME | typeof PARAM_LANGUAGE | string; - -export type AppConfigType = typeof COMMON_APP_NAME | typeof APP_NAME; - -/** - * Permit knowing if a parameter is common/shared between webapps or is specific to this application. - * @param paramName the parameter name/key - */ -export function getAppName(paramName: AppConfigParameter): AppConfigType { - return COMMON_CONFIG_PARAMS_NAMES.has(paramName) ? COMMON_APP_NAME : APP_NAME; -} diff --git a/src/utils/error.ts b/src/utils/error.ts deleted file mode 100644 index c8959d9..0000000 --- a/src/utils/error.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) 2024, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -export function getErrorMessage(error: unknown): string | null { - if (error instanceof Error) { - return error.message; - } else if (error instanceof Object && 'message' in error) { - if ( - typeof error.message === 'string' || - typeof error.message === 'number' || - typeof error.message === 'boolean' - ) { - return `${error.message}`; - } else { - return JSON.stringify(error.message ?? undefined) ?? null; - } - } else if (typeof error === 'string') { - return error; - } else { - return JSON.stringify(error ?? undefined) ?? null; - } -} diff --git a/src/utils/language.ts b/src/utils/language.ts deleted file mode 100644 index 725f944..0000000 --- a/src/utils/language.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2021, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import { GsLang, GsLangUser, LANG_ENGLISH, LANG_FRENCH, LANG_SYSTEM } from '@gridsuite/commons-ui'; - -const supportedLanguages = [LANG_FRENCH, LANG_ENGLISH]; - -export function getSystemLanguage(): GsLangUser { - const systemLanguage = navigator.language.split(/[-_]/)[0]; - return supportedLanguages.includes(systemLanguage) ? (systemLanguage as GsLangUser) : LANG_ENGLISH; -} - -export function getComputedLanguage(language: GsLang) { - return language === LANG_SYSTEM ? getSystemLanguage() : language; -} diff --git a/src/utils/rest-api.ts b/src/utils/rest-api.ts deleted file mode 100644 index 1cdee09..0000000 --- a/src/utils/rest-api.ts +++ /dev/null @@ -1,240 +0,0 @@ -/** - * Copyright (c) 2020, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import { Env, GsLangUser, GsTheme } from '@gridsuite/commons-ui'; -import { User } from 'oidc-client'; -import { APP_NAME, getAppName, PARAM_LANGUAGE, PARAM_THEME } from './config-params'; -import { store } from '../redux/store'; -import ReconnectingWebSocket, { Event } from 'reconnecting-websocket'; -import { AppState } from '../redux/reducer'; -import { getErrorMessage } from './error'; - -export interface ErrorWithStatus extends Error { - status?: number; -} - -// TODO remove when exported in commons-ui (src/utils/AuthService.ts) -type IdpSettings = { - authority: string; - client_id: string; - redirect_uri: string; - post_logout_redirect_uri: string; - silent_redirect_uri: string; - scope: string; - maxExpiresIn?: number; -}; - -export type Url = string | URL; -export type InitRequest = Partial; -export type Token = string; - -const PREFIX_USER_ADMIN_SERVER_QUERIES = `${process.env.REACT_APP_API_GATEWAY}/user-admin`; - -// If you want to use user-admin-server in dev mode you must avoid passing through gateway -// and use the user-admin-server directly. SetupProxy should allow this. -// const PREFIX_USER_ADMIN_SERVER_QUERIES = -// process.env.REACT_APP_API_PREFIX + -// (process.env.REACT_APP_USE_AUTHENTICATION === 'true' -// ? `${process.env.REACT_APP_API_GATEWAY}/user-admin` -// : process.env.REACT_APP_USER_ADMIN_URI); - -const PREFIX_CONFIG_QUERIES = `${process.env.REACT_APP_API_GATEWAY}/config`; -const PREFIX_CONFIG_NOTIFICATION_WS = `${process.env.REACT_APP_WS_GATEWAY}/config-notification`; - -function getToken(): Token | null { - const state: AppState = store.getState(); - return state.user?.id_token ?? null; -} - -export function connectNotificationsWsUpdateConfig(): ReconnectingWebSocket { - const webSocketBaseUrl = document.baseURI.replace(/^http:\/\//, 'ws://').replace(/^https:\/\//, 'wss://'); - const webSocketUrl = `${webSocketBaseUrl}${PREFIX_CONFIG_NOTIFICATION_WS}/notify?appName=${APP_NAME}`; - - const reconnectingWebSocket = new ReconnectingWebSocket(() => `${webSocketUrl}&access_token=${getToken()}`); - reconnectingWebSocket.onopen = function (event: Event) { - console.info(`Connected Websocket update config ui ${webSocketUrl} ...`); - }; - return reconnectingWebSocket; -} - -function parseError(text: string) { - try { - return JSON.parse(text); - } catch (err) { - return null; - } -} - -function handleError(response: Response): Promise { - return response.text().then((text: string) => { - const errorName = 'HttpResponseError : '; - let error: ErrorWithStatus; - const errorJson = parseError(text); - if (errorJson?.status && errorJson?.error && errorJson?.message) { - error = new Error( - `${errorName}${errorJson.status} ${errorJson.error}, message : ${errorJson.message}` - ) as ErrorWithStatus; - error.status = errorJson.status; - } else { - error = new Error(`${errorName}${response.status} ${response.statusText}`) as ErrorWithStatus; - error.status = response.status; - } - throw error; - }); -} - -function prepareRequest(init?: InitRequest, token?: Token): RequestInit { - if (!(typeof init == 'undefined' || typeof init == 'object')) { - throw new TypeError(`Argument 2 of backendFetch is not an object ${typeof init}`); - } - const initCopy: RequestInit = { ...init }; - initCopy.headers = new Headers(initCopy.headers || {}); - const tokenCopy = token || getToken(); - initCopy.headers.append('Authorization', `Bearer ${tokenCopy}`); - return initCopy; -} - -function safeFetch(url: Url, initCopy?: InitRequest) { - return fetch(url, initCopy).then((response: Response) => (response.ok ? response : handleError(response))); -} - -export function backendFetch(url: Url, init?: InitRequest, token?: Token): Promise { - return safeFetch(url, prepareRequest(init, token)); -} - -export function backendFetchText(url: Url, init?: InitRequest, token?: Token): Promise { - return backendFetch(url, init, token).then((safeResponse: Response) => safeResponse.text()); -} - -export function backendFetchJson(url: Url, init?: InitRequest, token?: Token): Promise { - return backendFetch(url, init, token).then((safeResponse: Response) => safeResponse.json()); -} - -export function fetchValidateUser(user: User): Promise { - const sub = user?.profile?.sub; - if (!sub) { - return Promise.reject(new Error(`Error : Fetching access for missing user.profile.sub : ${user}`)); - } - - console.info(`Fetching access for user...`); - const CheckAccessUrl = `${PREFIX_USER_ADMIN_SERVER_QUERIES}/v1/users/${sub}`; - console.debug(CheckAccessUrl); - - return backendFetch(CheckAccessUrl, { method: 'head' }, user?.id_token) - .then((response: Response) => { - //if the response is ok, the responseCode will be either 200 or 204 otherwise it's a Http error and it will be caught - return response.status === 200; - }) - .catch((error) => { - if (error.status === 403) { - return false; - } else { - throw error; - } - }); -} - -export type EnvJson = Env & typeof import('../../public/env.json'); - -function fetchEnv(): Promise { - return fetch('env.json').then((res: Response) => res.json()); -} - -export function fetchIdpSettings(): Promise { - return fetch('idpSettings.json').then((res) => res.json()); -} - -export type VersionJson = { - deployVersion?: string; -}; - -export function fetchVersion(): Promise { - console.info(`Fetching global metadata...`); - return fetchEnv() - .then((env: EnvJson) => fetch(`${env.appsMetadataServerUrl}/version.json`)) - .then((response: Response) => response.json()) - .catch((error) => { - console.error(`Error while fetching the version : ${getErrorMessage(error)}`); - throw error; - }); -} - -export type MetadataCommon = { - name: string; - url: Url; - appColor: string; - hiddenInAppsMenu: boolean; -}; - -export type MetadataStudy = MetadataCommon & { - readonly name: 'Study'; - resources?: { - types: string[]; - path: string; - }[]; - predefinedEquipmentProperties?: { - substation?: { - region?: string[]; - tso?: string[]; - totallyFree?: unknown[]; - Demo?: string[]; - }; - load?: { - codeOI?: string[]; - }; - }; - defaultParametersValues?: { - fluxConvention?: string; - enableDeveloperMode?: string; //maybe 'true'|'false' type? - mapManualRefresh?: string; //maybe 'true'|'false' type? - }; -}; - -// https://github.com/gridsuite/deployment/blob/main/docker-compose/docker-compose.base.yml -// https://github.com/gridsuite/deployment/blob/main/k8s/resources/common/config/apps-metadata.json -export type MetadataJson = MetadataCommon | MetadataStudy; - -export function fetchAppsAndUrls(): Promise { - console.info(`Fetching apps and urls...`); - return fetchEnv() - .then((env: EnvJson) => fetch(`${env.appsMetadataServerUrl}/apps-metadata.json`)) - .then((response: Response) => response.json()); -} - -// https://github.com/gridsuite/config-server/blob/main/src/main/java/org/gridsuite/config/server/dto/ParameterInfos.java -export type ConfigParameter = - | { - readonly name: typeof PARAM_LANGUAGE; - value: GsLangUser; - } - | { - readonly name: typeof PARAM_THEME; - value: GsTheme; - }; -export type ConfigParameters = ConfigParameter[]; - -export function fetchConfigParameters(appName: string = APP_NAME): Promise { - console.info(`Fetching UI configuration params for app : ${appName}`); - const fetchParams = `${PREFIX_CONFIG_QUERIES}/v1/applications/${appName}/parameters`; - return backendFetchJson(fetchParams) as Promise; -} - -export function fetchConfigParameter(name: string): Promise { - const appName = getAppName(name); - console.info(`Fetching UI config parameter '${name}' for app '${appName}'`); - const fetchParams = `${PREFIX_CONFIG_QUERIES}/v1/applications/${appName}/parameters/${name}`; - return backendFetchJson(fetchParams) as Promise; -} - -export function updateConfigParameter(name: string, value: Parameters[0]): Promise { - const appName = getAppName(name); - console.info(`Updating config parameter '${name}=${value}' for app '${appName}'`); - const updateParams = `${PREFIX_CONFIG_QUERIES}/v1/applications/${appName}/parameters/${name}?value=${encodeURIComponent( - value - )}`; - return backendFetch(updateParams, { method: 'put' }).then(); -}