diff --git a/.changeset/tiny-laws-give.md b/.changeset/tiny-laws-give.md new file mode 100644 index 000000000..2efbe06e7 --- /dev/null +++ b/.changeset/tiny-laws-give.md @@ -0,0 +1,7 @@ +--- +"@cloudoperators/juno-app-supernova": patch +"@cloudoperators/juno-app-example": patch +"@cloudoperators/juno-app-doop": patch +--- + +Delete juno-utils package diff --git a/.github/workflows/ci-title-lint-check.yaml b/.github/workflows/ci-title-lint-check.yaml index 30ecd12d1..7535087d9 100644 --- a/.github/workflows/ci-title-lint-check.yaml +++ b/.github/workflows/ci-title-lint-check.yaml @@ -57,7 +57,6 @@ jobs: supernova template ui - utils url-state-provider version ISSUE-\d+ diff --git a/apps/doop/package.json b/apps/doop/package.json index 9372fb7f3..009b8ae59 100644 --- a/apps/doop/package.json +++ b/apps/doop/package.json @@ -49,7 +49,6 @@ "@cloudoperators/juno-messages-provider": "*", "@cloudoperators/juno-ui-components": "*", "@cloudoperators/juno-url-state-provider-v1": "^1.3.2", - "@cloudoperators/juno-utils": "*", "react": "^18.2.0", "react-dom": "^18.2.0" } diff --git a/apps/doop/turbo.json b/apps/doop/turbo.json index 26f1bd3d4..097306e7c 100644 --- a/apps/doop/turbo.json +++ b/apps/doop/turbo.json @@ -5,14 +5,12 @@ "dependsOn": [ "@cloudoperators/juno-ui-components#build", "@cloudoperators/juno-messages-provider#build", - "@cloudoperators/juno-utils#build", "@cloudoperators/juno-communicator#build" ] }, "build": { "dependsOn": [ "@cloudoperators/juno-ui-components#build", - "@cloudoperators/juno-utils#build", "@cloudoperators/juno-messages-provider#build", "@cloudoperators/juno-communicator#build" ] @@ -20,7 +18,6 @@ "build:static": { "dependsOn": [ "@cloudoperators/juno-ui-components#build", - "@cloudoperators/juno-utils#build", "@cloudoperators/juno-messages-provider#build", "@cloudoperators/juno-communicator#build" ] diff --git a/apps/example/turbo.json b/apps/example/turbo.json index 67ec21988..e64cc0546 100644 --- a/apps/example/turbo.json +++ b/apps/example/turbo.json @@ -7,8 +7,7 @@ "@cloudoperators/juno-messages-provider#build", "@cloudoperators/juno-oauth#build", "@cloudoperators/juno-communicator#build", - "@cloudoperators/juno-url-state-provider#build", - "@cloudoperators/juno-utils#build" + "@cloudoperators/juno-url-state-provider#build" ] }, "build": { @@ -17,8 +16,7 @@ "@cloudoperators/juno-messages-provider#build", "@cloudoperators/juno-oauth#build", "@cloudoperators/juno-communicator#build", - "@cloudoperators/juno-url-state-provider#build", - "@cloudoperators/juno-utils#build" + "@cloudoperators/juno-url-state-provider#build" ] }, "build:static": { @@ -27,8 +25,7 @@ "@cloudoperators/juno-messages-provider#build", "@cloudoperators/juno-oauth#build", "@cloudoperators/juno-communicator#build", - "@cloudoperators/juno-url-state-provider#build", - "@cloudoperators/juno-utils#build" + "@cloudoperators/juno-url-state-provider#build" ] } } diff --git a/apps/supernova/package.json b/apps/supernova/package.json index 4fd1224cb..6613d9464 100644 --- a/apps/supernova/package.json +++ b/apps/supernova/package.json @@ -49,7 +49,6 @@ "@cloudoperators/juno-messages-provider": "*", "@cloudoperators/juno-ui-components": "*", "@cloudoperators/juno-url-state-provider-v1": "^1.3.2", - "@cloudoperators/juno-utils": "*", "react-error-boundary": "^4.0.13", "react": "^18.2.0", "react-dom": "^18.2.0" diff --git a/apps/supernova/turbo.json b/apps/supernova/turbo.json index 8e80f42f9..76c6eb459 100644 --- a/apps/supernova/turbo.json +++ b/apps/supernova/turbo.json @@ -5,14 +5,12 @@ "dependsOn": [ "@cloudoperators/juno-ui-components#build", "@cloudoperators/juno-messages-provider#build", - "@cloudoperators/juno-utils#build", "@cloudoperators/juno-communicator#build" ] }, "build": { "dependsOn": [ "@cloudoperators/juno-ui-components#build", - "@cloudoperators/juno-utils#build", "@cloudoperators/juno-messages-provider#build", "@cloudoperators/juno-communicator#build" ] @@ -20,7 +18,6 @@ "build:static": { "dependsOn": [ "@cloudoperators/juno-ui-components#build", - "@cloudoperators/juno-utils#build", "@cloudoperators/juno-messages-provider#build", "@cloudoperators/juno-communicator#build" ] diff --git a/commitlint.config.js b/commitlint.config.js index 0d8a24460..ad6682275 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -32,7 +32,6 @@ module.exports = { "supernova", "template", "ui", - "utils", "url-state-provider", "version", /^ISSUE-\d+$/, // Regex pattern for ISSUE- diff --git a/docs/husky_commitlint_guide.md b/docs/husky_commitlint_guide.md index 936c8d4a3..2283bf591 100644 --- a/docs/husky_commitlint_guide.md +++ b/docs/husky_commitlint_guide.md @@ -67,7 +67,6 @@ The following scopes are permitted for commits: - **supernova**: Changes in the supernova app - **template**: Changes to the template app - **ui**: changes to ui-components -- **utils**: Utility functions or modules - **url-state-provider**: Changes in the URL state provider - **version**: Versioning and release-related changes - **ISSUE-\**: Reference to a specific issue (e.g., `ISSUE-123`) diff --git a/package-lock.json b/package-lock.json index 918a64730..91dcc71f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -280,14 +280,13 @@ }, "apps/doop": { "name": "@cloudoperators/juno-app-doop", - "version": "2.3.1", + "version": "2.3.2", "license": "Apache-2.0", "dependencies": { "@cloudoperators/juno-communicator": "*", "@cloudoperators/juno-messages-provider": "*", "@cloudoperators/juno-ui-components": "*", "@cloudoperators/juno-url-state-provider-v1": "^1.3.2", - "@cloudoperators/juno-utils": "*", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -463,7 +462,7 @@ }, "apps/example": { "name": "@cloudoperators/juno-app-example", - "version": "1.0.11", + "version": "1.0.12", "license": "Apache-2.0", "dependencies": { "@cloudoperators/juno-communicator": "*", @@ -471,7 +470,7 @@ "@cloudoperators/juno-oauth": "*", "@cloudoperators/juno-ui-components": "*", "@cloudoperators/juno-url-state-provider": "*", - "@cloudoperators/juno-utils": "1.1.14", + "@cloudoperators/juno-utils": "1.1.15", "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -682,7 +681,7 @@ }, "apps/greenhouse": { "name": "@cloudoperators/juno-app-greenhouse", - "version": "0.3.6", + "version": "0.3.7", "license": "Apache-2.0", "dependencies": { "@cloudoperators/juno-app-doop": "*", @@ -1019,14 +1018,13 @@ }, "apps/supernova": { "name": "@cloudoperators/juno-app-supernova", - "version": "0.14.3", + "version": "0.14.4", "license": "Apache-2.0", "dependencies": { "@cloudoperators/juno-communicator": "*", "@cloudoperators/juno-messages-provider": "*", "@cloudoperators/juno-ui-components": "*", "@cloudoperators/juno-url-state-provider-v1": "^1.3.2", - "@cloudoperators/juno-utils": "*", "react": "^18.2.0", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.13" @@ -3968,8 +3966,17 @@ } }, "node_modules/@cloudoperators/juno-utils": { - "resolved": "packages/utils", - "link": true + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@cloudoperators/juno-utils/-/juno-utils-1.1.15.tgz", + "integrity": "sha512-Q3w9yKxtG2ZX3Pqthon8jA6ItUPNwsYryt4Q9cpcwcRcQBAr/dCgV7gE3IrJxc7EUcjSvluHfkpeQSZIXkcqQg==", + "deprecated": "This package is deprecated and will be removed from juno core. Use @cloudoperators/juno-mock-server and @cloudoperators/juno-ui-components instad.", + "engines": { + "node": ">=20.0.0 <21.0.0", + "npm": ">=10.0.0 <11.0.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } }, "node_modules/@comandeer/babel-plugin-banner": { "version": "5.0.0", @@ -28620,13 +28627,6 @@ "node": ">=0.10.0" } }, - "node_modules/whatwg-fetch": { - "version": "3.6.20", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", - "dev": true, - "license": "MIT" - }, "node_modules/whatwg-mimetype": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", @@ -29961,7 +29961,7 @@ }, "packages/ui-components": { "name": "@cloudoperators/juno-ui-components", - "version": "2.31.0", + "version": "2.32.0", "license": "Apache-2.0", "devDependencies": { "@babel/plugin-transform-parameters": "^7.22.15", @@ -30576,562 +30576,6 @@ "engines": { "node": ">=12" } - }, - "packages/utils": { - "name": "@cloudoperators/juno-utils", - "version": "1.1.14", - "license": "Apache-2.0", - "devDependencies": { - "@babel/preset-env": "^7.20.2", - "@babel/preset-react": "^7.18.6", - "@cloudoperators/juno-config": "*", - "@rollup/plugin-babel": "^6.0.4", - "@rollup/plugin-commonjs": "^26.0.1", - "@rollup/plugin-node-resolve": "^15.2.3", - "@testing-library/react": "^16.0.1", - "babel-jest": "^29.4.2", - "esbuild": "^0.17.6", - "jest": "^29.4.2", - "jest-environment-jsdom": "^29.7.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "rollup": "^4.0.0", - "rollup-plugin-analyzer": "^4.0.0", - "rollup-plugin-babel-minify": "^10.0.0", - "rollup-plugin-delete": "^2.0.0", - "rollup-plugin-includepaths": "^0.2.4", - "rollup-plugin-node-resolve": "^5.2.0", - "rollup-plugin-postcss": "^4.0.2", - "whatwg-fetch": "^3.6.19" - }, - "engines": { - "node": ">=20.0.0 <21.0.0", - "npm": ">=10.0.0 <11.0.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "packages/utils/node_modules/@esbuild/android-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", - "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/android-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", - "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/android-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", - "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/darwin-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", - "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/darwin-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", - "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/freebsd-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", - "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/freebsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", - "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/linux-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", - "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/linux-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", - "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/linux-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", - "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/linux-loong64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", - "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/linux-mips64el": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", - "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/linux-ppc64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", - "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/linux-riscv64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", - "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/linux-s390x": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", - "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/linux-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", - "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/netbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", - "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/openbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", - "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/sunos-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", - "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/win32-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", - "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/win32-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", - "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@esbuild/win32-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", - "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "packages/utils/node_modules/@rollup/plugin-commonjs": { - "version": "26.0.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-26.0.3.tgz", - "integrity": "sha512-2BJcolt43MY+y5Tz47djHkodCC3c1VKVrBDKpVqHKpQ9z9S158kCCqB8NF6/gzxLdNlYW9abB3Ibh+kOWLp8KQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "commondir": "^1.0.1", - "estree-walker": "^2.0.2", - "glob": "^10.4.1", - "is-reference": "1.2.1", - "magic-string": "^0.30.3" - }, - "engines": { - "node": ">=16.0.0 || 14 >= 14.17" - }, - "peerDependencies": { - "rollup": "^2.68.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "packages/utils/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "packages/utils/node_modules/esbuild": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", - "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.17.19", - "@esbuild/android-arm64": "0.17.19", - "@esbuild/android-x64": "0.17.19", - "@esbuild/darwin-arm64": "0.17.19", - "@esbuild/darwin-x64": "0.17.19", - "@esbuild/freebsd-arm64": "0.17.19", - "@esbuild/freebsd-x64": "0.17.19", - "@esbuild/linux-arm": "0.17.19", - "@esbuild/linux-arm64": "0.17.19", - "@esbuild/linux-ia32": "0.17.19", - "@esbuild/linux-loong64": "0.17.19", - "@esbuild/linux-mips64el": "0.17.19", - "@esbuild/linux-ppc64": "0.17.19", - "@esbuild/linux-riscv64": "0.17.19", - "@esbuild/linux-s390x": "0.17.19", - "@esbuild/linux-x64": "0.17.19", - "@esbuild/netbsd-x64": "0.17.19", - "@esbuild/openbsd-x64": "0.17.19", - "@esbuild/sunos-x64": "0.17.19", - "@esbuild/win32-arm64": "0.17.19", - "@esbuild/win32-ia32": "0.17.19", - "@esbuild/win32-x64": "0.17.19" - } - }, - "packages/utils/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/utils/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "packages/utils/node_modules/rollup": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", - "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.6" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.0", - "@rollup/rollup-android-arm64": "4.24.0", - "@rollup/rollup-darwin-arm64": "4.24.0", - "@rollup/rollup-darwin-x64": "4.24.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", - "@rollup/rollup-linux-arm-musleabihf": "4.24.0", - "@rollup/rollup-linux-arm64-gnu": "4.24.0", - "@rollup/rollup-linux-arm64-musl": "4.24.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", - "@rollup/rollup-linux-riscv64-gnu": "4.24.0", - "@rollup/rollup-linux-s390x-gnu": "4.24.0", - "@rollup/rollup-linux-x64-gnu": "4.24.0", - "@rollup/rollup-linux-x64-musl": "4.24.0", - "@rollup/rollup-win32-arm64-msvc": "4.24.0", - "@rollup/rollup-win32-ia32-msvc": "4.24.0", - "@rollup/rollup-win32-x64-msvc": "4.24.0", - "fsevents": "~2.3.2" - } } } } diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md deleted file mode 100644 index 80eafdcbd..000000000 --- a/packages/utils/CHANGELOG.md +++ /dev/null @@ -1,44 +0,0 @@ -# @cloudoperators/juno-utils - -## 1.1.15 - -### Patch Changes - -- 8615024: Replace useEndlessScrollList from utils to ui-componetns and utils deprecation - -## 1.1.14 - -### Patch Changes - -- 8c06648: Unify Typescript and Eslint config in one package - -## 1.1.13 - -### Patch Changes - -- 3ca3d35: Update instructions in readme files - -## 1.1.12 - -### Patch Changes - -- 5102bf8: Update instructions in readme files - -## 1.1.11 - -### Patch Changes - -- Fix files config in package.json - -## 1.1.10 - -### Patch Changes - -- Update Version - -## 1.1.9 - -### Patch Changes - -- 79aedc3: Update patch version of all packages -- Add files to be contained in the release to files in package.json diff --git a/packages/utils/LICENSE b/packages/utils/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/packages/utils/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/packages/utils/README.md b/packages/utils/README.md deleted file mode 100644 index daef00ea0..000000000 --- a/packages/utils/README.md +++ /dev/null @@ -1,445 +0,0 @@ -# Juno utils - -[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) - -**Juno utils** is a collection of utility functions, libraries, and hooks that are commonly used in building a Juno application. - -## Features - -- [**Infinite Scrolling (useEndlessScrollList)**](#usage-of-infinite-scrolling-useendlessscrolllist): react hook to facilitate the integration of an infinite scroll list. -- [**Mock Rest API (fetchProxyInitDB, fetchProxy)**](#usage-of-mock-rest-api-fetchproxyinitdb-fetchproxy): library designed to replicate the behavior of a Fetch REST API with mock data. -- [**Mount Juno Apps (useAppLoader)**](#mount-juno-apps-useapploader): react hook which enables mounting of Juno applications within other Juno applications. - -## Installation - -Add Juno utils to dependencies in package.json: - -```json - - "dependencies": { - "@cloudoperators/juno-utils": "*" - }, - -``` - -## Usage of Infinite scrolling (useEndlessScrollList) - -1. Import the react hook -2. Invoke the useEndlessScrollList hook by providing the complete set of items and desired options as a custom loading object and a function to be used to render the ref element. Please see below for more options. -3. Use the `scrollListItems` attribute to check if there are items to render. In case no items are available, display a corresponding message. -4. Use the iterator to iterate over the items to display. This will automatically handle the rendering of loading elements and reference objects whenever they are needed. - -```js -//ViolationDetailsList.jsx -(1) import {useEndlessScrollList} from "@cloudoperators/juno-utils" - -const ViolationDetailsList = ({items}) => { - - (2) const { scrollListItems, iterator } = useEndlessScrollList( - items, - { - loadingObject: ( - - - Loading ... - - - ), - refFunction: (ref) => ( - - - - - - ), - } - ) - - return ( - <> - (3) {scrollListItems?.length > 0 ? ( - - (4) {iterator.map((item, index) => ( - - [...] - - ))} - - ) : ( - - )} - - ) -} -``` - -Available options: - -| Option | description | -| ------------- | ------------------------------------------------------------------------------------------------------- | -| delay | the delay in ms between adding items to the list. Default is 500ms | -| showLoading | whether to show the loading indicator. Default is true and it renders a span with the text "Loading..." | -| loadingObject | the object to be rendered as the loading indicator. Default is a span with the text "Loading..." | -| showRef | whether to show the ref element | -| refFunction | the function to be used to render the ref element. It receives the ref as a parameter | - -Return object attributes - -| Option | description | -| --------------- | ------------------------------------------------------------------------------------------------------------------------ | -| scrollListItems | the items to be displayed | -| lastLisItemRef | the ref element to be used as the last item | -| isAddingItems | whether items are being added to the list | -| iterator | an iterator to be used to render the list. It has a map function that receives a function to be used to render each item | - -## Usage of Mock REST API (fetchProxyInitDB, fetchProxy) - -Utilize this library to develop against mock data and without requiring any code modifications when switching to a real REST API. When utilizing fetchProxy with the `mock` flag, it utilizes a provided mock data. If the mock flag is unset, the request is then forwarded to the actual Fetch REST API. - -### Get started - -1. Define the JSON data to use when mocking the REST API. - - ```json - { - "peaks": [{ "id": 1, "name": "Ama Dablam", "height": "6814m", "region": "Khumbu" }], - "regions": [{ "id": 1, "name": "Khumbu", "countries": "Nepal" }] - } - ``` - -2. Save the data into a file, such as `db.json`. While it's optional to include the JSON directly as a parameter when initializing `fetchProxy`, for the sake of code cleanliness, we recommend storing it in a separate file. - -3. Initialize the fetchProxy with the mock JSON data. - - ```js - // App.jsx - import React { useEffect} from "react" - import AppContent from "./components/AppContent" - import { fetchProxyInitDB } from "@cloudoperators/juno-utils" - import db from "../db.json" - - const App = (props = {}) => { - // setup the mock db.json - useEffect(() => { - if (props.mockAPI) { - fetchProxyInitDB(db) - } - }, [props.mockAPI]) - - return - } - ``` - -4. Use the `fetchProxy` within your components to retrieve the mock JSON. Add the `mock` option to determine whether the API should be mocked or not. - - ```js - // AppContent.jsx - import React, { useEffect, useState } from "react" - import { fetchProxy } from "@cloudoperators/juno-utils" - - const AppContent = () => { - const [data, setData] = useState(null) - - useEffect(() => { - fetchProxy(`${window.location.origin}/peaks`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - ...{ mock: true }, - }) - .then((response) => { - if (!response.ok) { - throw new Error("Network response was not ok") - } - return response.json() - }) - .then((result) => { - setData(result) - }) - }, []) - - return <>{data &&
{JSON.stringify(data, null, 2)}
} - } - ``` - -### Conditions and Limitations - -- fetchProxy - - 1. Provide a Browser-compatible URL ([WHATWG URL Standard](https://nodejs.org/api/url.html#the-whatwg-url-api)) as you would use with the fetch API as for example `http://localhost:3001/peaks`. - 2. Additional query parameters will be disregarded. Currently, there is no functionality to paginate or sort based on query parameters yet. - 3. No PATCH method defined yet. - -- Mock json data - - 1. Flat collection of key-value pairs - 2. Key defines the name of the object category - 3. Value muss be an array. - 4. Each element in the array muss have the attribute id. - -```react {linenos=inline,hl_lines=[3,6,"13-15"],linenostart=1} -// db.json -(1){ - (2)"peaks": - (3)[ - { - (4)"id": 1, - "name": "Ama Dablam", - "height": "6814m", - "region": "Khumbu" - } - ] -} -``` - -### Routes - -Based on the previous mock JSON data, here are all the default routes. When making POST, PUT, or DELETE requests, any changes will be automatically saved to the 'db' object and reset upon browser reload. - -```bash -GET /peaks -GET /peaks/1 -POST /peaks -PUT /peaks/1 -DELETE /peaks/1 -``` - -### Extended Options - -**Rewrite Routes** - -Utilize the rewriteRoutes option when you wish to align URLs with the structure of your mock database. For instance, if the API URL includes /api/v1/peaks, but the corresponding JSON structure is {"peaks":[]}, you can add a rewrite rule to exclude the /api/v1 portion. This ensures seamless mapping between your API endpoints and the mock database structure. The option rewriteRoutes accepts a collection of key value pairs with key as regex expresion and value as string to rewrite the url path. - -```js -const customRoutes = { - "/api/v1/(.*)": "/$1", // Replace '/api/v1' with an empty string - "^/api": "", // Replace '/api' with an empty string -} - -fetchProxyInitDB(db, { rewriteRoutes: customRoutes }) -``` - -Now you can access resources using following routes: - -```bash -/api/v1/peaks # → /peaks -/api/peaks # → /peaks -``` - -**Rewrite Responses** - -Employ the rewriteResponses option when you intend to customize the response from the mock API. This flexibility is available on a per-API method and path basis. For instance, suppose you require a distinct response when making a POST request to /api/v1/peaks, deviating from the standard response that includes the posted object. In such cases, you can introduce a rewriteResponses rule, exemplified as follows: {"POST": {"/api/v1/peaks": {"test": "custom response"}}}. It's essential to note that rewriteResponses takes precedence over rewriteRoutes. Therefore, if you've altered the original path in rewriteRoutes, ensure it matches the original path for accurate execution. - -```js -const customResponses = { - POST: { - "^/peaks": { certificate: "testCertificate" }, - }, -} - -fetchProxyInitDB(db, { rewriteResponses: customResponses }) -``` - -### Self Contained Running Example - -Simply copy the following example and run it to explore how to use this library. - -```js -import React, { useEffect, useState } from "react" -import { fetchProxy, fetchProxyInitDB } from "@cloudoperators/juno-utils" - -const App = () => { - const [data, setData] = useState(null) - - // setup the mock db.json - useEffect(() => { - fetchProxyInitDB({ - peaks: [{ id: 1, name: "Ama Dablam", height: "6814m", region: "Khumbu" }], - }) - }, []) - - useEffect(() => { - fetchProxy(`${window.location.origin}/peaks`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - ...{ mock: true }, - }) - .then((response) => { - if (!response.ok) { - throw new Error("Network response was not ok") - } - return response.json() - }) - .then((result) => { - setData(result) - }) - }, []) - - return <>{data &&
{JSON.stringify(data, null, 2)}
} -} - -export default App -``` - -## Mount Juno Apps (useAppLoader) - -This react hook is designed for scenarios where you wish to embed a Juno application within another application, particularly useful when running multiple applications within a single environment. - -### How it Works - -This hook uses our widget loader app, creating a runtime environment (shell) that ensures all dependencies required by the widget are available. To facilitate shared dependencies among multiple applications and optimize resource loading, the process unfolds in the following steps: - -**Loading ES Module Shim:** -Initially, the hook loads the ES Module Shim, which supports import maps. This provides a foundation for efficient dependency management. - -**Import Maps:** -In the second step, an importMap is loaded. This map serves as a blueprint, guiding the browser on the origin of package imports. This step ensures that all necessary dependencies are accessible at runtime. - -**Loading the target Application:** -Finally, the target application is loaded into the environment. Thanks to the importMap, the browser intelligently retrieves packages, and shared dependencies are loaded only ONCE and not per application. The browser cache further optimizes performance by ensuring packages are not fetched with every page load. - -### Prerequisites - -Ensure the following prerequisites are met before using this hook: - -**URL to Our Assets Host:** -Provide the URL to our assets host, allowing the hook to fetch our widget loader. - -**Compiled as ES Module:** -The application must be compiled as an ES module to accommodate the dependency on ES Module Shim. - -**Name and Version or URL:** -If the application is hosted in our assets sever you can choose between: - -1. Provide the name and version (default version is "latest") of the application. The hook will then fetch the application from our assets server. -2. Provide the complete URL path to the application. The hook will then fetch the application from the provided URL. - -If the application is hosted in a different server you can choose between: - -1. Provide the complete URL path to the application, remember that the application must be compiled as an ES module. The hook will then fetch the application from the provided URL. - -### Get started - -1. Import the react hook useAppLoader. - - ```js - import { useAppLoader } from "@cloudoperators/juno-utils" - ``` - -2. Invoke the use hook useAppLoader by providing the assets URL. - - ```js - const { mount } = useAppLoader("https://assets.juno.qa-de-1.cloud.sap/") - ``` - -3. Create a ref using the useRef hook. - - ```js - const app = useRef(null) - ``` - -4. Use the mount function to mount the application. The mount function accepts the following options: - - - container: the ref to the container element which will host the application - - options object with the following attributes: - - name: the name of the application - - version: the version of the application (default is latest) - - props: the props to be passed to the application - - Example using name and version and passing embedded as a prop to the target application: - - ```js - useEffect(() => { - if (!mount) return - mount(app.current, { - name: "exampleapp", - version: "latest", - props: { embedded: true }, - }) - }, [mount]) - ``` - - Example using URL and passing embedded as a prop to the target application: - - ```js - useEffect(() => { - if (!mount) return - mount(app.current, { - url: "https://assets.juno.global.cloud.sap/apps/exampleapp@latest/build/index.js", - props: { embedded: true }, - }) - }, [mount]) - ``` - -5. Use the ref to render the application. - - ```js -
- ``` - -### Self Contained Running Example - -Simply copy the following example and run it to explore how to use this library. - -```js -import React, { useEffect, useRef } from "react" -import { useAppLoader } from "@cloudoperators/juno-utils" - -const App = () => { - const { mount } = useAppLoader("https://assets.juno.qa-de-1.cloud.sap/") - const app = useRef() - - useEffect(() => { - if (!mount || !app.current) return - mount(app.current, { name: "exampleapp" }) - }, [mount, app]) - - return ( - <> -
This is the root app responsible for loading the other apps.
-
- - ) -} - -export default App -``` - -## Testing - -To run tests, use the following command: - -```bash -npm run test -``` - -If you're working within the Juno monorepo using workspaces, you can use: - -````bash -npm -w @cloudoperators/juno-utils run test -```@cloudoperators/ - -## Build - -To build your project, run: - -```bash -npm run build -```` - -For Juno monorepo users within workspaces, you can use: - -```bash -npm -w @cloudoperators/juno-utils run build -``` diff --git a/packages/utils/__mocks__/intersectionObserverMock.js b/packages/utils/__mocks__/intersectionObserverMock.js deleted file mode 100644 index 0ebcf1703..000000000 --- a/packages/utils/__mocks__/intersectionObserverMock.js +++ /dev/null @@ -1,10 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -const intersectionObserverMock = () => ({ - observe: () => null, - disconnect: () => null, -}) -window.IntersectionObserver = jest.fn().mockImplementation(intersectionObserverMock) diff --git a/packages/utils/babel.config.json b/packages/utils/babel.config.json deleted file mode 100644 index 96500acb4..000000000 --- a/packages/utils/babel.config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "presets": ["@babel/preset-react"], - "env": { - "test": { - "presets": ["@babel/env"] - } - } -} diff --git a/packages/utils/eslint.config.mjs b/packages/utils/eslint.config.mjs deleted file mode 100644 index 91e92de58..000000000 --- a/packages/utils/eslint.config.mjs +++ /dev/null @@ -1,22 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import junoConfigs from "@cloudoperators/juno-config/eslint/juno.mjs" - -export default [ - ...junoConfigs, - { - files: ["**/*.test.js"], - languageOptions: { sourceType: "module" }, - }, - { - files: ["**/*.{js,mjs,cjs,jsx}"], - languageOptions: { - globals: { - importShim: "readonly", - }, - }, - }, -] diff --git a/packages/utils/package.json b/packages/utils/package.json deleted file mode 100644 index ef80d9ac2..000000000 --- a/packages/utils/package.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "name": "@cloudoperators/juno-utils", - "version": "1.1.15", - "description": "Description of utils", - "deprecated": "This package is deprecated and will be removed from juno core. Use @cloudoperators/juno-mock-server and @cloudoperators/juno-ui-components instad.", - "author": "UI-Team", - "contributors": [ - "Andreas Pfau", - "Arturo Reuschenbach Pucernau" - ], - "repository": "https://github.com/cloudoperators/juno/tree/main/packages/utils", - "source": "src/index.js", - "main": "build/index.js", - "module": "build/index.js", - "files": [ - "build" - ], - "license": "Apache-2.0", - "engines": { - "node": ">=20.0.0 <21.0.0", - "npm": ">=10.0.0 <11.0.0" - }, - "devDependencies": { - "@babel/preset-env": "^7.20.2", - "@babel/preset-react": "^7.18.6", - "@cloudoperators/juno-config": "*", - "@rollup/plugin-babel": "^6.0.4", - "@rollup/plugin-commonjs": "^26.0.1", - "@rollup/plugin-node-resolve": "^15.2.3", - "@testing-library/react": "^16.0.1", - "babel-jest": "^29.4.2", - "esbuild": "^0.17.6", - "jest": "^29.4.2", - "jest-environment-jsdom": "^29.7.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "rollup": "^4.0.0", - "rollup-plugin-analyzer": "^4.0.0", - "rollup-plugin-babel-minify": "^10.0.0", - "rollup-plugin-delete": "^2.0.0", - "rollup-plugin-includepaths": "^0.2.4", - "rollup-plugin-node-resolve": "^5.2.0", - "rollup-plugin-postcss": "^4.0.2", - "whatwg-fetch": "^3.6.19" - }, - "peerDependencies": { - "react": "^18.2.0" - }, - "scripts": { - "build": "NODE_ENV=production rollup -c", - "lint": "eslint", - "test": "NODE_ENV=test jest", - "dev": "NODE_ENV=development rollup -c -w", - "clean": "rm -rf build && rm -rf node_modules && rm -rf .turbo", - "clean:cache": "rm -rf .turbo" - }, - "babel": { - "presets": [ - "@babel/preset-env" - ] - }, - "jest": { - "testEnvironment": "jsdom", - "verbose": true, - "transform": { - "\\.js$": "babel-jest" - }, - "watchPathIgnorePatterns": [ - "/dev/", - "/build/" - ], - "testPathIgnorePatterns": [ - "/__fixtures__/", - "/__utils__/" - ] - } -} diff --git a/packages/utils/rollup.config.js b/packages/utils/rollup.config.js deleted file mode 100644 index c1d57e785..000000000 --- a/packages/utils/rollup.config.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -const babel = require("@rollup/plugin-babel") -const del = require("rollup-plugin-delete") -const pkg = require("./package.json") -const minify = require("rollup-plugin-babel-minify") -const analyze = require("rollup-plugin-analyzer") -const commonjs = require("@rollup/plugin-commonjs") -const { nodeResolve } = require("@rollup/plugin-node-resolve") - -if (!/.+\/.+\.js/.test(pkg.module)) throw new Error("module value is incorrect, use DIR/FILE.js like build/index.js") -const buildDir = pkg.module.slice(0, pkg.module.lastIndexOf("/")) - -const config = [ - { - input: pkg.source, - output: [ - { - file: pkg.module, - format: "esm", - compact: true, - }, - ], - plugins: [ - nodeResolve(), - - babel({ - exclude: "node_modules/**", - babelHelpers: "bundled", - }), - del({ targets: [`${buildDir}/**/*`] }), - minify({ comments: false }), - analyze({ summaryOnly: true, limit: 0 }), - commonjs(), - ], - external: Object.keys(pkg.peerDependencies || {}), - }, -] - -module.exports = config diff --git a/packages/utils/src/hooks/useAppLoader.js b/packages/utils/src/hooks/useAppLoader.js deleted file mode 100644 index 20e0410d6..000000000 --- a/packages/utils/src/hooks/useAppLoader.js +++ /dev/null @@ -1,83 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { useCallback } from "react" - -// load widget-loader with importmap-only attribute to -// ensure the importmap is loaded -const importmaps = {} -const loadWidgetLoaderWithImportmap = (assetsHost) => { - if (!assetsHost) { - console.warn("utils#useAppLoader: assetsHost is undefined. Please prove a valid assetsHost") - return Promise.resolve(false) - } - - importmaps[assetsHost] = - importmaps[assetsHost] || - // this promise is resolved once! - new Promise((resolve) => { - // create a script element into the head to load the importMaps - const url = new URL("/apps/widget-loader@latest/build/app.js", assetsHost) - const script = document.createElement("script") - script.src = url.href - script.setAttribute("data-importmap-only", "true") - document.head.append(script) - window.addEventListener("JUNO_IMPORTMAP_LOADED", () => { - resolve(true) - }) - }) - - return importmaps[assetsHost] -} - -/* - @assetsHost: url of the assetsHost to be able to load the widget-loader with importMap - ++mount++ - @param container: complete list of items to be displayed - @param options: options to load the application - @param options.name: the name of the application hosted in the assets server - @param options.version: the version of the application hosted in the assets server or 'latest' - @param options.url: url of the application when the application is not hosted in the assets server - @param options.props: app props needed to run the application -*/ -const useAppLoader = (assetsHost) => { - const mount = useCallback( - (container, options) => { - if (!assetsHost) { - console.warn(`useAppLoader:: assetsHost is not set`) - return null - } - - // check whether the user gave an url or the name of the application to fetch - if (!options.name && !options.url) { - console.warn( - `useAppLoader:: options.name (name der App from assets server) or options.url (url where the app is hosted) is required. Got`, - options - ) - return null - } - // fetch first the widget-loader (with importMap) before we mount the application - return loadWidgetLoaderWithImportmap(assetsHost).then(() => { - console.log("useAppLoader: mount", options, assetsHost) - const name = options?.name?.startsWith("@") ? options.name : `@juno/${options.name}` - - let url = options.url ? options.url : `${name}@${options?.version || "latest"}` - return importShim(url).then(async (app) => { - try { - await app.mount(container, { props: { ...options.props } }) - } catch (error) { - throw new Error(`useAppLoader:: mount:: ${error}`) - } - - return app.unmount - }) - }) - }, - [assetsHost] - ) - return { mount } -} - -export default useAppLoader diff --git a/packages/utils/src/hooks/useEndlessScrollList.js b/packages/utils/src/hooks/useEndlessScrollList.js deleted file mode 100644 index 27f583abb..000000000 --- a/packages/utils/src/hooks/useEndlessScrollList.js +++ /dev/null @@ -1,93 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React, { useRef, useEffect, useState, useCallback, useMemo } from "react" - -/* - This hook is used to create an endless scroll list. - @param items: complete list of items to be displayed - @param options: options for the hook - @param options.delay: the delay in ms between adding items to the list. Default is 500ms - @param options.showLoading: whether to show the loading indicator. Default is true and it renders a span with the text "Loading..." - @param options.loadingObject: the object to be rendered as the loading indicator. Default is a span with the text "Loading..." - @param options.showRef: whether to show the ref element - @param options.refFunction: the function to be used to render the ref element. It receives the ref as a parameter - @return: an object with the following properties: - @property scrollListItems: the items to be displayed - @property iterator: an iterator to be used to render the list. It has a map function that receives a function to be used to render each item - */ -const useEndlessScrollList = (items, options = {}) => { - const [visibleAmount, setVisibleAmount] = useState(20) - const [isAddingItems, setIsAddingItems] = useState(false) - const timeoutRef = useRef(null) - const observer = useRef() - - useEffect(() => { - // clear when component is unmounted - return () => clearTimeout(timeoutRef.current) - }, []) - - // recalculate if items change - const scrollListItems = useMemo(() => { - if (items) { - return items.slice(0, visibleAmount) - } - }, [items, visibleAmount]) - - // recalculate if items change - const lastLisItemRef = useCallback( - (node) => { - // skip if already adding items - if (isAddingItems) return - // disconnect previous observer - if (observer.current) observer.current.disconnect() - // create new observer - observer.current = new IntersectionObserver((entries) => { - // if the last element is intersecting and there are still items to show - if (entries[0].isIntersecting && visibleAmount <= items.length) { - clearTimeout(timeoutRef.current) - setIsAddingItems(true) - timeoutRef.current = setTimeout(() => { - setIsAddingItems(false) - setVisibleAmount((prev) => prev + 10) - }, options?.delay || 500) - } - }) - if (node) observer.current.observe(node) - }, - [items, isAddingItems] - ) - - const iterator = useMemo(() => { - return { - map: (f) => { - const content = scrollListItems.map(f) - return ( - <> - {content} - {isAddingItems && options?.showLoading !== false && ( - <> - {options?.loadingObject ? options.loadingObject : Loading...} - - )} - {options?.showRef !== false && ( - <> - {options?.refFunction ? ( - options.refFunction(lastLisItemRef) - ) : ( - - )} - - )} - - ) - }, - } - }, [scrollListItems, lastLisItemRef]) - - return { scrollListItems, iterator } -} - -export default useEndlessScrollList diff --git a/packages/utils/src/hooks/useEndlessScrollList.test.js b/packages/utils/src/hooks/useEndlessScrollList.test.js deleted file mode 100644 index aa667217c..000000000 --- a/packages/utils/src/hooks/useEndlessScrollList.test.js +++ /dev/null @@ -1,74 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { render, queryByAttribute, renderHook } from "@testing-library/react" -import { useEndlessScrollList } from "../index" -// eslint-disable-next-line jest/no-mocks-import -import "../../__mocks__/intersectionObserverMock" - -// TODO: add tests for the loading indicator which currently is not implemented do tue intersection observer can't be reproduced in tests - -describe("useEndlessScrollList", () => { - it("return no scroll items if items not all provided", () => { - const { result } = renderHook(() => useEndlessScrollList([])) - expect(result.current.scrollListItems.length).toBe(0) - }) - - describe("scrollListItems", () => { - it("return all items if the whole amount is less then 20", () => { - const { result } = renderHook(() => useEndlessScrollList(["1", "2", "3"])) - expect(result.current.scrollListItems.length).toBe(3) - }) - it("return 20 items if the whole amount is more then 20", () => { - const newArray = Array(25) - .fill() - .map((_, index) => `${index + 1}`) - const { result } = renderHook(() => useEndlessScrollList(newArray)) - expect(result.current.scrollListItems.length).toBe(20) - }) - }) - - describe("iterator", () => { - it("returns a map function which iterates over all scrollListItems and adds the intersection ref if nothing else specified in options", () => { - const { result } = renderHook(() => useEndlessScrollList(["1", "2", "3"])) - const mapFunction = result.current.iterator.map((item) => item) - const getById = queryByAttribute.bind(null, "id") - const dom = render(mapFunction) - const intersectionRefElement = getById(dom.container, "endlessScrollListLastItemRef") - expect(intersectionRefElement).toBeTruthy() - }) - - it("returns a map function which iterates over all scrollListItems and do not include the intersection ref or do not call refFunction if showRef is set to false in options", () => { - const refFunction = jest.fn() - const { result } = renderHook(() => - useEndlessScrollList(["1", "2", "3"], { - showRef: false, - refFunction: refFunction, - }) - ) - const mapFunction = result.current.iterator.map((item) => item) - const getById = queryByAttribute.bind(null, "id") - const dom = render(mapFunction) - const intersectionRefElement = getById(dom.container, "endlessScrollListLastItemRef") - expect(intersectionRefElement).toBeFalsy() - expect(refFunction).not.toHaveBeenCalled() - }) - - it("returns a map function which iterates over all scrollListItems and does not add an intersection ref element", () => { - const refFunction = jest.fn() - const { result } = renderHook(() => - useEndlessScrollList(["1", "2", "3"], { - refFunction: refFunction, - }) - ) - const mapFunction = result.current.iterator.map((item) => item) - const getById = queryByAttribute.bind(null, "id") - const dom = render(mapFunction) - const intersectionRefElement = getById(dom.container, "endlessScrollListLastItemRef") - expect(intersectionRefElement).toBeFalsy() - expect(refFunction).toHaveBeenCalled() - }) - }) -}) diff --git a/packages/utils/src/index.js b/packages/utils/src/index.js deleted file mode 100644 index 23613df46..000000000 --- a/packages/utils/src/index.js +++ /dev/null @@ -1,10 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import useEndlessScrollList from "./hooks/useEndlessScrollList" -import fetchProxy, { fetchProxyInitDB } from "./lib/fetchProxy" -import useAppLoader from "./hooks/useAppLoader" - -export { useEndlessScrollList, fetchProxy, fetchProxyInitDB, useAppLoader } diff --git a/packages/utils/src/lib/fetchProxy.js b/packages/utils/src/lib/fetchProxy.js deleted file mode 100644 index bb43723b3..000000000 --- a/packages/utils/src/lib/fetchProxy.js +++ /dev/null @@ -1,318 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -const resolveResponse = (json) => { - return new Response(JSON.stringify(json), { - status: 200, - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - }) -} - -const rejectResponse = (error) => { - return new Response(error, { - status: 404, - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - }) -} - -let localDB = null -let rewriteRoutes = null -let rewriteResponses = null -let debug = false - -// fetchProxyInitDB is used to initialize the localDB -// @jsonData: json object with the data to be used as localDB -// @options: object with the options -// rewriteRoutes: object with the rewrite rules for the routes. Example: -// { -// "^/api/v1/peaks": "/peaks", -// "^/api/v1/peaks/([0-9]+)": "/peaks/$1", -// } -// rewriteResponses: object with the rewrite rules for the responses. Example: -// { -// POST: { -// "^/peaks": { certificate: "testCertificate" }, -// }, -// } -export const fetchProxyInitDB = (jsonData, options = {}) => { - // set the debug mode - if (options?.debug) { - debug = true - } - - // set localDB to null to reset it - if (jsonData === null) { - if (debug) console.log(`fetchProxyInitDB:: Reset localDB`) - localDB = null - return - } - - // check if the localDB is initialized and warns if already initialized - if (localDB) { - // create a new custom warning to return - console.warn( - `fetchProxyInitDB:: localDB already initialized. This typically occurs when the component or hook, responsible for local database initialization, is accidentally re-rendered. If you intend to reset the local database, please ensure to set localDB to null first by invoking fetchProxyInitDB(null) before providing new data."` - ) - } - - // check if the given json is valid and also checks if the jsondata are a collection of key value pairs with values as arrays - if (typeof jsonData !== "object") { - // create a new custom error to return - throw new Error(`It seems that jsonData is not a valid JSON object.`) - } - - // check if there are custom routes in the options - if (options?.rewriteRoutes) { - if (debug) console.log(`fetchProxyInitDB:: rewriteRoutes::`, options?.rewriteRoutes) - - // Filter out non-regex rules - const regexRules = Object.fromEntries( - Object.entries(options?.rewriteRoutes).filter(([key]) => { - // check if key is a regex expresion - try { - new RegExp(key) - return true - } catch (_error) { - // warn if expresion is not regex - console.warn(`It seems that the given rewrite rule ${key} for routes is not a valid regex expresion.`) - return false - } - }) - ) - // save them globally - rewriteRoutes = regexRules - } - - if (options?.rewriteResponses) { - const allowedMethods = ["GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS"] //'PATCH' - const regexResponses = {} - - if (debug) console.log(`fetchProxyInitDB:: rewriteResponses::`, options?.rewriteResponses) - - Object.keys(options?.rewriteResponses).forEach((key) => { - // check if method is allowed - if (!allowedMethods.includes(key)) { - // warn if method is not allowed - console.warn(`It seems that the given rewrite rule ${key} for responses is not a valid method.`) - return - } - - // check for each method defined if the key is a regex expresion - const methodRegex = options?.rewriteResponses[key] - // Filter out non-regex rules - const methodRegexResponses = Object.fromEntries( - Object.entries(methodRegex).filter(([key]) => { - // check if key is a regex expresion - try { - new RegExp(key) - return true - } catch (_error) { - // warn if expresion is not regex - console.warn(`It seems that the given rewrite rule ${key} for responses is not a valid regex expresion.`) - return false - } - }) - ) - regexResponses[key] = methodRegexResponses - }) - - // set the responseRewriteRules - rewriteResponses = regexResponses - } - - // check if the given json is valid and also checks if the jsondata are a collection of key value pairs with values as arrays - if ( - Object.keys(jsonData).some((key) => { - return !Array.isArray(jsonData[key]) - }) - ) { - // create a new custom error to return - throw new Error(`It seems that jsonData is not a collection of key value pairs with values as arrays.`) - } - - if (debug) console.log(`fetchProxyInitDB:: jsonData::`, jsonData) - - // set the localDB - localDB = jsonData -} - -// use a custom option to switch between real fetch and mock fetch since process.env.NODE_ENV -// is set to production when building for browser platform: https://esbuild.github.io/api/#platform -const fetchProxy = (urlString, options) => { - // split custom options from fetch options - const { mock, ...fetchOptions } = options - - // if not set explicitly to true or "true", use the real fetch - if (mock !== true && mock !== "true") { - console.log(`fetchProxy:: real fetch for::`, urlString) - return fetch(urlString, fetchOptions) - } - - // warn localDB not initialized - if (!localDB) { - // create a new custom error to return - throw new Error(`localDB not initialized. - Please use fetchProxyInitDB(jsonData) to initialize the localDB.`) - } - - let url = null - try { - // get the path from the url - url = new URL(urlString) - } catch (_error) { - throw new Error(`Invalid URL: ${urlString}`) - } - - // if method is not set, use GET as default - let method = options?.method - if (!method) method = "GET" - let path = url.pathname - - // check if there are custom responses for the given path and method and save it for later - let customResponse = null - if (rewriteResponses?.[method]) { - const customResponsePerMethod = rewriteResponses[method] - for (const regexPattern in customResponsePerMethod) { - const regex = new RegExp(regexPattern) - if (regex.test(path)) { - customResponse = resolveResponse(customResponsePerMethod[regexPattern]) - break - } - } - } - - // check if there are custom routes - if (rewriteRoutes) { - for (const regexPattern in rewriteRoutes) { - const regex = new RegExp(regexPattern) - if (regex.test(path)) { - path = path.replace(regex, rewriteRoutes[regexPattern]) - break - } - } - } - - // get the object from the path - const object = path.split("/")[1] - // get the id from the path - const id = path.split("/")[2] - - if (debug) { - console.log( - `fetchProxy:: mock fetch with method: `, - method, - ", path: ", - path, - ", object: ", - object, - ", id: ", - id, - ", customResponse: ", - customResponse - ) - } - - const body = options?.body - // switch over the header method - switch (method) { - case "GET": - return new Promise((resolve) => { - if (object) { - // object is given - if (localDB?.[object]) { - // object is found - if (id) { - // find the object with the id - const index = localDB?.[object].findIndex((item) => { - // compare with just == because id is a string - // https://www.w3schools.com/js/js_comparisons.asp - return item.id == id - }) - // id is given - if (index >= 0) { - // id is found - return resolve(customResponse || resolveResponse(localDB?.[object]?.[index])) - } else { - return resolve(rejectResponse(`No id ${id} for object ${object} found`)) - } - } - return resolve(customResponse || resolveResponse(localDB?.[object])) - } else { - return resolve(rejectResponse(`No object ${object} found`)) - } - } - resolve(resolveResponse(customResponse || localDB)) - }) - case "POST": - return new Promise((resolve) => { - if (!object || !body) resolve(rejectResponse(`No object '${object}' or body '${body}' given`)) - if (!localDB?.[object]) resolve(rejectResponse(`No object '${object}' found`)) - - let newBody = JSON.parse(body) - // set default id - newBody.id = 1 - // if there are items find the item with the highest id - if (localDB?.[object]?.length > 0) { - // find the object with the highest id - const maxObject = localDB?.[object].reduce((max, obj) => (obj.id > max.id ? obj : max)) - // set the id to the highest id + 1 - newBody.id = (maxObject?.id || 0) + 1 - } - localDB?.[object].push(newBody) - resolve(customResponse || resolveResponse(newBody)) - }) - case "PUT": - return new Promise((resolve) => { - if (!object || !id) resolve(rejectResponse(`No object '${object}' or id '${id}' given`)) - if (!localDB?.[object]) resolve(rejectResponse(`No object '${object}' found`)) - - // find the object with the id - const index = localDB?.[object].findIndex((item) => { - // compare with just == because id is a string - // https://www.w3schools.com/js/js_comparisons.asp - return item.id == id - }) - // update object with the id - if (index >= 0) { - // merge existing object with new body without changing the id - localDB[object][index] = { - ...localDB[object][index], - ...JSON.parse(body), - id: localDB[object][index].id, - } - resolve(customResponse || resolveResponse(localDB[object][index])) - } else { - return resolve(rejectResponse(`No item with id '${id}' found`)) - } - }) - case "DELETE": - return new Promise((resolve) => { - if (!object || !id) resolve(rejectResponse(`No object '${object}' or id '${id}' given`)) - if (!localDB?.[object]) resolve(rejectResponse(`No object '${object}' found`)) - - // find the object with the id - const index = localDB?.[object].findIndex((item) => { - // compare with just == because id is a string - // https://www.w3schools.com/js/js_comparisons.asp - return item.id == id - }) - // delete object with the id - if (index >= 0) { - localDB[object].splice(index, 1) - resolve(customResponse || resolveResponse("Object deleted")) - } else { - return resolve(rejectResponse(`No item with id '${id}' found`)) - } - }) - } -} - -export default fetchProxy diff --git a/packages/utils/src/lib/fetchProxy.test.js b/packages/utils/src/lib/fetchProxy.test.js deleted file mode 100644 index ffd52d23b..000000000 --- a/packages/utils/src/lib/fetchProxy.test.js +++ /dev/null @@ -1,258 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import "whatwg-fetch" -import fetchProxy, { fetchProxyInitDB } from "./fetchProxy" - -const originalConsoleError = global.console.warn - -// catch consol WARNS: https://www.jackfranklin.co.uk/blog/failing-tests-on-react-proptypes/ -beforeEach(() => { - global.console.warn = (...args) => { - const fetchProxyWarns = [/localDB already initialized/] - - if (fetchProxyWarns.some((p) => p.test(args[0]))) { - throw new Error(args[0]) - } - - originalConsoleError(...args) - } -}) - -describe("fetchProxy lib", () => { - describe("fetchProxyInitDB", () => { - it("through an error if given data is not valid JSON", () => { - // setup wrang json data - const db = 1234 - expect(() => fetchProxyInitDB(db)).toThrow(/not a valid JSON object/) - }) - it("through an error if given data is not a collection of key value pairs with values as arrays", () => { - // setup wrang json data - const db = { - peaks: { - id: 1, - name: "Mont Blanc", - height: 4808, - country: "France", - range: "Alps", - }, - } - expect(() => fetchProxyInitDB(db)).toThrow(/not a collection of key value pairs with values as arrays/) - }) - it("through an warn if localDB already initialized", () => { - // setup mock db.json - const db = { - peaks: [ - { - id: 1, - name: "Mont Blanc", - height: 4808, - country: "France", - range: "Alps", - }, - ], - } - fetchProxyInitDB(db) - expect(() => fetchProxyInitDB(db)).toThrow(/already initialized/) - }) - }) - - describe("fetch", () => { - beforeEach(() => { - // reset localDB - fetchProxyInitDB(null) - - // setup mock db.json - const db = { - peaks: [ - { - id: 1, - name: "Mont Blanc", - height: 4808, - country: "France", - range: "Alps", - }, - ], - } - fetchProxyInitDB(db) - }) - - it("through an error if no mock data setup before fetching", () => { - // set localDB to null to simulate not initialized - fetchProxyInitDB(null) - - expect(() => fetchProxy("http://localhost:3000/peaks", { mock: true })).toThrow(/localDB not initialized/) - }) - - it("through an error if given url is not conform", () => { - expect(() => fetchProxy("/peaks", { mock: true })).toThrow() - }) - - test("GET all peaks", async () => { - const data = await fetchProxy("http://localhost:3000/peaks", { - mock: true, - }) - expect(await data.json()).toStrictEqual([ - { - id: 1, - name: "Mont Blanc", - height: 4808, - country: "France", - range: "Alps", - }, - ]) - }) - - test("GET one peak", async () => { - const data = await fetchProxy("http://localhost:3000/peaks/1", { - mock: true, - }) - expect(await data.json()).toStrictEqual({ - id: 1, - name: "Mont Blanc", - height: 4808, - country: "France", - range: "Alps", - }) - }) - - test("POST", async () => { - const data = await fetchProxy("http://localhost:3000/peaks", { - method: "POST", - body: JSON.stringify({ - name: "test", - }), - mock: true, - }) - expect(await data.json()).toStrictEqual({ - id: 2, - name: "test", - }) - }) - - test("PUT", async () => { - const data = await fetchProxy("http://localhost:3000/peaks/1", { - method: "PUT", - body: JSON.stringify({ - id: 1, - name: "test", - }), - mock: true, - }) - expect(await data.json()).toStrictEqual({ - id: 1, - name: "test", - height: 4808, - country: "France", - range: "Alps", - }) - }) - - test("DELETE", async () => { - const data = await fetchProxy("http://localhost:3000/peaks/1", { - method: "DELETE", - mock: true, - }) - expect(await data.json()).toStrictEqual("Object deleted") - }) - }) - - describe("fetch with rewriteRoutes", () => { - beforeEach(() => { - // reset localDB - fetchProxyInitDB(null) - - // setup mock db.json - const db = { - peaks: [ - { - id: 1, - name: "Mont Blanc", - height: 4808, - country: "France", - range: "Alps", - }, - ], - } - - const rewriteRoutes = { - "/api/v1/(.*)": "/$1", // Replace '/api/v1' with an empty string - "^/api": "", // Replace '/api' with an empty string - } - - fetchProxyInitDB(db, { rewriteRoutes }) - }) - - test("ignore prefix api/v1", async () => { - const data = await fetchProxy("http://localhost:3000/api/v1/peaks", { - mock: true, - }) - expect(await data.json()).toStrictEqual([ - { - id: 1, - name: "Mont Blanc", - height: 4808, - country: "France", - range: "Alps", - }, - ]) - }) - test("ignore api", async () => { - const data = await fetchProxy("http://localhost:3000/api/peaks", { - mock: true, - }) - expect(await data.json()).toStrictEqual([ - { - id: 1, - name: "Mont Blanc", - height: 4808, - country: "France", - range: "Alps", - }, - ]) - }) - }) - - describe("fetch with rewriteResponses", () => { - beforeEach(() => { - // reset localDB - fetchProxyInitDB(null) - - // setup mock db.json - const db = { - peaks: [ - { - id: 1, - name: "Mont Blanc", - height: 4808, - country: "France", - range: "Alps", - }, - ], - } - - const rewriteResponses = { - POST: { - "^/peaks": { certificate: "testCertificate" }, - }, - } - - fetchProxyInitDB(db, { rewriteResponses }) - }) - - test("POST", async () => { - const data = await fetchProxy("http://localhost:3000/peaks", { - method: "POST", - body: JSON.stringify({ - name: "test", - }), - mock: true, - }) - expect(await data.json()).toStrictEqual({ - certificate: "testCertificate", - }) - }) - }) -})