mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
wip: restructure to monorepo
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
1 parent
8f9dd11420
commit
a9f150a827
3232 files changed
+373174
-11298
No files matched your search
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"useWorkspaces": false,
|
||||
"packages": ["packages/*"],
|
||||
"version": "0.0.0",
|
||||
"npmClient": "yarn"
|
||||
}
|
||||
+8
-463
@@ -1,474 +1,19 @@
|
||||
{
|
||||
"name": "@k8slens/open-lens",
|
||||
"productName": "OpenLens",
|
||||
"description": "OpenLens - Open Source IDE for Kubernetes",
|
||||
"homepage": "https://github.com/lensapp/lens",
|
||||
"version": "6.4.0-alpha.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lensapp/lens.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"bugs": {
|
||||
"url": "https://github.com/lensapp/lens/issues"
|
||||
},
|
||||
"main": "static/build/main.js",
|
||||
"exports": {
|
||||
"./main": "./static/build/library/main.js",
|
||||
"./renderer": "./static/build/library/renderer.js",
|
||||
"./common": "./static/build/library/common.js",
|
||||
"./styles": "./static/build/library/renderer.css"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"main": [
|
||||
"./src/main/library.ts"
|
||||
],
|
||||
"renderer": [
|
||||
"./src/renderer/library.ts"
|
||||
],
|
||||
"common": [
|
||||
"./src/common/library.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"build/download_binaries.ts",
|
||||
"build/*.plist",
|
||||
"build/installer.nsh",
|
||||
"build/notarize.js",
|
||||
"src/**/*",
|
||||
"static/build/library/**/*",
|
||||
"templates/**/*",
|
||||
"types/*",
|
||||
"tsconfig.json"
|
||||
],
|
||||
"copyright": "© 2022 OpenLens Authors",
|
||||
"license": "MIT",
|
||||
"author": "OpenLens Authors <info@k8slens.dev>",
|
||||
"name": "lens-monorepo",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"adr:create": "echo \"What is the title?\"; read title; adr new \"$title\"",
|
||||
"adr:change-status": "echo \"Decision number?:\"; read decision; adr status $decision",
|
||||
"adr:update-readme": "adr update",
|
||||
"adr:list": "adr list",
|
||||
"dev": "concurrently -i -k \"yarn run dev-run -C\" yarn:dev:*",
|
||||
"dev-build": "concurrently yarn:compile:*",
|
||||
"debug-build": "concurrently yarn:compile:main yarn:compile:extension-types",
|
||||
"dev-run": "nodemon --watch ./static/build/main.js --exec \"electron --remote-debugging-port=9223 --inspect .\"",
|
||||
"dev:main": "yarn run compile:main --watch --progress",
|
||||
"dev:renderer": "yarn run ts-node webpack/dev-server.ts",
|
||||
"compile-library": "env NODE_ENV=production yarn run webpack --config webpack/library-bundle.ts",
|
||||
"compile": "env NODE_ENV=production concurrently yarn:compile:*",
|
||||
"compile:main": "yarn run webpack --config webpack/main.ts",
|
||||
"compile:renderer": "yarn run webpack --config webpack/renderer.ts",
|
||||
"compile:extension-types": "yarn run webpack --config webpack/extensions.ts",
|
||||
"compile:node-fetch": "yarn run webpack --config webpack/node-fetch.ts",
|
||||
"prepare": "yarn run compile:node-fetch",
|
||||
"npm:fix-extensions-package-version": "yarn run ts-node build/set_extensions_npm_version.ts",
|
||||
"build:linux": "yarn run compile && electron-builder --linux --dir",
|
||||
"build:mac": "yarn run compile && electron-builder --mac --dir",
|
||||
"build:win": "yarn run compile && electron-builder --win --dir",
|
||||
"integration": "jest --runInBand --detectOpenHandles --forceExit integration",
|
||||
"test:unit": "func() { jest ${1} --watch --testPathIgnorePatterns integration; }; func",
|
||||
"test:integration": "func() { jest ${1:-xyz} --watch --runInBand --detectOpenHandles --forceExit --modulePaths=[\"<rootDir>/integration/\"]; }; func",
|
||||
"dist": "yarn run compile && electron-builder --publish onTag",
|
||||
"dist:dir": "yarn run dist --dir -c.compression=store -c.mac.identity=null",
|
||||
"download:binaries": "yarn run ts-node build/download_binaries.ts",
|
||||
"build:tray-icons": "yarn run ts-node build/generate-tray-icons.ts",
|
||||
"build:theme-vars": "yarn run ts-node build/build_theme_vars.ts",
|
||||
"lint": "PROD=true yarn run eslint --ext js,ts,tsx --max-warnings=0 .",
|
||||
"lint:fix": "yarn run lint --fix",
|
||||
"mkdocs-serve-local": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -it -p 8000:8000 -v ${PWD}:/docs mkdocs-serve-local:latest",
|
||||
"verify-docs": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -v ${PWD}:/docs mkdocs-serve-local:latest build --strict",
|
||||
"typedocs-extensions-api": "yarn run typedoc src/extensions/extension-api.ts",
|
||||
"version-checkout": "cat package.json | jq '.version' -r | xargs printf \"release/v%s\" | xargs git checkout -b",
|
||||
"version-commit": "cat package.json | jq '.version' -r | xargs printf \"release v%s\" | git commit --no-edit -s -F -",
|
||||
"version": "yarn run version-checkout && git add package.json && yarn run version-commit",
|
||||
"postversion": "git push --set-upstream ${GIT_REMOTE:-origin} release/v$npm_package_version",
|
||||
"precreate-release-pr": "npx swc ./scripts/create-release-pr.ts -o ./scripts/create-release-pr.mjs",
|
||||
"create-release-pr": "node ./scripts/create-release-pr.mjs"
|
||||
},
|
||||
"config": {
|
||||
"k8sProxyVersion": "0.3.0",
|
||||
"bundledKubectlVersion": "1.23.3",
|
||||
"bundledHelmVersion": "3.7.2",
|
||||
"sentryDsn": "",
|
||||
"contentSecurityPolicy": "script-src 'unsafe-eval' 'self'; frame-src https://*.lens.app:*/; img-src * data:",
|
||||
"welcomeRoute": "/welcome"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 <17"
|
||||
},
|
||||
"jest": {
|
||||
"collectCoverage": false,
|
||||
"verbose": true,
|
||||
"transform": {
|
||||
"^.+\\.(t|j)sx?$": [
|
||||
"@swc/jest"
|
||||
]
|
||||
},
|
||||
"testEnvironment": "jsdom",
|
||||
"resolver": "<rootDir>/src/jest-28-resolver.js",
|
||||
"moduleNameMapper": {
|
||||
"\\.(css|scss)$": "identity-obj-proxy",
|
||||
"\\.(svg|png|jpg|eot|woff2?|ttf)$": "<rootDir>/__mocks__/assetMock.ts"
|
||||
},
|
||||
"modulePathIgnorePatterns": [
|
||||
"<rootDir>/dist",
|
||||
"<rootDir>/packages"
|
||||
],
|
||||
"setupFiles": [
|
||||
"<rootDir>/src/jest.setup.ts",
|
||||
"jest-canvas-mock"
|
||||
],
|
||||
"globalSetup": "<rootDir>/src/jest.timezone.ts",
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/src/jest-after-env.setup.ts"
|
||||
],
|
||||
"runtime": "@side/jest-runtime"
|
||||
},
|
||||
"build": {
|
||||
"generateUpdatesFilesForAllChannels": true,
|
||||
"files": [
|
||||
"static/**/*"
|
||||
],
|
||||
"afterSign": "build/notarize.js",
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "templates/",
|
||||
"to": "./templates/",
|
||||
"filter": "**/*.yaml"
|
||||
},
|
||||
"LICENSE"
|
||||
],
|
||||
"linux": {
|
||||
"executableName": "open-lens",
|
||||
"category": "Network",
|
||||
"artifactName": "${productName}-${version}.${arch}.${ext}",
|
||||
"target": [
|
||||
"deb",
|
||||
"rpm",
|
||||
"AppImage"
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "binaries/client/linux/${arch}/kubectl",
|
||||
"to": "./${arch}/kubectl"
|
||||
},
|
||||
{
|
||||
"from": "binaries/client/linux/${arch}/lens-k8s-proxy",
|
||||
"to": "./${arch}/lens-k8s-proxy"
|
||||
},
|
||||
{
|
||||
"from": "binaries/client/linux/${arch}/helm",
|
||||
"to": "./${arch}/helm"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rpm": {
|
||||
"fpm": [
|
||||
"--rpm-rpmbuild-define=%define _build_id_links none"
|
||||
]
|
||||
},
|
||||
"mac": {
|
||||
"executableName": "OpenLens",
|
||||
"hardenedRuntime": true,
|
||||
"gatekeeperAssess": false,
|
||||
"entitlements": "build/entitlements.mac.plist",
|
||||
"entitlementsInherit": "build/entitlements.mac.plist",
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "binaries/client/darwin/${arch}/kubectl",
|
||||
"to": "./${arch}/kubectl"
|
||||
},
|
||||
{
|
||||
"from": "binaries/client/darwin/${arch}/lens-k8s-proxy",
|
||||
"to": "./${arch}/lens-k8s-proxy"
|
||||
},
|
||||
{
|
||||
"from": "binaries/client/darwin/${arch}/helm",
|
||||
"to": "./${arch}/helm"
|
||||
}
|
||||
]
|
||||
},
|
||||
"win": {
|
||||
"executableName": "OpenLens.exe",
|
||||
"target": [
|
||||
"nsis"
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "binaries/client/windows/${arch}/kubectl.exe",
|
||||
"to": "./${arch}/kubectl.exe"
|
||||
},
|
||||
{
|
||||
"from": "binaries/client/windows/${arch}/lens-k8s-proxy.exe",
|
||||
"to": "./${arch}/lens-k8s-proxy.exe"
|
||||
},
|
||||
{
|
||||
"from": "binaries/client/windows/${arch}/helm.exe",
|
||||
"to": "./${arch}/helm.exe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"nsis": {
|
||||
"include": "build/installer.nsh",
|
||||
"oneClick": false,
|
||||
"allowElevation": true,
|
||||
"createStartMenuShortcut": true,
|
||||
"allowToChangeInstallationDirectory": true
|
||||
},
|
||||
"protocols": {
|
||||
"name": "Lens Protocol Handler",
|
||||
"schemes": [
|
||||
"lens"
|
||||
],
|
||||
"role": "Viewer"
|
||||
}
|
||||
},
|
||||
"resolutions": {
|
||||
"@astronautlabs/jsonpath/underscore": "^1.12.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astronautlabs/jsonpath": "^1.1.0",
|
||||
"@hapi/call": "^9.0.0",
|
||||
"@hapi/subtext": "^7.0.4",
|
||||
"@kubernetes/client-node": "^0.18.0",
|
||||
"@material-ui/styles": "^4.11.5",
|
||||
"@ogre-tools/fp": "^12.0.1",
|
||||
"@ogre-tools/injectable": "^12.0.1",
|
||||
"@ogre-tools/injectable-extension-for-auto-registration": "^12.0.1",
|
||||
"@ogre-tools/injectable-extension-for-mobx": "^12.0.1",
|
||||
"@ogre-tools/injectable-react": "^12.0.1",
|
||||
"@sentry/electron": "^3.0.8",
|
||||
"@sentry/integrations": "^6.19.3",
|
||||
"@side/jest-runtime": "^1.0.1",
|
||||
"abort-controller": "^3.0.0",
|
||||
"auto-bind": "^4.0.0",
|
||||
"await-lock": "^2.2.2",
|
||||
"byline": "^5.0.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"conf": "^7.1.2",
|
||||
"crypto-js": "^4.1.1",
|
||||
"electron-devtools-installer": "^3.2.0",
|
||||
"electron-updater": "^4.6.5",
|
||||
"electron-window-state": "^5.0.3",
|
||||
"filehound": "^1.17.6",
|
||||
"fs-extra": "^9.0.1",
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"got": "^11.8.6",
|
||||
"grapheme-splitter": "^1.0.4",
|
||||
"handlebars": "^4.7.7",
|
||||
"history": "^4.10.1",
|
||||
"hpagent": "^1.2.0",
|
||||
"http-proxy": "^1.18.1",
|
||||
"immer": "^9.0.17",
|
||||
"joi": "^17.7.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jsdom": "^16.7.0",
|
||||
"lodash": "^4.17.15",
|
||||
"marked": "^4.2.5",
|
||||
"md5-file": "^5.0.0",
|
||||
"mobx": "^6.7.0",
|
||||
"mobx-observable-history": "^2.0.3",
|
||||
"mobx-react": "^7.6.0",
|
||||
"mobx-utils": "^6.0.4",
|
||||
"moment": "^2.29.4",
|
||||
"moment-timezone": "^0.5.40",
|
||||
"node-fetch": "^3.3.0",
|
||||
"node-pty": "0.10.1",
|
||||
"npm": "^8.19.3",
|
||||
"p-limit": "^3.1.0",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-material-ui-carousel": "^2.3.11",
|
||||
"react-router": "^5.3.4",
|
||||
"react-virtualized-auto-sizer": "^1.0.7",
|
||||
"readable-stream": "^3.6.0",
|
||||
"request": "^2.88.2",
|
||||
"request-promise-native": "^1.0.9",
|
||||
"rfc6902": "^5.0.1",
|
||||
"selfsigned": "^2.1.1",
|
||||
"semver": "^7.3.8",
|
||||
"tar": "^6.1.13",
|
||||
"tcp-port-used": "^1.0.2",
|
||||
"tempy": "1.0.1",
|
||||
"typed-regex": "^0.0.8",
|
||||
"url-parse": "^1.5.10",
|
||||
"uuid": "^8.3.2",
|
||||
"win-ca": "^3.5.0",
|
||||
"winston": "^3.8.2",
|
||||
"winston-transport-browserconsole": "^1.0.5",
|
||||
"ws": "^8.12.0",
|
||||
"xterm-link-provider": "^1.3.1"
|
||||
"build": "lerna run build",
|
||||
"clean:node_modules": "lerna clean -y && rm -rf node_modules",
|
||||
"mkdocs:serve-local": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -it -p 8000:8000 -v ${PWD}:/docs mkdocs-serve-local:latest",
|
||||
"mkdocs:verify": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -v ${PWD}:/docs mkdocs-serve-local:latest build --strict",
|
||||
"test": "lerna run test:unit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@async-fn/jest": "1.6.4",
|
||||
"@material-ui/core": "^4.12.3",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@material-ui/lab": "^4.0.0-alpha.60",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
|
||||
"@sentry/types": "^6.19.7",
|
||||
"@swc/cli": "^0.1.59",
|
||||
"@swc/core": "^1.3.25",
|
||||
"@swc/jest": "^0.2.24",
|
||||
"@testing-library/dom": "^7.31.2",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/byline": "^4.2.33",
|
||||
"@types/chart.js": "^2.9.36",
|
||||
"@types/circular-dependency-plugin": "5.0.5",
|
||||
"@types/cli-progress": "^3.11.0",
|
||||
"@types/color": "^3.0.3",
|
||||
"@types/command-line-args": "^5.2.0",
|
||||
"@types/crypto-js": "^3.1.47",
|
||||
"@types/dompurify": "^2.4.0",
|
||||
"@types/electron-devtools-installer": "^2.2.1",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/glob-to-regexp": "^0.4.1",
|
||||
"@types/gunzip-maybe": "^1.4.0",
|
||||
"@types/hapi__call": "^9.0.0",
|
||||
"@types/hapi__subtext": "^7.0.0",
|
||||
"@types/html-webpack-plugin": "^3.2.6",
|
||||
"@types/http-proxy": "^1.17.9",
|
||||
"@types/jest": "^28.1.6",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/jsdom": "^16.2.14",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/marked": "^4.0.8",
|
||||
"@types/md5-file": "^4.0.2",
|
||||
"@types/memorystream": "^0.3.0",
|
||||
"@types/mini-css-extract-plugin": "^2.4.0",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/node": "^16.18.11",
|
||||
"@types/proper-lockfile": "^4.1.2",
|
||||
"@types/randomcolor": "^0.5.7",
|
||||
"@types/react": "^17.0.45",
|
||||
"@types/react-beautiful-dnd": "^13.1.3",
|
||||
"@types/react-dom": "^17.0.16",
|
||||
"@types/react-router": "^5.1.19",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-table": "^7.7.14",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
||||
"@types/react-window": "^1.8.5",
|
||||
"@types/readable-stream": "^2.3.13",
|
||||
"@types/request": "^2.48.7",
|
||||
"@types/request-promise-native": "^1.0.18",
|
||||
"@types/semver": "^7.3.13",
|
||||
"@types/sharp": "^0.31.1",
|
||||
"@types/tar": "^6.1.3",
|
||||
"@types/tar-stream": "^2.2.2",
|
||||
"@types/tcp-port-used": "^1.0.1",
|
||||
"@types/tempy": "^0.3.0",
|
||||
"@types/triple-beam": "^1.3.2",
|
||||
"@types/url-parse": "^1.4.8",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/webpack": "^5.28.0",
|
||||
"@types/webpack-dev-server": "^4.7.2",
|
||||
"@types/webpack-env": "^1.18.0",
|
||||
"@types/webpack-node-externals": "^2.5.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.48.1",
|
||||
"@typescript-eslint/parser": "^5.48.1",
|
||||
"adr": "^1.4.3",
|
||||
"ansi_up": "^5.1.0",
|
||||
"chalk": "^4.1.2",
|
||||
"chart.js": "^2.9.4",
|
||||
"circular-dependency-plugin": "^5.2.2",
|
||||
"cli-progress": "^3.11.2",
|
||||
"color": "^3.2.1",
|
||||
"command-line-args": "^5.2.1",
|
||||
"concurrently": "^7.6.0",
|
||||
"css-loader": "^6.7.3",
|
||||
"deepdash": "^5.3.9",
|
||||
"dompurify": "^2.4.3",
|
||||
"electron": "^19.1.9",
|
||||
"electron-builder": "^23.6.0",
|
||||
"electron-notarize": "^0.3.0",
|
||||
"esbuild": "^0.16.14",
|
||||
"esbuild-loader": "^2.20.0",
|
||||
"eslint": "^8.31.0",
|
||||
"eslint-import-resolver-typescript": "^3.5.2",
|
||||
"eslint-plugin-header": "^3.1.1",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-react": "7.31.11",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"fork-ts-checker-webpack-plugin": "^6.5.2",
|
||||
"gunzip-maybe": "^1.4.2",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"ignore-loader": "^0.1.2",
|
||||
"include-media": "^1.4.9",
|
||||
"jest": "^28.1.3",
|
||||
"jest-canvas-mock": "^2.3.1",
|
||||
"jest-environment-jsdom": "^28.1.3",
|
||||
"jest-mock-extended": "^2.0.9",
|
||||
"make-plural": "^6.2.2",
|
||||
"memfs": "^3.4.12",
|
||||
"memorystream": "^0.3.1",
|
||||
"mini-css-extract-plugin": "^2.7.2",
|
||||
"mock-http": "^1.1.0",
|
||||
"monaco-editor": "^0.29.1",
|
||||
"monaco-editor-webpack-plugin": "^5.0.0",
|
||||
"node-gyp": "^8.3.0",
|
||||
"node-loader": "^2.0.0",
|
||||
"nodemon": "^2.0.20",
|
||||
"playwright": "^1.29.2",
|
||||
"postcss": "^8.4.21",
|
||||
"postcss-loader": "^6.2.1",
|
||||
"query-string": "^7.1.3",
|
||||
"randomcolor": "^0.6.2",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-refresh": "^0.14.0",
|
||||
"react-refresh-typescript": "^2.0.7",
|
||||
"react-router-dom": "^5.3.4",
|
||||
"react-select": "^5.7.0",
|
||||
"react-select-event": "^5.5.1",
|
||||
"react-table": "^7.8.0",
|
||||
"react-window": "^1.8.8",
|
||||
"sass": "^1.57.1",
|
||||
"sass-loader": "^12.6.0",
|
||||
"sharp": "^0.31.3",
|
||||
"style-loader": "^3.3.1",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"tar-stream": "^2.2.0",
|
||||
"ts-loader": "^9.4.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"type-fest": "^2.14.0",
|
||||
"typed-emitter": "^1.4.0",
|
||||
"typedoc": "0.23.24",
|
||||
"typedoc-plugin-markdown": "^3.13.6",
|
||||
"typescript": "^4.9.4",
|
||||
"typescript-plugin-css-modules": "^3.4.0",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^4.9.2",
|
||||
"webpack-dev-server": "^4.11.1",
|
||||
"webpack-node-externals": "^3.0.0",
|
||||
"xterm": "^4.19.0",
|
||||
"xterm-addon-fit": "^0.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/byline": "^4.2.33",
|
||||
"@types/chart.js": "^2.9.36",
|
||||
"@types/color": "^3.0.3",
|
||||
"@types/crypto-js": "^3.1.47",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/proper-lockfile": "^4.1.2",
|
||||
"@types/react-dom": "^17.0.16",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
||||
"@types/react-window": "^1.8.5",
|
||||
"@types/request-promise-native": "^1.0.18",
|
||||
"@types/tar": "^6.1.3",
|
||||
"@types/tcp-port-used": "^1.0.1",
|
||||
"@types/url-parse": "^1.4.8",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"monaco-editor": "^0.29.1",
|
||||
"react-select": "^5.7.0",
|
||||
"typed-emitter": "^1.4.0",
|
||||
"xterm-addon-fit": "^0.5.0"
|
||||
"lerna": "^6.3.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
static/build/
|
||||
build/webpack/
|
||||
binaries/
|
||||
dist/
|
||||
node_modules/
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,357 @@
|
||||
{
|
||||
"name": "@k8slens/open-lens",
|
||||
"productName": "OpenLens",
|
||||
"description": "OpenLens - Open Source IDE for Kubernetes",
|
||||
"homepage": "https://github.com/lensapp/lens",
|
||||
"version": "6.4.0-alpha.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lensapp/lens.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"bugs": {
|
||||
"url": "https://github.com/lensapp/lens/issues"
|
||||
},
|
||||
"main": "static/build/main.js",
|
||||
"exports": {
|
||||
"./main": "./static/build/library/main.js",
|
||||
"./renderer": "./static/build/library/renderer.js",
|
||||
"./common": "./static/build/library/common.js",
|
||||
"./styles": "./static/build/library/renderer.css"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"main": [
|
||||
"./static/build/library/src/main/library.d.ts"
|
||||
],
|
||||
"renderer": [
|
||||
"./static/build/library/src/renderer/library.d.ts"
|
||||
],
|
||||
"common": [
|
||||
"./static/build/library/src/common/library.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"build/download_binaries.ts",
|
||||
"build/*.plist",
|
||||
"build/installer.nsh",
|
||||
"build/notarize.js",
|
||||
"static/build/library/**/*",
|
||||
"templates/**/*",
|
||||
"types/*",
|
||||
"tsconfig.json"
|
||||
],
|
||||
"copyright": "© 2022 OpenLens Authors",
|
||||
"license": "MIT",
|
||||
"author": "OpenLens Authors <info@k8slens.dev>",
|
||||
"scripts": {
|
||||
"build": "env NODE_ENV=production yarn run webpack --config webpack/library-bundle.ts",
|
||||
"compile:node-fetch": "yarn run webpack --config webpack/node-fetch.ts",
|
||||
"prepare": "yarn run compile:node-fetch",
|
||||
"build:linux": "yarn run compile && electron-builder --linux --dir",
|
||||
"build:mac": "yarn run compile && electron-builder --mac --dir",
|
||||
"build:win": "yarn run compile && electron-builder --win --dir",
|
||||
"integration": "jest --runInBand --detectOpenHandles --forceExit integration",
|
||||
"test:unit": "func() { jest ${1} --watch --testPathIgnorePatterns integration; }; func",
|
||||
"test:integration": "func() { jest ${1:-xyz} --watch --runInBand --detectOpenHandles --forceExit --modulePaths=[\"<rootDir>/integration/\"]; }; func",
|
||||
"dist": "yarn run compile && electron-builder --publish onTag",
|
||||
"dist:dir": "yarn run dist --dir -c.compression=store -c.mac.identity=null",
|
||||
"download:binaries": "yarn run ts-node build/download_binaries.ts",
|
||||
"build:tray-icons": "yarn run ts-node build/generate-tray-icons.ts",
|
||||
"build:theme-vars": "yarn run ts-node build/build_theme_vars.ts",
|
||||
"lint": "PROD=true yarn run eslint --ext js,ts,tsx --max-warnings=0 .",
|
||||
"lint:fix": "yarn run lint --fix",
|
||||
"version-checkout": "cat package.json | jq '.version' -r | xargs printf \"release/v%s\" | xargs git checkout -b",
|
||||
"version-commit": "cat package.json | jq '.version' -r | xargs printf \"release v%s\" | git commit --no-edit -s -F -",
|
||||
"version": "yarn run version-checkout && git add package.json && yarn run version-commit",
|
||||
"postversion": "git push --set-upstream ${GIT_REMOTE:-origin} release/v$npm_package_version",
|
||||
"precreate-release-pr": "npx swc ./scripts/create-release-pr.ts -o ./scripts/create-release-pr.mjs",
|
||||
"create-release-pr": "node ./scripts/create-release-pr.mjs"
|
||||
},
|
||||
"config": {
|
||||
"k8sProxyVersion": "0.3.0",
|
||||
"bundledKubectlVersion": "1.23.3",
|
||||
"bundledHelmVersion": "3.7.2",
|
||||
"sentryDsn": "",
|
||||
"contentSecurityPolicy": "script-src 'unsafe-eval' 'self'; frame-src https://*.lens.app:*/; img-src * data:",
|
||||
"welcomeRoute": "/welcome"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 <17"
|
||||
},
|
||||
"jest": {
|
||||
"collectCoverage": false,
|
||||
"verbose": true,
|
||||
"transform": {
|
||||
"^.+\\.(t|j)sx?$": [
|
||||
"@swc/jest"
|
||||
]
|
||||
},
|
||||
"testEnvironment": "jsdom",
|
||||
"resolver": "<rootDir>/src/jest-28-resolver.js",
|
||||
"moduleNameMapper": {
|
||||
"\\.(css|scss)$": "identity-obj-proxy",
|
||||
"\\.(svg|png|jpg|eot|woff2?|ttf)$": "<rootDir>/__mocks__/assetMock.ts"
|
||||
},
|
||||
"modulePathIgnorePatterns": [
|
||||
"<rootDir>/dist",
|
||||
"<rootDir>/packages",
|
||||
"<rootDir>/static/build"
|
||||
],
|
||||
"setupFiles": [
|
||||
"<rootDir>/src/jest.setup.ts",
|
||||
"jest-canvas-mock"
|
||||
],
|
||||
"globalSetup": "<rootDir>/src/jest.timezone.ts",
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/src/jest-after-env.setup.ts"
|
||||
],
|
||||
"runtime": "@side/jest-runtime"
|
||||
},
|
||||
"build": {},
|
||||
"resolutions": {
|
||||
"@astronautlabs/jsonpath/underscore": "^1.12.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astronautlabs/jsonpath": "^1.1.0",
|
||||
"@hapi/call": "^9.0.0",
|
||||
"@hapi/subtext": "^7.0.4",
|
||||
"@kubernetes/client-node": "^0.18.0",
|
||||
"@material-ui/styles": "^4.11.5",
|
||||
"@ogre-tools/fp": "^12.0.1",
|
||||
"@ogre-tools/injectable": "^12.0.1",
|
||||
"@ogre-tools/injectable-extension-for-auto-registration": "^12.0.1",
|
||||
"@ogre-tools/injectable-extension-for-mobx": "^12.0.1",
|
||||
"@ogre-tools/injectable-react": "^12.0.1",
|
||||
"@sentry/electron": "^3.0.8",
|
||||
"@sentry/integrations": "^6.19.3",
|
||||
"@side/jest-runtime": "^1.0.1",
|
||||
"abort-controller": "^3.0.0",
|
||||
"auto-bind": "^4.0.0",
|
||||
"await-lock": "^2.2.2",
|
||||
"byline": "^5.0.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"conf": "^7.1.2",
|
||||
"crypto-js": "^4.1.1",
|
||||
"electron-devtools-installer": "^3.2.0",
|
||||
"electron-updater": "^4.6.5",
|
||||
"electron-window-state": "^5.0.3",
|
||||
"filehound": "^1.17.6",
|
||||
"fs-extra": "^9.0.1",
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"got": "^11.8.6",
|
||||
"grapheme-splitter": "^1.0.4",
|
||||
"handlebars": "^4.7.7",
|
||||
"history": "^4.10.1",
|
||||
"hpagent": "^1.2.0",
|
||||
"http-proxy": "^1.18.1",
|
||||
"immer": "^9.0.17",
|
||||
"joi": "^17.7.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jsdom": "^16.7.0",
|
||||
"lodash": "^4.17.15",
|
||||
"marked": "^4.2.5",
|
||||
"md5-file": "^5.0.0",
|
||||
"mobx": "^6.7.0",
|
||||
"mobx-observable-history": "^2.0.3",
|
||||
"mobx-react": "^7.6.0",
|
||||
"mobx-utils": "^6.0.4",
|
||||
"moment": "^2.29.4",
|
||||
"moment-timezone": "^0.5.40",
|
||||
"node-fetch": "^3.3.0",
|
||||
"node-pty": "0.10.1",
|
||||
"npm": "^8.19.3",
|
||||
"p-limit": "^3.1.0",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-material-ui-carousel": "^2.3.11",
|
||||
"react-router": "^5.3.4",
|
||||
"react-virtualized-auto-sizer": "^1.0.7",
|
||||
"readable-stream": "^3.6.0",
|
||||
"request": "^2.88.2",
|
||||
"request-promise-native": "^1.0.9",
|
||||
"rfc6902": "^5.0.1",
|
||||
"selfsigned": "^2.1.1",
|
||||
"semver": "^7.3.8",
|
||||
"tar": "^6.1.13",
|
||||
"tcp-port-used": "^1.0.2",
|
||||
"tempy": "1.0.1",
|
||||
"typed-regex": "^0.0.8",
|
||||
"url-parse": "^1.5.10",
|
||||
"uuid": "^8.3.2",
|
||||
"win-ca": "^3.5.0",
|
||||
"winston": "^3.8.2",
|
||||
"winston-transport-browserconsole": "^1.0.5",
|
||||
"ws": "^8.12.0",
|
||||
"xterm-link-provider": "^1.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@async-fn/jest": "1.6.4",
|
||||
"@material-ui/core": "^4.12.3",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@material-ui/lab": "^4.0.0-alpha.60",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
|
||||
"@sentry/types": "^6.19.7",
|
||||
"@swc/cli": "^0.1.59",
|
||||
"@swc/core": "^1.3.25",
|
||||
"@swc/jest": "^0.2.24",
|
||||
"@testing-library/dom": "^7.31.2",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/byline": "^4.2.33",
|
||||
"@types/chart.js": "^2.9.36",
|
||||
"@types/circular-dependency-plugin": "5.0.5",
|
||||
"@types/cli-progress": "^3.11.0",
|
||||
"@types/color": "^3.0.3",
|
||||
"@types/command-line-args": "^5.2.0",
|
||||
"@types/crypto-js": "^3.1.47",
|
||||
"@types/dompurify": "^2.4.0",
|
||||
"@types/electron-devtools-installer": "^2.2.1",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/glob-to-regexp": "^0.4.1",
|
||||
"@types/gunzip-maybe": "^1.4.0",
|
||||
"@types/hapi__call": "^9.0.0",
|
||||
"@types/hapi__subtext": "^7.0.0",
|
||||
"@types/html-webpack-plugin": "^3.2.6",
|
||||
"@types/http-proxy": "^1.17.9",
|
||||
"@types/jest": "^28.1.6",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/jsdom": "^16.2.14",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/marked": "^4.0.8",
|
||||
"@types/md5-file": "^4.0.2",
|
||||
"@types/memorystream": "^0.3.0",
|
||||
"@types/mini-css-extract-plugin": "^2.4.0",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/node": "^16.18.11",
|
||||
"@types/proper-lockfile": "^4.1.2",
|
||||
"@types/randomcolor": "^0.5.7",
|
||||
"@types/react": "^17.0.45",
|
||||
"@types/react-beautiful-dnd": "^13.1.3",
|
||||
"@types/react-dom": "^17.0.16",
|
||||
"@types/react-router": "^5.1.19",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-table": "^7.7.14",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
||||
"@types/react-window": "^1.8.5",
|
||||
"@types/readable-stream": "^2.3.13",
|
||||
"@types/request": "^2.48.7",
|
||||
"@types/request-promise-native": "^1.0.18",
|
||||
"@types/semver": "^7.3.13",
|
||||
"@types/sharp": "^0.31.1",
|
||||
"@types/tar": "^6.1.3",
|
||||
"@types/tar-stream": "^2.2.2",
|
||||
"@types/tcp-port-used": "^1.0.1",
|
||||
"@types/tempy": "^0.3.0",
|
||||
"@types/triple-beam": "^1.3.2",
|
||||
"@types/url-parse": "^1.4.8",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/webpack": "^5.28.0",
|
||||
"@types/webpack-dev-server": "^4.7.2",
|
||||
"@types/webpack-node-externals": "^2.5.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.48.1",
|
||||
"@typescript-eslint/parser": "^5.48.1",
|
||||
"adr": "^1.4.3",
|
||||
"ansi_up": "^5.1.0",
|
||||
"chalk": "^4.1.2",
|
||||
"chart.js": "^2.9.4",
|
||||
"circular-dependency-plugin": "^5.2.2",
|
||||
"cli-progress": "^3.11.2",
|
||||
"color": "^3.2.1",
|
||||
"command-line-args": "^5.2.1",
|
||||
"concurrently": "^7.6.0",
|
||||
"css-loader": "^6.7.3",
|
||||
"deepdash": "^5.3.9",
|
||||
"dompurify": "^2.4.3",
|
||||
"electron": "^19.1.9",
|
||||
"electron-builder": "^23.6.0",
|
||||
"electron-notarize": "^0.3.0",
|
||||
"esbuild": "^0.16.14",
|
||||
"esbuild-loader": "^2.20.0",
|
||||
"eslint": "^8.31.0",
|
||||
"eslint-import-resolver-typescript": "^3.5.2",
|
||||
"eslint-plugin-header": "^3.1.1",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-react": "7.31.11",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"fork-ts-checker-webpack-plugin": "^6.5.2",
|
||||
"gunzip-maybe": "^1.4.2",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"ignore-loader": "^0.1.2",
|
||||
"include-media": "^1.4.9",
|
||||
"jest": "^28.1.3",
|
||||
"jest-canvas-mock": "^2.3.1",
|
||||
"jest-environment-jsdom": "^28.1.3",
|
||||
"jest-mock-extended": "^2.0.9",
|
||||
"make-plural": "^6.2.2",
|
||||
"memfs": "^3.4.12",
|
||||
"memorystream": "^0.3.1",
|
||||
"mini-css-extract-plugin": "^2.7.2",
|
||||
"mock-http": "^1.1.0",
|
||||
"monaco-editor": "^0.29.1",
|
||||
"monaco-editor-webpack-plugin": "^5.0.0",
|
||||
"node-gyp": "^8.3.0",
|
||||
"node-loader": "^2.0.0",
|
||||
"nodemon": "^2.0.20",
|
||||
"playwright": "^1.29.2",
|
||||
"postcss": "^8.4.21",
|
||||
"postcss-loader": "^6.2.1",
|
||||
"query-string": "^7.1.3",
|
||||
"randomcolor": "^0.6.2",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-refresh": "^0.14.0",
|
||||
"react-refresh-typescript": "^2.0.7",
|
||||
"react-router-dom": "^5.3.4",
|
||||
"react-select": "^5.7.0",
|
||||
"react-select-event": "^5.5.1",
|
||||
"react-table": "^7.8.0",
|
||||
"react-window": "^1.8.8",
|
||||
"sass": "^1.57.1",
|
||||
"sass-loader": "^12.6.0",
|
||||
"sharp": "^0.31.3",
|
||||
"style-loader": "^3.3.1",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"tar-stream": "^2.2.0",
|
||||
"ts-loader": "^9.4.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"type-fest": "^2.14.0",
|
||||
"typed-emitter": "^1.4.0",
|
||||
"typedoc": "0.23.24",
|
||||
"typedoc-plugin-markdown": "^3.13.6",
|
||||
"typescript": "^4.9.4",
|
||||
"typescript-plugin-css-modules": "^3.4.0",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^5.0.1",
|
||||
"webpack-dev-server": "^4.11.1",
|
||||
"webpack-node-externals": "^3.0.0",
|
||||
"xterm": "^4.19.0",
|
||||
"xterm-addon-fit": "^0.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/byline": "^4.2.33",
|
||||
"@types/chart.js": "^2.9.36",
|
||||
"@types/color": "^3.0.3",
|
||||
"@types/crypto-js": "^3.1.47",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/proper-lockfile": "^4.1.2",
|
||||
"@types/react-dom": "^17.0.16",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
||||
"@types/react-window": "^1.8.5",
|
||||
"@types/request-promise-native": "^1.0.18",
|
||||
"@types/tar": "^6.1.3",
|
||||
"@types/tcp-port-used": "^1.0.1",
|
||||
"@types/url-parse": "^1.4.8",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"monaco-editor": "^0.29.1",
|
||||
"react-select": "^5.7.0",
|
||||
"typed-emitter": "^1.4.0",
|
||||
"xterm-addon-fit": "^0.5.0"
|
||||
}
|
||||
}
|
||||
Whitespace-only changes.
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { CatalogCategorySpec } from "../catalog";
|
||||
import { CatalogCategory, CatalogCategoryRegistry } from "../catalog";
|
||||
|
||||
class TestCatalogCategoryRegistry extends CatalogCategoryRegistry { }
|
||||
|
||||
class TestCatalogCategory extends CatalogCategory {
|
||||
public readonly apiVersion = "catalog.k8slens.dev/v1alpha1";
|
||||
public readonly kind = "CatalogCategory";
|
||||
public metadata = {
|
||||
name: "Test Category",
|
||||
icon: "",
|
||||
};
|
||||
public spec: CatalogCategorySpec = {
|
||||
group: "entity.k8slens.dev",
|
||||
versions: [],
|
||||
names: {
|
||||
kind: "Test",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
class TestCatalogCategory2 extends CatalogCategory {
|
||||
public readonly apiVersion = "catalog.k8slens.dev/v1alpha1";
|
||||
public readonly kind = "CatalogCategory";
|
||||
public metadata = {
|
||||
name: "Test Category 2",
|
||||
icon: "",
|
||||
};
|
||||
public spec: CatalogCategorySpec = {
|
||||
group: "entity.k8slens.dev",
|
||||
versions: [],
|
||||
names: {
|
||||
kind: "Test2",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe("CatalogCategoryRegistry", () => {
|
||||
it("should remove only the category registered when running the disposer", () => {
|
||||
const registry = new TestCatalogCategoryRegistry();
|
||||
|
||||
expect(registry.items.length).toBe(0);
|
||||
|
||||
const d1 = registry.add(new TestCatalogCategory());
|
||||
const d2 = registry.add(new TestCatalogCategory2());
|
||||
|
||||
expect(registry.items.length).toBe(2);
|
||||
|
||||
d1();
|
||||
expect(registry.items.length).toBe(1);
|
||||
|
||||
d2();
|
||||
expect(registry.items.length).toBe(0);
|
||||
});
|
||||
|
||||
it("doesn't return items that are filtered out", () => {
|
||||
const registry = new TestCatalogCategoryRegistry();
|
||||
|
||||
registry.add(new TestCatalogCategory());
|
||||
registry.add(new TestCatalogCategory2());
|
||||
|
||||
expect(registry.items.length).toBe(2);
|
||||
expect(registry.filteredItems.length).toBe(2);
|
||||
|
||||
const disposer = registry.addCatalogCategoryFilter(category => category.metadata.name === "Test Category");
|
||||
|
||||
expect(registry.items.length).toBe(2);
|
||||
expect(registry.filteredItems.length).toBe(1);
|
||||
|
||||
const disposer2 = registry.addCatalogCategoryFilter(category => category.metadata.name === "foo");
|
||||
|
||||
expect(registry.items.length).toBe(2);
|
||||
expect(registry.filteredItems.length).toBe(0);
|
||||
|
||||
disposer();
|
||||
|
||||
expect(registry.items.length).toBe(2);
|
||||
expect(registry.filteredItems.length).toBe(0);
|
||||
|
||||
disposer2();
|
||||
|
||||
expect(registry.items.length).toBe(2);
|
||||
expect(registry.filteredItems.length).toBe(2);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { CatalogCategory } from "../catalog";
|
||||
import type { CatalogCategorySpec } from "../catalog";
|
||||
|
||||
class TestCatalogCategoryWithoutBadge extends CatalogCategory {
|
||||
public readonly apiVersion = "catalog.k8slens.dev/v1alpha1";
|
||||
public readonly kind = "CatalogCategory";
|
||||
|
||||
public metadata = {
|
||||
name: "Test Category",
|
||||
icon: "",
|
||||
};
|
||||
|
||||
public spec: CatalogCategorySpec = {
|
||||
group: "entity.k8slens.dev",
|
||||
versions: [],
|
||||
names: {
|
||||
kind: "Test",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
class TestCatalogCategoryWithBadge extends TestCatalogCategoryWithoutBadge {
|
||||
getBadge() {
|
||||
return (<div>Test Badge</div>);
|
||||
}
|
||||
}
|
||||
|
||||
describe("CatalogCategory", () => {
|
||||
it("returns name", () => {
|
||||
const category = new TestCatalogCategoryWithoutBadge();
|
||||
|
||||
expect(category.getName()).toEqual("Test Category");
|
||||
});
|
||||
|
||||
it("doesn't return badge by default", () => {
|
||||
const category = new TestCatalogCategoryWithoutBadge();
|
||||
|
||||
expect(category.getBadge()).toEqual(null);
|
||||
});
|
||||
|
||||
it("returns a badge", () => {
|
||||
const category = new TestCatalogCategoryWithBadge();
|
||||
|
||||
expect(category.getBadge()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,359 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { ClusterStore } from "../cluster-store/cluster-store";
|
||||
import type { GetCustomKubeConfigFilePath } from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
|
||||
import getCustomKubeConfigFilePathInjectable from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
|
||||
import clusterStoreInjectable from "../cluster-store/cluster-store.injectable";
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import type { CreateCluster } from "../cluster/create-cluster-injection-token";
|
||||
import { createClusterInjectionToken } from "../cluster/create-cluster-injection-token";
|
||||
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
||||
import assert from "assert";
|
||||
import directoryForTempInjectable from "../app-paths/directory-for-temp/directory-for-temp.injectable";
|
||||
import kubectlBinaryNameInjectable from "../../main/kubectl/binary-name.injectable";
|
||||
import kubectlDownloadingNormalizedArchInjectable from "../../main/kubectl/normalized-arch.injectable";
|
||||
import normalizedPlatformInjectable from "../vars/normalized-platform.injectable";
|
||||
import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable";
|
||||
import type { WriteJsonSync } from "../fs/write-json-sync.injectable";
|
||||
import writeJsonSyncInjectable from "../fs/write-json-sync.injectable";
|
||||
import type { ReadFileSync } from "../fs/read-file-sync.injectable";
|
||||
import readFileSyncInjectable from "../fs/read-file-sync.injectable";
|
||||
import { readFileSync } from "fs";
|
||||
import type { WriteFileSync } from "../fs/write-file-sync.injectable";
|
||||
import writeFileSyncInjectable from "../fs/write-file-sync.injectable";
|
||||
import type { WriteBufferSync } from "../fs/write-buffer-sync.injectable";
|
||||
import writeBufferSyncInjectable from "../fs/write-buffer-sync.injectable";
|
||||
|
||||
// NOTE: this is intended to read the actual file system
|
||||
const testDataIcon = readFileSync("test-data/cluster-store-migration-icon.png");
|
||||
const clusterServerUrl = "https://localhost";
|
||||
const kubeconfig = `
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: ${clusterServerUrl}
|
||||
name: test
|
||||
contexts:
|
||||
- context:
|
||||
cluster: test
|
||||
user: test
|
||||
name: foo
|
||||
- context:
|
||||
cluster: test
|
||||
user: test
|
||||
name: foo2
|
||||
current-context: test
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: test
|
||||
user:
|
||||
token: kubeconfig-user-q4lm4:xxxyyyy
|
||||
`;
|
||||
|
||||
describe("cluster-store", () => {
|
||||
let di: DiContainer;
|
||||
let clusterStore: ClusterStore;
|
||||
let createCluster: CreateCluster;
|
||||
let writeJsonSync: WriteJsonSync;
|
||||
let writeFileSync: WriteFileSync;
|
||||
let writeBufferSync: WriteBufferSync;
|
||||
let readFileSync: ReadFileSync;
|
||||
let getCustomKubeConfigFilePath: GetCustomKubeConfigFilePath;
|
||||
let writeFileSyncAndReturnPath: (filePath: string, contents: string) => string;
|
||||
|
||||
beforeEach(async () => {
|
||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data");
|
||||
di.override(directoryForTempInjectable, () => "/some-temp-directory");
|
||||
di.override(kubectlBinaryNameInjectable, () => "kubectl");
|
||||
di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
|
||||
di.override(normalizedPlatformInjectable, () => "darwin");
|
||||
createCluster = di.inject(createClusterInjectionToken);
|
||||
getCustomKubeConfigFilePath = di.inject(getCustomKubeConfigFilePathInjectable);
|
||||
writeJsonSync = di.inject(writeJsonSyncInjectable);
|
||||
writeFileSync = di.inject(writeFileSyncInjectable);
|
||||
writeBufferSync = di.inject(writeBufferSyncInjectable);
|
||||
readFileSync = di.inject(readFileSyncInjectable);
|
||||
writeFileSyncAndReturnPath = (filePath, contents) => (writeFileSync(filePath, contents), filePath);
|
||||
});
|
||||
|
||||
describe("empty config", () => {
|
||||
beforeEach(async () => {
|
||||
writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", {});
|
||||
clusterStore = di.inject(clusterStoreInjectable);
|
||||
clusterStore.load();
|
||||
});
|
||||
|
||||
describe("with foo cluster added", () => {
|
||||
beforeEach(() => {
|
||||
const cluster = createCluster({
|
||||
id: "foo",
|
||||
contextName: "foo",
|
||||
preferences: {
|
||||
terminalCWD: "/some-directory-for-user-data",
|
||||
icon: "data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5",
|
||||
clusterName: "minikube",
|
||||
},
|
||||
kubeConfigPath: writeFileSyncAndReturnPath(
|
||||
getCustomKubeConfigFilePath("foo"),
|
||||
kubeconfig,
|
||||
),
|
||||
}, {
|
||||
clusterServerUrl,
|
||||
});
|
||||
|
||||
clusterStore.addCluster(cluster);
|
||||
});
|
||||
|
||||
it("adds new cluster to store", async () => {
|
||||
const storedCluster = clusterStore.getById("foo");
|
||||
|
||||
assert(storedCluster);
|
||||
|
||||
expect(storedCluster.id).toBe("foo");
|
||||
expect(storedCluster.preferences.terminalCWD).toBe("/some-directory-for-user-data");
|
||||
expect(storedCluster.preferences.icon).toBe(
|
||||
"data:image/jpeg;base64, iVBORw0KGgoAAAANSUhEUgAAA1wAAAKoCAYAAABjkf5",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with prod and dev clusters added", () => {
|
||||
beforeEach(() => {
|
||||
const store = clusterStore;
|
||||
|
||||
store.addCluster({
|
||||
id: "prod",
|
||||
contextName: "foo",
|
||||
preferences: {
|
||||
clusterName: "prod",
|
||||
},
|
||||
kubeConfigPath: writeFileSyncAndReturnPath(
|
||||
getCustomKubeConfigFilePath("prod"),
|
||||
kubeconfig,
|
||||
),
|
||||
});
|
||||
store.addCluster({
|
||||
id: "dev",
|
||||
contextName: "foo2",
|
||||
preferences: {
|
||||
clusterName: "dev",
|
||||
},
|
||||
kubeConfigPath: writeFileSyncAndReturnPath(
|
||||
getCustomKubeConfigFilePath("dev"),
|
||||
kubeconfig,
|
||||
),
|
||||
});
|
||||
});
|
||||
|
||||
it("check if store can contain multiple clusters", () => {
|
||||
expect(clusterStore.hasClusters()).toBeTruthy();
|
||||
expect(clusterStore.clusters.size).toBe(2);
|
||||
});
|
||||
|
||||
it("check if cluster's kubeconfig file saved", () => {
|
||||
const file = writeFileSyncAndReturnPath(getCustomKubeConfigFilePath("boo"), "kubeconfig");
|
||||
|
||||
expect(readFileSync(file)).toBe("kubeconfig");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("config with existing clusters", () => {
|
||||
beforeEach(() => {
|
||||
writeFileSync("/temp-kube-config", kubeconfig);
|
||||
writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", {
|
||||
__internal__: {
|
||||
migrations: {
|
||||
version: "99.99.99",
|
||||
},
|
||||
},
|
||||
clusters: [
|
||||
{
|
||||
id: "cluster1",
|
||||
kubeConfigPath: "/temp-kube-config",
|
||||
contextName: "foo",
|
||||
preferences: { terminalCWD: "/foo" },
|
||||
workspace: "default",
|
||||
},
|
||||
{
|
||||
id: "cluster2",
|
||||
kubeConfigPath: "/temp-kube-config",
|
||||
contextName: "foo2",
|
||||
preferences: { terminalCWD: "/foo2" },
|
||||
},
|
||||
{
|
||||
id: "cluster3",
|
||||
kubeConfigPath: "/temp-kube-config",
|
||||
contextName: "foo",
|
||||
preferences: { terminalCWD: "/foo" },
|
||||
workspace: "foo",
|
||||
ownerRef: "foo",
|
||||
},
|
||||
],
|
||||
});
|
||||
clusterStore = di.inject(clusterStoreInjectable);
|
||||
clusterStore.load();
|
||||
});
|
||||
it("allows to retrieve a cluster", () => {
|
||||
const storedCluster = clusterStore.getById("cluster1");
|
||||
|
||||
assert(storedCluster);
|
||||
|
||||
expect(storedCluster.id).toBe("cluster1");
|
||||
expect(storedCluster.preferences.terminalCWD).toBe("/foo");
|
||||
});
|
||||
|
||||
it("allows getting all of the clusters", async () => {
|
||||
const storedClusters = clusterStore.clustersList;
|
||||
|
||||
expect(storedClusters.length).toBe(3);
|
||||
expect(storedClusters[0].id).toBe("cluster1");
|
||||
expect(storedClusters[0].preferences.terminalCWD).toBe("/foo");
|
||||
expect(storedClusters[1].id).toBe("cluster2");
|
||||
expect(storedClusters[1].preferences.terminalCWD).toBe("/foo2");
|
||||
expect(storedClusters[2].id).toBe("cluster3");
|
||||
});
|
||||
});
|
||||
|
||||
describe("config with invalid cluster kubeconfig", () => {
|
||||
beforeEach(() => {
|
||||
writeFileSync("/invalid-kube-config", invalidKubeconfig);
|
||||
writeFileSync("/valid-kube-config", kubeconfig);
|
||||
writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", {
|
||||
__internal__: {
|
||||
migrations: {
|
||||
version: "99.99.99",
|
||||
},
|
||||
},
|
||||
clusters: [
|
||||
{
|
||||
id: "cluster1",
|
||||
kubeConfigPath: "/invalid-kube-config",
|
||||
contextName: "test",
|
||||
preferences: { terminalCWD: "/foo" },
|
||||
workspace: "foo",
|
||||
},
|
||||
{
|
||||
id: "cluster2",
|
||||
kubeConfigPath: "/valid-kube-config",
|
||||
contextName: "foo",
|
||||
preferences: { terminalCWD: "/foo" },
|
||||
workspace: "default",
|
||||
},
|
||||
],
|
||||
});
|
||||
clusterStore = di.inject(clusterStoreInjectable);
|
||||
clusterStore.load();
|
||||
});
|
||||
|
||||
it("does not enable clusters with invalid kubeconfig", () => {
|
||||
const storedClusters = clusterStore.clustersList;
|
||||
|
||||
expect(storedClusters.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("pre 3.6.0-beta.1 config with an existing cluster", () => {
|
||||
beforeEach(() => {
|
||||
writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", {
|
||||
__internal__: {
|
||||
migrations: {
|
||||
version: "3.5.0",
|
||||
},
|
||||
},
|
||||
clusters: [
|
||||
{
|
||||
id: "cluster1",
|
||||
kubeConfig: minimalValidKubeConfig,
|
||||
contextName: "cluster",
|
||||
preferences: {
|
||||
icon: "store://icon_path",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
writeBufferSync("/some-directory-for-user-data/icon_path", testDataIcon);
|
||||
|
||||
di.override(storeMigrationVersionInjectable, () => "3.6.0");
|
||||
|
||||
clusterStore = di.inject(clusterStoreInjectable);
|
||||
clusterStore.load();
|
||||
});
|
||||
|
||||
it("migrates to modern format with kubeconfig in a file", async () => {
|
||||
const config = clusterStore.clustersList[0].kubeConfigPath;
|
||||
|
||||
expect(readFileSync(config)).toBe(minimalValidKubeConfig);
|
||||
});
|
||||
|
||||
it("migrates to modern format with icon not in file", async () => {
|
||||
expect(clusterStore.clustersList[0].preferences.icon).toMatch(/data:;base64,/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const invalidKubeconfig = JSON.stringify({
|
||||
apiVersion: "v1",
|
||||
clusters: [{
|
||||
cluster: {
|
||||
server: "https://localhost",
|
||||
},
|
||||
name: "test2",
|
||||
}],
|
||||
contexts: [{
|
||||
context: {
|
||||
cluster: "test",
|
||||
user: "test",
|
||||
},
|
||||
name: "test",
|
||||
}],
|
||||
"current-context": "test",
|
||||
kind: "Config",
|
||||
preferences: {},
|
||||
users: [{
|
||||
user: {
|
||||
token: "kubeconfig-user-q4lm4:xxxyyyy",
|
||||
},
|
||||
name: "test",
|
||||
}],
|
||||
});
|
||||
|
||||
const minimalValidKubeConfig = JSON.stringify({
|
||||
apiVersion: "v1",
|
||||
clusters: [
|
||||
{
|
||||
name: "minikube",
|
||||
cluster: {
|
||||
server: "https://192.168.64.3:8443",
|
||||
},
|
||||
},
|
||||
],
|
||||
"current-context": "minikube",
|
||||
contexts: [
|
||||
{
|
||||
context: {
|
||||
cluster: "minikube",
|
||||
user: "minikube",
|
||||
},
|
||||
name: "minikube",
|
||||
},
|
||||
],
|
||||
users: [
|
||||
{
|
||||
name: "minikube",
|
||||
user: {
|
||||
"client-certificate": "/Users/foo/.minikube/client.crt",
|
||||
"client-key": "/Users/foo/.minikube/client.key",
|
||||
},
|
||||
},
|
||||
],
|
||||
kind: "Config",
|
||||
preferences: {},
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from "../event-emitter";
|
||||
|
||||
describe("EventEmitter", () => {
|
||||
it("should stop early if a listener returns false", () => {
|
||||
let called = false;
|
||||
const e = new EventEmitter<[]>();
|
||||
|
||||
e.addListener(() => false, {});
|
||||
e.addListener(() => { called = true; }, {});
|
||||
e.emit();
|
||||
|
||||
expect(called).toBe(false);
|
||||
});
|
||||
|
||||
it("shouldn't stop early if a listener returns 0", () => {
|
||||
let called = false;
|
||||
const e = new EventEmitter<[]>();
|
||||
|
||||
e.addListener(() => 0 as never, {});
|
||||
e.addListener(() => { called = true; }, {});
|
||||
e.emit();
|
||||
|
||||
expect(called).toBe(true);
|
||||
});
|
||||
|
||||
it("prepended listeners should be called before others", () => {
|
||||
const callOrder: number[] = [];
|
||||
const e = new EventEmitter<[]>();
|
||||
|
||||
e.addListener(() => { callOrder.push(1); }, {});
|
||||
e.addListener(() => { callOrder.push(2); }, {});
|
||||
e.addListener(() => { callOrder.push(3); }, { prepend: true });
|
||||
e.emit();
|
||||
|
||||
expect(callOrder).toStrictEqual([3, 1, 2]);
|
||||
});
|
||||
|
||||
it("once listeners should be called only once", () => {
|
||||
const callOrder: number[] = [];
|
||||
const e = new EventEmitter<[]>();
|
||||
|
||||
e.addListener(() => { callOrder.push(1); }, {});
|
||||
e.addListener(() => { callOrder.push(2); }, {});
|
||||
e.addListener(() => { callOrder.push(3); }, { once: true });
|
||||
e.emit();
|
||||
e.emit();
|
||||
|
||||
expect(callOrder).toStrictEqual([1, 2, 3, 1, 2]);
|
||||
});
|
||||
|
||||
it("removeListener should stop the listener from being called", () => {
|
||||
const callOrder: number[] = [];
|
||||
const e = new EventEmitter<[]>();
|
||||
const r = () => { callOrder.push(3); };
|
||||
|
||||
e.addListener(() => { callOrder.push(1); }, {});
|
||||
e.addListener(() => { callOrder.push(2); }, {});
|
||||
e.addListener(r);
|
||||
|
||||
e.emit();
|
||||
e.removeListener(r);
|
||||
e.emit();
|
||||
|
||||
expect(callOrder).toStrictEqual([1, 2, 3, 1, 2]);
|
||||
});
|
||||
|
||||
it("removeAllListeners should stop the all listeners from being called", () => {
|
||||
const callOrder: number[] = [];
|
||||
const e = new EventEmitter<[]>();
|
||||
|
||||
e.addListener(() => { callOrder.push(1); });
|
||||
e.addListener(() => { callOrder.push(2); });
|
||||
e.addListener(() => { callOrder.push(3); });
|
||||
|
||||
e.emit();
|
||||
e.removeAllListeners();
|
||||
e.emit();
|
||||
|
||||
expect(callOrder).toStrictEqual([1, 2, 3]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,356 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { anyObject } from "jest-mock-extended";
|
||||
import type { CatalogEntity, CatalogEntityData, CatalogEntityKindData } from "../catalog";
|
||||
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import hotbarStoreInjectable from "../hotbars/store.injectable";
|
||||
import type { HotbarStore } from "../hotbars/store";
|
||||
import catalogEntityRegistryInjectable from "../../main/catalog/entity-registry.injectable";
|
||||
import { computed } from "mobx";
|
||||
import hasCategoryForEntityInjectable from "../catalog/has-category-for-entity.injectable";
|
||||
import catalogCatalogEntityInjectable from "../catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable";
|
||||
import loggerInjectable from "../logger.injectable";
|
||||
import type { Logger } from "../logger";
|
||||
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable";
|
||||
import writeJsonSyncInjectable from "../fs/write-json-sync.injectable";
|
||||
|
||||
function getMockCatalogEntity(data: Partial<CatalogEntityData> & CatalogEntityKindData): CatalogEntity {
|
||||
return {
|
||||
getName: jest.fn(() => data.metadata?.name),
|
||||
getId: jest.fn(() => data.metadata?.uid),
|
||||
getSource: jest.fn(() => data.metadata?.source ?? "unknown"),
|
||||
isEnabled: jest.fn(() => data.status?.enabled ?? true),
|
||||
onContextMenuOpen: jest.fn(),
|
||||
onSettingsOpen: jest.fn(),
|
||||
metadata: {},
|
||||
spec: {},
|
||||
status: {},
|
||||
...data,
|
||||
} as CatalogEntity;
|
||||
}
|
||||
|
||||
describe("HotbarStore", () => {
|
||||
let di: DiContainer;
|
||||
let hotbarStore: HotbarStore;
|
||||
let testCluster: CatalogEntity;
|
||||
let minikubeCluster: CatalogEntity;
|
||||
let awsCluster: CatalogEntity;
|
||||
let loggerMock: jest.Mocked<Logger>;
|
||||
|
||||
beforeEach(async () => {
|
||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
testCluster = getMockCatalogEntity({
|
||||
apiVersion: "v1",
|
||||
kind: "Cluster",
|
||||
status: {
|
||||
phase: "Running",
|
||||
},
|
||||
metadata: {
|
||||
uid: "some-test-id",
|
||||
name: "my-test-cluster",
|
||||
source: "local",
|
||||
labels: {},
|
||||
},
|
||||
});
|
||||
minikubeCluster = getMockCatalogEntity({
|
||||
apiVersion: "v1",
|
||||
kind: "Cluster",
|
||||
status: {
|
||||
phase: "Running",
|
||||
},
|
||||
metadata: {
|
||||
uid: "some-minikube-id",
|
||||
name: "my-minikube-cluster",
|
||||
source: "local",
|
||||
labels: {},
|
||||
},
|
||||
});
|
||||
awsCluster = getMockCatalogEntity({
|
||||
apiVersion: "v1",
|
||||
kind: "Cluster",
|
||||
status: {
|
||||
phase: "Running",
|
||||
},
|
||||
metadata: {
|
||||
uid: "some-aws-id",
|
||||
name: "my-aws-cluster",
|
||||
source: "local",
|
||||
labels: {},
|
||||
},
|
||||
});
|
||||
|
||||
di.override(hasCategoryForEntityInjectable, () => () => true);
|
||||
|
||||
loggerMock = {
|
||||
warn: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
info: jest.fn(),
|
||||
silly: jest.fn(),
|
||||
};
|
||||
|
||||
di.override(loggerInjectable, () => loggerMock);
|
||||
|
||||
di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data");
|
||||
|
||||
const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable);
|
||||
const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable);
|
||||
|
||||
catalogEntityRegistry.addComputedSource("some-id", computed(() => [
|
||||
testCluster,
|
||||
minikubeCluster,
|
||||
awsCluster,
|
||||
catalogCatalogEntity,
|
||||
]));
|
||||
});
|
||||
|
||||
describe("given no previous data in store, running all migrations", () => {
|
||||
beforeEach(() => {
|
||||
hotbarStore = di.inject(hotbarStoreInjectable);
|
||||
|
||||
hotbarStore.load();
|
||||
});
|
||||
|
||||
describe("load", () => {
|
||||
it("loads one hotbar by default", () => {
|
||||
expect(hotbarStore.hotbars.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("add", () => {
|
||||
it("adds a hotbar", () => {
|
||||
hotbarStore.add({ name: "hottest" });
|
||||
expect(hotbarStore.hotbars.length).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("hotbar items", () => {
|
||||
it("initially creates 12 empty cells", () => {
|
||||
expect(hotbarStore.getActive().items.length).toEqual(12);
|
||||
});
|
||||
|
||||
it("initially adds catalog entity as first item", () => {
|
||||
expect(hotbarStore.getActive().items[0]?.entity.name).toEqual("Catalog");
|
||||
});
|
||||
|
||||
it("adds items", () => {
|
||||
hotbarStore.addToHotbar(testCluster);
|
||||
const items = hotbarStore.getActive().items.filter(Boolean);
|
||||
|
||||
expect(items.length).toEqual(2);
|
||||
});
|
||||
|
||||
it("removes items", () => {
|
||||
hotbarStore.addToHotbar(testCluster);
|
||||
hotbarStore.removeFromHotbar("some-test-id");
|
||||
hotbarStore.removeFromHotbar("catalog-entity");
|
||||
const items = hotbarStore.getActive().items.filter(Boolean);
|
||||
|
||||
expect(items).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it("does nothing if removing with invalid uid", () => {
|
||||
hotbarStore.addToHotbar(testCluster);
|
||||
hotbarStore.removeFromHotbar("invalid uid");
|
||||
const items = hotbarStore.getActive().items.filter(Boolean);
|
||||
|
||||
expect(items.length).toEqual(2);
|
||||
});
|
||||
|
||||
it("moves item to empty cell", () => {
|
||||
hotbarStore.addToHotbar(testCluster);
|
||||
hotbarStore.addToHotbar(minikubeCluster);
|
||||
hotbarStore.addToHotbar(awsCluster);
|
||||
|
||||
expect(hotbarStore.getActive().items[6]).toBeNull();
|
||||
|
||||
hotbarStore.restackItems(1, 5);
|
||||
|
||||
expect(hotbarStore.getActive().items[5]).toBeTruthy();
|
||||
expect(hotbarStore.getActive().items[5]?.entity.uid).toEqual("some-test-id");
|
||||
});
|
||||
|
||||
it("moves items down", () => {
|
||||
hotbarStore.addToHotbar(testCluster);
|
||||
hotbarStore.addToHotbar(minikubeCluster);
|
||||
hotbarStore.addToHotbar(awsCluster);
|
||||
|
||||
// aws -> catalog
|
||||
hotbarStore.restackItems(3, 0);
|
||||
|
||||
const items = hotbarStore.getActive().items.map(item => item?.entity.uid || null);
|
||||
|
||||
expect(items.slice(0, 4)).toEqual(["some-aws-id", "catalog-entity", "some-test-id", "some-minikube-id"]);
|
||||
});
|
||||
|
||||
it("moves items up", () => {
|
||||
hotbarStore.addToHotbar(testCluster);
|
||||
hotbarStore.addToHotbar(minikubeCluster);
|
||||
hotbarStore.addToHotbar(awsCluster);
|
||||
|
||||
// test -> aws
|
||||
hotbarStore.restackItems(1, 3);
|
||||
|
||||
const items = hotbarStore.getActive().items.map(item => item?.entity.uid || null);
|
||||
|
||||
expect(items.slice(0, 4)).toEqual(["catalog-entity", "some-minikube-id", "some-aws-id", "some-test-id"]);
|
||||
});
|
||||
|
||||
it("logs an error if cellIndex is out of bounds", () => {
|
||||
hotbarStore.add({ name: "hottest", id: "hottest" });
|
||||
hotbarStore.setActiveHotbar("hottest");
|
||||
|
||||
hotbarStore.addToHotbar(testCluster, -1);
|
||||
expect(loggerMock.error).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject());
|
||||
|
||||
hotbarStore.addToHotbar(testCluster, 12);
|
||||
expect(loggerMock.error).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject());
|
||||
|
||||
hotbarStore.addToHotbar(testCluster, 13);
|
||||
expect(loggerMock.error).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject());
|
||||
});
|
||||
|
||||
it("throws an error if getId is invalid or returns not a string", () => {
|
||||
expect(() => hotbarStore.addToHotbar({} as any)).toThrowError(TypeError);
|
||||
expect(() => hotbarStore.addToHotbar({ getId: () => true } as any)).toThrowError(TypeError);
|
||||
});
|
||||
|
||||
it("throws an error if getName is invalid or returns not a string", () => {
|
||||
expect(() => hotbarStore.addToHotbar({ getId: () => "" } as any)).toThrowError(TypeError);
|
||||
expect(() => hotbarStore.addToHotbar({ getId: () => "", getName: () => 4 } as any)).toThrowError(TypeError);
|
||||
});
|
||||
|
||||
it("does nothing when item moved to same cell", () => {
|
||||
hotbarStore.addToHotbar(testCluster);
|
||||
hotbarStore.restackItems(1, 1);
|
||||
|
||||
expect(hotbarStore.getActive().items[1]?.entity.uid).toEqual("some-test-id");
|
||||
});
|
||||
|
||||
it("new items takes first empty cell", () => {
|
||||
hotbarStore.addToHotbar(testCluster);
|
||||
hotbarStore.addToHotbar(awsCluster);
|
||||
hotbarStore.restackItems(0, 3);
|
||||
hotbarStore.addToHotbar(minikubeCluster);
|
||||
|
||||
expect(hotbarStore.getActive().items[0]?.entity.uid).toEqual("some-minikube-id");
|
||||
});
|
||||
|
||||
it("throws if invalid arguments provided", () => {
|
||||
hotbarStore.addToHotbar(testCluster);
|
||||
|
||||
expect(() => hotbarStore.restackItems(-5, 0)).toThrow();
|
||||
expect(() => hotbarStore.restackItems(2, -1)).toThrow();
|
||||
expect(() => hotbarStore.restackItems(14, 1)).toThrow();
|
||||
expect(() => hotbarStore.restackItems(11, 112)).toThrow();
|
||||
});
|
||||
|
||||
it("checks if entity already pinned to hotbar", () => {
|
||||
hotbarStore.addToHotbar(testCluster);
|
||||
|
||||
expect(hotbarStore.isAddedToActive(testCluster)).toBeTruthy();
|
||||
expect(hotbarStore.isAddedToActive(awsCluster)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("given data from 5.0.0-beta.3 and version being 5.0.0-beta.10", () => {
|
||||
beforeEach(() => {
|
||||
const writeJsonSync = di.inject(writeJsonSyncInjectable);
|
||||
|
||||
writeJsonSync("/some-directory-for-user-data/lens-hotbar-store.json", {
|
||||
__internal__: {
|
||||
migrations: {
|
||||
version: "5.0.0-beta.3",
|
||||
},
|
||||
},
|
||||
hotbars: [
|
||||
{
|
||||
id: "3caac17f-aec2-4723-9694-ad204465d935",
|
||||
name: "myhotbar",
|
||||
items: [
|
||||
{
|
||||
entity: {
|
||||
uid: "some-aws-id",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity: {
|
||||
uid: "55b42c3c7ba3b04193416cda405269a5",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity: {
|
||||
uid: "176fd331968660832f62283219d7eb6e",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity: {
|
||||
uid: "61c4fb45528840ebad1badc25da41d14",
|
||||
name: "user1-context",
|
||||
source: "local",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity: {
|
||||
uid: "27d6f99fe9e7548a6e306760bfe19969",
|
||||
name: "foo2",
|
||||
source: "local",
|
||||
},
|
||||
},
|
||||
null,
|
||||
{
|
||||
entity: {
|
||||
uid: "c0b20040646849bb4dcf773e43a0bf27",
|
||||
name: "multinode-demo",
|
||||
source: "local",
|
||||
},
|
||||
},
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
di.override(storeMigrationVersionInjectable, () => "5.0.0-beta.10");
|
||||
|
||||
hotbarStore = di.inject(hotbarStoreInjectable);
|
||||
|
||||
hotbarStore.load();
|
||||
});
|
||||
|
||||
it("allows to retrieve a hotbar", () => {
|
||||
const hotbar = hotbarStore.findById("3caac17f-aec2-4723-9694-ad204465d935");
|
||||
|
||||
expect(hotbar?.id).toBe("3caac17f-aec2-4723-9694-ad204465d935");
|
||||
});
|
||||
|
||||
it("clears cells without entity", () => {
|
||||
const items = hotbarStore.hotbars[0].items;
|
||||
|
||||
expect(items[2]).toBeNull();
|
||||
});
|
||||
|
||||
it("adds extra data to cells with according entity", () => {
|
||||
const items = hotbarStore.hotbars[0].items;
|
||||
|
||||
expect(items[0]).toEqual({
|
||||
entity: {
|
||||
name: "my-aws-cluster",
|
||||
source: "local",
|
||||
uid: "some-aws-id",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { KubeConfig } from "@kubernetes/client-node";
|
||||
import { validateKubeConfig, loadConfigFromString } from "../kube-helpers";
|
||||
|
||||
const kubeconfig = `
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: https://localhost
|
||||
name: test
|
||||
contexts:
|
||||
- context:
|
||||
cluster: test
|
||||
user: test
|
||||
name: valid
|
||||
- context:
|
||||
cluster: test2
|
||||
user: test
|
||||
name: invalidCluster
|
||||
- context:
|
||||
cluster: test
|
||||
user: test2
|
||||
name: invalidUser
|
||||
- context:
|
||||
cluster: test
|
||||
user: invalidExec
|
||||
name: invalidExec
|
||||
current-context: test
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: test
|
||||
user:
|
||||
exec:
|
||||
command: echo
|
||||
- name: invalidExec
|
||||
user:
|
||||
exec:
|
||||
command: foo
|
||||
`;
|
||||
|
||||
interface Kubeconfig {
|
||||
apiVersion: string;
|
||||
clusters: [{
|
||||
name: string;
|
||||
cluster: {
|
||||
server: string;
|
||||
};
|
||||
}];
|
||||
contexts: [{
|
||||
context: {
|
||||
cluster: string;
|
||||
user: string;
|
||||
};
|
||||
name: string;
|
||||
}];
|
||||
users: [{
|
||||
name: string;
|
||||
}];
|
||||
kind: string;
|
||||
"current-context": string;
|
||||
preferences: {};
|
||||
}
|
||||
|
||||
let mockKubeConfig: Kubeconfig;
|
||||
|
||||
describe("kube helpers", () => {
|
||||
describe("validateKubeconfig", () => {
|
||||
const kc = new KubeConfig();
|
||||
|
||||
beforeAll(() => {
|
||||
kc.loadFromString(kubeconfig);
|
||||
});
|
||||
describe("with default validation options", () => {
|
||||
describe("with valid kubeconfig", () => {
|
||||
it("does not return an error", () => {
|
||||
expect(validateKubeConfig(kc, "valid")).toBeDefined();
|
||||
});
|
||||
});
|
||||
describe("with invalid context object", () => {
|
||||
it("returns an error", () => {
|
||||
expect(validateKubeConfig(kc, "invalid").error?.toString()).toEqual(
|
||||
expect.stringContaining("No valid context object provided in kubeconfig for context 'invalid'"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with invalid cluster object", () => {
|
||||
it("returns an error", () => {
|
||||
expect(validateKubeConfig(kc, "invalidCluster").error?.toString()).toEqual(
|
||||
expect.stringContaining("No valid cluster object provided in kubeconfig for context 'invalidCluster'"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with invalid user object", () => {
|
||||
it("returns an error", () => {
|
||||
expect(validateKubeConfig(kc, "invalidUser").error?.toString()).toEqual(
|
||||
expect.stringContaining("No valid user object provided in kubeconfig for context 'invalidUser'"),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("pre-validate context object in kubeconfig tests", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("Check logger.error() output", () => {
|
||||
it("invalid yaml string", () => {
|
||||
const invalidYAMLString = "fancy foo config";
|
||||
|
||||
expect(loadConfigFromString(invalidYAMLString).error).toBeInstanceOf(Error);
|
||||
});
|
||||
it("empty contexts", () => {
|
||||
const emptyContexts = `apiVersion: v1\ncontexts: []`;
|
||||
|
||||
expect(loadConfigFromString(emptyContexts).error).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Check valid kubeconfigs", () => {
|
||||
beforeEach(() => {
|
||||
mockKubeConfig = {
|
||||
apiVersion: "v1",
|
||||
clusters: [{
|
||||
name: "minikube",
|
||||
cluster: {
|
||||
server: "https://192.168.64.3:8443",
|
||||
},
|
||||
}],
|
||||
contexts: [{
|
||||
context: {
|
||||
cluster: "minikube",
|
||||
user: "minikube",
|
||||
},
|
||||
name: "minikube",
|
||||
}],
|
||||
users: [{
|
||||
name: "minikube",
|
||||
}],
|
||||
kind: "Config",
|
||||
"current-context": "minikube",
|
||||
preferences: {},
|
||||
};
|
||||
});
|
||||
|
||||
it("single context is ok", async () => {
|
||||
const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig));
|
||||
|
||||
expect(config.getCurrentContext()).toBe("minikube");
|
||||
});
|
||||
|
||||
it("multiple context is ok", async () => {
|
||||
mockKubeConfig.contexts.push({ context: { cluster: "cluster-2", user: "cluster-2" }, name: "cluster-2" });
|
||||
const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig));
|
||||
|
||||
expect(config.getCurrentContext()).toBe("minikube");
|
||||
expect(config.contexts.length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Check invalid kubeconfigs", () => {
|
||||
beforeEach(() => {
|
||||
mockKubeConfig = {
|
||||
apiVersion: "v1",
|
||||
clusters: [{
|
||||
name: "minikube",
|
||||
cluster: {
|
||||
server: "https://192.168.64.3:8443",
|
||||
},
|
||||
}],
|
||||
contexts: [{
|
||||
context: {
|
||||
cluster: "minikube",
|
||||
user: "minikube",
|
||||
},
|
||||
name: "minikube",
|
||||
}],
|
||||
users: [{
|
||||
name: "minikube",
|
||||
}],
|
||||
kind: "Config",
|
||||
"current-context": "minikube",
|
||||
preferences: {},
|
||||
};
|
||||
});
|
||||
|
||||
it("empty name in context causes it to be removed", async () => {
|
||||
mockKubeConfig.contexts.push({ context: { cluster: "cluster-2", user: "cluster-2" }, name: "" });
|
||||
expect(mockKubeConfig.contexts.length).toBe(2);
|
||||
const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig));
|
||||
|
||||
expect(config.getCurrentContext()).toBe("minikube");
|
||||
expect(config.contexts.length).toBe(1);
|
||||
});
|
||||
|
||||
it("empty cluster in context causes it to be removed", async () => {
|
||||
mockKubeConfig.contexts.push({ context: { cluster: "", user: "cluster-2" }, name: "cluster-2" });
|
||||
expect(mockKubeConfig.contexts.length).toBe(2);
|
||||
const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig));
|
||||
|
||||
expect(config.getCurrentContext()).toBe("minikube");
|
||||
expect(config.contexts.length).toBe(1);
|
||||
});
|
||||
|
||||
it("empty user in context causes it to be removed", async () => {
|
||||
mockKubeConfig.contexts.push({ context: { cluster: "cluster-2", user: "" }, name: "cluster-2" });
|
||||
expect(mockKubeConfig.contexts.length).toBe(2);
|
||||
const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig));
|
||||
|
||||
expect(config.getCurrentContext()).toBe("minikube");
|
||||
expect(config.contexts.length).toBe(1);
|
||||
});
|
||||
|
||||
it("invalid context in between valid contexts is removed", async () => {
|
||||
mockKubeConfig.contexts.push({ context: { cluster: "cluster-2", user: "" }, name: "cluster-2" });
|
||||
mockKubeConfig.contexts.push({ context: { cluster: "cluster-3", user: "cluster-3" }, name: "cluster-3" });
|
||||
expect(mockKubeConfig.contexts.length).toBe(3);
|
||||
const { config } = loadConfigFromString(JSON.stringify(mockKubeConfig));
|
||||
|
||||
expect(config.getCurrentContext()).toBe("minikube");
|
||||
expect(config.contexts.length).toBe(2);
|
||||
expect(config.contexts[0].name).toBe("minikube");
|
||||
expect(config.contexts[1].name).toBe("cluster-3");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
describe("Timezones", () => {
|
||||
it("should always be UTC", () => {
|
||||
expect(new Date().getTimezoneOffset()).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
export {};
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { UserStore } from "../user-store";
|
||||
import userStoreInjectable from "../user-store/user-store.injectable";
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import type { ClusterStoreModel } from "../cluster-store/cluster-store";
|
||||
import { defaultThemeId } from "../vars";
|
||||
import writeFileInjectable from "../fs/write-file.injectable";
|
||||
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
||||
import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable";
|
||||
import releaseChannelInjectable from "../vars/release-channel.injectable";
|
||||
import defaultUpdateChannelInjectable from "../../features/application-update/common/selected-update-channel/default-update-channel.injectable";
|
||||
import writeJsonSyncInjectable from "../fs/write-json-sync.injectable";
|
||||
import writeFileSyncInjectable from "../fs/write-file-sync.injectable";
|
||||
|
||||
describe("user store tests", () => {
|
||||
let userStore: UserStore;
|
||||
let di: DiContainer;
|
||||
|
||||
beforeEach(async () => {
|
||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
di.override(writeFileInjectable, () => () => Promise.resolve());
|
||||
di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data");
|
||||
|
||||
di.override(releaseChannelInjectable, () => ({
|
||||
get: () => "latest" as const,
|
||||
init: async () => {},
|
||||
}));
|
||||
await di.inject(defaultUpdateChannelInjectable).init();
|
||||
|
||||
userStore = di.inject(userStoreInjectable);
|
||||
});
|
||||
|
||||
describe("for an empty config", () => {
|
||||
beforeEach(() => {
|
||||
const writeJsonSync = di.inject(writeJsonSyncInjectable);
|
||||
|
||||
writeJsonSync("/some-directory-for-user-data/lens-user-store.json", {});
|
||||
writeJsonSync("/some-directory-for-user-data/kube_config", {});
|
||||
|
||||
userStore.load();
|
||||
});
|
||||
|
||||
it("allows setting and getting preferences", () => {
|
||||
userStore.httpsProxy = "abcd://defg";
|
||||
|
||||
expect(userStore.httpsProxy).toBe("abcd://defg");
|
||||
expect(userStore.colorTheme).toBe(defaultThemeId);
|
||||
|
||||
userStore.colorTheme = "light";
|
||||
expect(userStore.colorTheme).toBe("light");
|
||||
});
|
||||
|
||||
it("correctly resets theme to default value", async () => {
|
||||
userStore.colorTheme = "some other theme";
|
||||
userStore.resetTheme();
|
||||
expect(userStore.colorTheme).toBe(defaultThemeId);
|
||||
});
|
||||
});
|
||||
|
||||
describe("migrations", () => {
|
||||
beforeEach(() => {
|
||||
const writeJsonSync = di.inject(writeJsonSyncInjectable);
|
||||
const writeFileSync = di.inject(writeFileSyncInjectable);
|
||||
|
||||
writeJsonSync("/some-directory-for-user-data/lens-user-store.json", {
|
||||
preferences: { colorTheme: "light" },
|
||||
});
|
||||
|
||||
writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", {
|
||||
clusters: [
|
||||
{
|
||||
id: "foobar",
|
||||
kubeConfigPath: "/some-directory-for-user-data/extension_data/foo/bar",
|
||||
},
|
||||
{
|
||||
id: "barfoo",
|
||||
kubeConfigPath: "/some/other/path",
|
||||
},
|
||||
],
|
||||
} as ClusterStoreModel);
|
||||
|
||||
writeJsonSync("/some-directory-for-user-data/extension_data", {});
|
||||
|
||||
writeFileSync("/some/other/path", "is file");
|
||||
|
||||
di.override(storeMigrationVersionInjectable, () => "10.0.0");
|
||||
|
||||
userStore.load();
|
||||
});
|
||||
|
||||
it("skips clusters for adding to kube-sync with files under extension_data/", () => {
|
||||
expect(userStore.syncKubeconfigEntries.has("/some-directory-for-user-data/extension_data/foo/bar")).toBe(false);
|
||||
expect(userStore.syncKubeconfigEntries.has("/some/other/path")).toBe(true);
|
||||
});
|
||||
|
||||
it("allows access to the colorTheme preference", () => {
|
||||
expect(userStore.colorTheme).toBe("light");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { EventEmitter } from "../event-emitter";
|
||||
import type { AppEvent } from "./event-bus";
|
||||
|
||||
const appEventBusInjectable = getInjectable({
|
||||
id: "app-event-bus",
|
||||
instantiate: () => new EventEmitter<[AppEvent]>,
|
||||
decorable: false,
|
||||
});
|
||||
|
||||
export default appEventBusInjectable;
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import appEventBusInjectable from "./app-event-bus.injectable";
|
||||
import type { AppEvent } from "./event-bus";
|
||||
|
||||
export type EmitAppEvent = (event: AppEvent) => void;
|
||||
|
||||
const emitAppEventInjectable = getInjectable({
|
||||
id: "emit-app-event",
|
||||
instantiate: (di): EmitAppEvent => {
|
||||
const bus = di.inject(appEventBusInjectable);
|
||||
|
||||
return (event) => bus.emit(event);
|
||||
},
|
||||
decorable: false,
|
||||
});
|
||||
|
||||
export default emitAppEventInjectable;
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Data for telemetry
|
||||
*/
|
||||
export interface AppEvent {
|
||||
name: string;
|
||||
action: string;
|
||||
destination?: string;
|
||||
params?: Record<string, any>;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { PathName } from "./app-path-names";
|
||||
|
||||
export type AppPaths = Record<PathName, string>;
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { app as electronApp } from "electron";
|
||||
|
||||
export type PathName = Parameters<typeof electronApp["getPath"]>[0] | "currentApp";
|
||||
|
||||
export const pathNames: PathName[] = [
|
||||
"currentApp",
|
||||
"home",
|
||||
"appData",
|
||||
"userData",
|
||||
"cache",
|
||||
"temp",
|
||||
"exe",
|
||||
"module",
|
||||
"desktop",
|
||||
"documents",
|
||||
"downloads",
|
||||
"music",
|
||||
"pictures",
|
||||
"videos",
|
||||
"logs",
|
||||
"crashDumps",
|
||||
"recent",
|
||||
];
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { AppPaths } from "./app-path-injection-token";
|
||||
import type { RequestChannel } from "../utils/channel/request-channel-listener-injection-token";
|
||||
|
||||
export type AppPathsChannel = RequestChannel<void, AppPaths>;
|
||||
|
||||
export const appPathsChannel: AppPathsChannel = {
|
||||
id: "app-paths",
|
||||
};
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { AppPaths } from "./app-path-injection-token";
|
||||
|
||||
const appPathsStateInjectable = getInjectable({
|
||||
id: "app-paths-state",
|
||||
|
||||
instantiate: () => {
|
||||
let state: AppPaths;
|
||||
|
||||
return {
|
||||
get: () =>{
|
||||
if (!state) {
|
||||
throw new Error("Tried to get app paths before state is setupped.");
|
||||
}
|
||||
|
||||
return state;
|
||||
},
|
||||
|
||||
set: (newState: AppPaths) => {
|
||||
if (state) {
|
||||
throw new Error("Tried to overwrite existing state of app paths.");
|
||||
}
|
||||
|
||||
state = newState;
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default appPathsStateInjectable;
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import appPathsStateInjectable from "./app-paths-state.injectable";
|
||||
|
||||
const appPathsInjectable = getInjectable({
|
||||
id: "app-paths",
|
||||
instantiate: (di) => di.inject(appPathsStateInjectable).get(),
|
||||
});
|
||||
|
||||
export default appPathsInjectable;
|
||||
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { AppPaths } from "./app-path-injection-token";
|
||||
import getElectronAppPathInjectable from "../../main/app-paths/get-electron-app-path/get-electron-app-path.injectable";
|
||||
import type { PathName } from "./app-path-names";
|
||||
import setElectronAppPathInjectable from "../../main/app-paths/set-electron-app-path/set-electron-app-path.injectable";
|
||||
import directoryForIntegrationTestingInjectable from "../../main/app-paths/directory-for-integration-testing/directory-for-integration-testing.injectable";
|
||||
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import appPathsInjectable from "./app-paths.injectable";
|
||||
|
||||
describe("app-paths", () => {
|
||||
let builder: ApplicationBuilder;
|
||||
|
||||
beforeEach(() => {
|
||||
builder = getApplicationBuilder();
|
||||
|
||||
const defaultAppPathsStub: AppPaths = {
|
||||
currentApp: "/some-current-app",
|
||||
appData: "/some-app-data",
|
||||
cache: "/some-cache",
|
||||
crashDumps: "/some-crash-dumps",
|
||||
desktop: "/some-desktop",
|
||||
documents: "/some-documents",
|
||||
downloads: "/some-downloads",
|
||||
exe: "/some-exe",
|
||||
home: "/some-home-path",
|
||||
logs: "/some-logs",
|
||||
module: "/some-module",
|
||||
music: "/some-music",
|
||||
pictures: "/some-pictures",
|
||||
recent: "/some-recent",
|
||||
temp: "/some-temp",
|
||||
videos: "/some-videos",
|
||||
userData: "/some-irrelevant-user-data",
|
||||
};
|
||||
|
||||
builder.beforeApplicationStart((mainDi) => {
|
||||
mainDi.override(
|
||||
getElectronAppPathInjectable,
|
||||
() =>
|
||||
(key: PathName): string | null =>
|
||||
defaultAppPathsStub[key],
|
||||
);
|
||||
|
||||
mainDi.override(
|
||||
setElectronAppPathInjectable,
|
||||
() =>
|
||||
(key: PathName, path: string): void => {
|
||||
defaultAppPathsStub[key] = path;
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("normally", () => {
|
||||
let windowDi: DiContainer;
|
||||
let mainDi: DiContainer;
|
||||
|
||||
beforeEach(async () => {
|
||||
await builder.render();
|
||||
|
||||
windowDi = builder.applicationWindow.only.di;
|
||||
mainDi = builder.mainDi;
|
||||
});
|
||||
|
||||
it("given in renderer, when injecting app paths, returns application specific app paths", () => {
|
||||
const actual = windowDi.inject(appPathsInjectable);
|
||||
|
||||
expect(actual).toEqual({
|
||||
currentApp: "/some-current-app",
|
||||
appData: "/some-app-data",
|
||||
cache: "/some-cache",
|
||||
crashDumps: "/some-crash-dumps",
|
||||
desktop: "/some-desktop",
|
||||
documents: "/some-documents",
|
||||
downloads: "/some-downloads",
|
||||
exe: "/some-exe",
|
||||
home: "/some-home-path",
|
||||
logs: "/some-logs",
|
||||
module: "/some-module",
|
||||
music: "/some-music",
|
||||
pictures: "/some-pictures",
|
||||
recent: "/some-recent",
|
||||
temp: "/some-temp",
|
||||
videos: "/some-videos",
|
||||
userData: "/some-app-data/some-product-name",
|
||||
});
|
||||
});
|
||||
|
||||
it("given in main, when injecting app paths, returns application specific app paths", () => {
|
||||
const actual = mainDi.inject(appPathsInjectable);
|
||||
|
||||
expect(actual).toEqual({
|
||||
currentApp: "/some-current-app",
|
||||
appData: "/some-app-data",
|
||||
cache: "/some-cache",
|
||||
crashDumps: "/some-crash-dumps",
|
||||
desktop: "/some-desktop",
|
||||
documents: "/some-documents",
|
||||
downloads: "/some-downloads",
|
||||
exe: "/some-exe",
|
||||
home: "/some-home-path",
|
||||
logs: "/some-logs",
|
||||
module: "/some-module",
|
||||
music: "/some-music",
|
||||
pictures: "/some-pictures",
|
||||
recent: "/some-recent",
|
||||
temp: "/some-temp",
|
||||
videos: "/some-videos",
|
||||
userData: "/some-app-data/some-product-name",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when running integration tests", () => {
|
||||
let windowDi: DiContainer;
|
||||
|
||||
beforeEach(async () => {
|
||||
builder.beforeApplicationStart((mainDi) => {
|
||||
mainDi.override(
|
||||
directoryForIntegrationTestingInjectable,
|
||||
() => "/some-integration-testing-app-data",
|
||||
);
|
||||
});
|
||||
|
||||
await builder.render();
|
||||
|
||||
windowDi = builder.applicationWindow.only.di;
|
||||
});
|
||||
|
||||
it("given in renderer, when injecting path for app data, has integration specific app data path", () => {
|
||||
const { appData, userData } = windowDi.inject(appPathsInjectable);
|
||||
|
||||
expect({ appData, userData }).toEqual({
|
||||
appData: "/some-integration-testing-app-data",
|
||||
userData: "/some-integration-testing-app-data/some-product-name",
|
||||
});
|
||||
});
|
||||
|
||||
it("given in main, when injecting path for app data, has integration specific app data path", () => {
|
||||
const { appData, userData } = windowDi.inject(appPathsInjectable);
|
||||
|
||||
expect({ appData, userData }).toEqual({
|
||||
appData: "/some-integration-testing-app-data",
|
||||
userData: "/some-integration-testing-app-data/some-product-name",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import directoryForUserDataInjectable from "../directory-for-user-data/directory-for-user-data.injectable";
|
||||
import joinPathsInjectable from "../../path/join-paths.injectable";
|
||||
|
||||
const directoryForBinariesInjectable = getInjectable({
|
||||
id: "directory-for-binaries",
|
||||
|
||||
instantiate: (di) => {
|
||||
const joinPaths = di.inject(joinPathsInjectable);
|
||||
const directoryForUserData = di.inject(directoryForUserDataInjectable);
|
||||
|
||||
return joinPaths(directoryForUserData, "binaries");
|
||||
},
|
||||
});
|
||||
|
||||
export default directoryForBinariesInjectable;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import appPathsInjectable from "../app-paths.injectable";
|
||||
|
||||
const directoryForDownloadsInjectable = getInjectable({
|
||||
id: "directory-for-downloads",
|
||||
instantiate: (di) => di.inject(appPathsInjectable).downloads,
|
||||
});
|
||||
|
||||
export default directoryForDownloadsInjectable;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import appPathsInjectable from "../app-paths.injectable";
|
||||
|
||||
const directoryForExesInjectable = getInjectable({
|
||||
id: "directory-for-exes",
|
||||
instantiate: (di) => di.inject(appPathsInjectable).exe,
|
||||
});
|
||||
|
||||
export default directoryForExesInjectable;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import directoryForUserDataInjectable from "../directory-for-user-data/directory-for-user-data.injectable";
|
||||
import joinPathsInjectable from "../../path/join-paths.injectable";
|
||||
|
||||
const directoryForKubeConfigsInjectable = getInjectable({
|
||||
id: "directory-for-kube-configs",
|
||||
|
||||
instantiate: (di) => {
|
||||
const joinPaths = di.inject(joinPathsInjectable);
|
||||
const directoryForUserData = di.inject(directoryForUserDataInjectable);
|
||||
|
||||
return joinPaths(directoryForUserData, "kubeconfigs");
|
||||
},
|
||||
});
|
||||
|
||||
export default directoryForKubeConfigsInjectable;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import directoryForBinariesInjectable from "../directory-for-binaries/directory-for-binaries.injectable";
|
||||
import joinPathsInjectable from "../../path/join-paths.injectable";
|
||||
|
||||
const directoryForKubectlBinariesInjectable = getInjectable({
|
||||
id: "directory-for-kubectl-binaries",
|
||||
|
||||
instantiate: (di) => {
|
||||
const joinPaths = di.inject(joinPathsInjectable);
|
||||
const directoryForBinaries = di.inject(directoryForBinariesInjectable);
|
||||
|
||||
return joinPaths(directoryForBinaries, "kubectl");
|
||||
},
|
||||
});
|
||||
|
||||
export default directoryForKubectlBinariesInjectable;
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import appPathsInjectable from "./app-paths.injectable";
|
||||
|
||||
const directoryForLogsInjectable = getInjectable({
|
||||
id: "directory-for-logs",
|
||||
instantiate: (di) => di.inject(appPathsInjectable).logs,
|
||||
});
|
||||
|
||||
export default directoryForLogsInjectable;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import appPathsInjectable from "../app-paths.injectable";
|
||||
|
||||
const directoryForTempInjectable = getInjectable({
|
||||
id: "directory-for-temp",
|
||||
instantiate: (di) => di.inject(appPathsInjectable).temp,
|
||||
});
|
||||
|
||||
export default directoryForTempInjectable;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import appPathsInjectable from "../app-paths.injectable";
|
||||
|
||||
const directoryForUserDataInjectable = getInjectable({
|
||||
id: "directory-for-user-data",
|
||||
instantiate: (di) => di.inject(appPathsInjectable).userData,
|
||||
});
|
||||
|
||||
export default directoryForUserDataInjectable;
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import directoryForKubeConfigsInjectable from "../directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import joinPathsInjectable from "../../path/join-paths.injectable";
|
||||
|
||||
export type GetCustomKubeConfigFilePath = (fileName: string) => string;
|
||||
|
||||
const getCustomKubeConfigFilePathInjectable = getInjectable({
|
||||
id: "get-custom-kube-config-directory",
|
||||
|
||||
instantiate: (di): GetCustomKubeConfigFilePath => {
|
||||
const directoryForKubeConfigs = di.inject(directoryForKubeConfigsInjectable);
|
||||
const joinPaths = di.inject(joinPathsInjectable);
|
||||
|
||||
return (fileName) => joinPaths(directoryForKubeConfigs, fileName);
|
||||
},
|
||||
});
|
||||
|
||||
export default getCustomKubeConfigFilePathInjectable;
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getGlobalOverride } from "../test-utils/get-global-override";
|
||||
import pathToNpmCliInjectable from "./path-to-npm-cli.injectable";
|
||||
|
||||
export default getGlobalOverride(pathToNpmCliInjectable, () => "/some/npm/cli/path");
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
const pathToNpmCliInjectable = getInjectable({
|
||||
id: "path-to-npm-cli",
|
||||
instantiate: () => __non_webpack_require__.resolve("npm"),
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default pathToNpmCliInjectable;
|
||||
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type Config from "conf";
|
||||
import type { Migrations, Options as ConfOptions } from "conf/dist/source/types";
|
||||
import type { IEqualsComparer } from "mobx";
|
||||
import { makeObservable, reaction } from "mobx";
|
||||
import { disposer, isPromiseLike, toJS } from "../utils";
|
||||
import { broadcastMessage } from "../ipc";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import { kebabCase } from "lodash";
|
||||
import type { GetConfigurationFileModel } from "../get-configuration-file-model/get-configuration-file-model.injectable";
|
||||
import type { Logger } from "../logger";
|
||||
import type { PersistStateToConfig } from "./save-to-file";
|
||||
import type { GetBasenameOfPath } from "../path/get-basename.injectable";
|
||||
import type { EnlistMessageChannelListener } from "../utils/channel/enlist-message-channel-listener-injection-token";
|
||||
|
||||
export interface BaseStoreParams<T> extends Omit<ConfOptions<T>, "migrations"> {
|
||||
syncOptions?: {
|
||||
fireImmediately?: boolean;
|
||||
equals?: IEqualsComparer<T>;
|
||||
};
|
||||
configName: string;
|
||||
}
|
||||
|
||||
export interface IpcChannelPrefixes {
|
||||
local: string;
|
||||
remote: string;
|
||||
}
|
||||
|
||||
export interface BaseStoreDependencies {
|
||||
readonly logger: Logger;
|
||||
readonly storeMigrationVersion: string;
|
||||
readonly directoryForUserData: string;
|
||||
readonly migrations: Migrations<Record<string, unknown>>;
|
||||
readonly ipcChannelPrefixes: IpcChannelPrefixes;
|
||||
readonly shouldDisableSyncInListener: boolean;
|
||||
getConfigurationFileModel: GetConfigurationFileModel;
|
||||
persistStateToConfig: PersistStateToConfig;
|
||||
getBasenameOfPath: GetBasenameOfPath;
|
||||
enlistMessageChannelListener: EnlistMessageChannelListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: T should only contain base JSON serializable types.
|
||||
*/
|
||||
export abstract class BaseStore<T extends object> {
|
||||
private readonly syncDisposers = disposer();
|
||||
|
||||
readonly displayName = kebabCase(this.params.configName).toUpperCase();
|
||||
|
||||
protected constructor(
|
||||
protected readonly dependencies: BaseStoreDependencies,
|
||||
protected readonly params: BaseStoreParams<T>,
|
||||
) {
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* This must be called after the last child's constructor is finished (or just before it finishes)
|
||||
*/
|
||||
load() {
|
||||
this.dependencies.logger.info(`[${this.displayName}]: LOADING ...`);
|
||||
|
||||
const config = this.dependencies.getConfigurationFileModel({
|
||||
projectName: "lens",
|
||||
projectVersion: this.dependencies.storeMigrationVersion,
|
||||
cwd: this.cwd(),
|
||||
...this.params,
|
||||
migrations: this.dependencies.migrations as Migrations<T>,
|
||||
});
|
||||
|
||||
const res = this.fromStore(config.store);
|
||||
|
||||
if (isPromiseLike(res)) {
|
||||
this.dependencies.logger.error(`${this.displayName} extends BaseStore<T>'s fromStore method returns a Promise or promise-like object. This is an error and must be fixed.`);
|
||||
}
|
||||
|
||||
this.startSyncing(config);
|
||||
this.dependencies.logger.info(`[${this.displayName}]: LOADED from ${config.path}`);
|
||||
}
|
||||
|
||||
protected cwd() {
|
||||
return this.dependencies.directoryForUserData;
|
||||
}
|
||||
|
||||
private startSyncing(config: Config<T>) {
|
||||
const name = this.dependencies.getBasenameOfPath(config.path);
|
||||
|
||||
const disableSync = () => this.syncDisposers();
|
||||
const enableSync = () => {
|
||||
this.syncDisposers.push(
|
||||
reaction(
|
||||
() => toJS(this.toJSON()), // unwrap possible observables and react to everything
|
||||
model => {
|
||||
this.dependencies.persistStateToConfig(config, model);
|
||||
broadcastMessage(`${this.dependencies.ipcChannelPrefixes.remote}:${config.path}`, model);
|
||||
},
|
||||
this.params.syncOptions,
|
||||
),
|
||||
this.dependencies.enlistMessageChannelListener({
|
||||
channel: {
|
||||
id: `${this.dependencies.ipcChannelPrefixes.local}:${config.path}`,
|
||||
},
|
||||
handler: (model) => {
|
||||
this.dependencies.logger.silly(`[${this.displayName}]: syncing ${name}`, { model });
|
||||
|
||||
if (this.dependencies.shouldDisableSyncInListener) {
|
||||
disableSync();
|
||||
}
|
||||
|
||||
// todo: use "resourceVersion" if merge required (to avoid equality checks => better performance)
|
||||
if (!isEqual(this.toJSON(), model)) {
|
||||
this.fromStore(model as T);
|
||||
}
|
||||
|
||||
if (this.dependencies.shouldDisableSyncInListener) {
|
||||
enableSync();
|
||||
}
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
enableSync();
|
||||
}
|
||||
|
||||
/**
|
||||
* fromStore is called internally when a child class syncs with the file
|
||||
* system.
|
||||
*
|
||||
* Note: This function **must** be synchronous.
|
||||
*
|
||||
* @param data the parsed information read from the stored JSON file
|
||||
*/
|
||||
protected abstract fromStore(data: T): void;
|
||||
|
||||
/**
|
||||
* toJSON is called when syncing the store to the filesystem. It should
|
||||
* produce a JSON serializable object representation of the current state.
|
||||
*
|
||||
* It is recommended that a round trip is valid. Namely, calling
|
||||
* `this.fromStore(this.toJSON())` shouldn't change the state.
|
||||
*/
|
||||
abstract toJSON(): T;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { IpcChannelPrefixes } from "./base-store";
|
||||
|
||||
export const baseStoreIpcChannelPrefixesInjectionToken = getInjectionToken<IpcChannelPrefixes>({
|
||||
id: "base-store-ipc-channel-prefix-token",
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
|
||||
export const shouldBaseStoreDisableSyncInIpcListenerInjectionToken = getInjectionToken<boolean>({
|
||||
id: "should-base-store-disable-sync-in-ipc-listener-token",
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { InjectionToken } from "@ogre-tools/injectable";
|
||||
import { lifecycleEnum, getInjectable } from "@ogre-tools/injectable";
|
||||
import type Conf from "conf/dist/source";
|
||||
import type { Migrations } from "conf/dist/source/types";
|
||||
import loggerInjectable from "../logger.injectable";
|
||||
import { getOrInsert, iter } from "../utils";
|
||||
|
||||
export interface MigrationDeclaration {
|
||||
version: string;
|
||||
run(store: Conf<Partial<Record<string, unknown>>>): void;
|
||||
}
|
||||
|
||||
const storeMigrationsInjectable = getInjectable({
|
||||
id: "store-migrations",
|
||||
instantiate: (di, token): Migrations<Record<string, unknown>> => {
|
||||
const logger = di.inject(loggerInjectable);
|
||||
const declarations = di.injectMany(token);
|
||||
const migrations = new Map<string, MigrationDeclaration["run"][]>();
|
||||
|
||||
for (const decl of declarations) {
|
||||
getOrInsert(migrations, decl.version, []).push(decl.run);
|
||||
}
|
||||
|
||||
return Object.fromEntries(
|
||||
iter.map(
|
||||
migrations,
|
||||
([v, fns]) => [v, (store) => {
|
||||
logger.info(`Running ${v} migration for ${store.path}`);
|
||||
|
||||
for (const fn of fns) {
|
||||
fn(store);
|
||||
}
|
||||
}],
|
||||
),
|
||||
);
|
||||
},
|
||||
lifecycle: lifecycleEnum.keyedSingleton({
|
||||
getInstanceKey: (di, token: InjectionToken<MigrationDeclaration, void>) => token.id,
|
||||
}),
|
||||
});
|
||||
|
||||
export default storeMigrationsInjectable;
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type Config from "conf";
|
||||
|
||||
export type PersistStateToConfig = <T extends object>(config: Config<T>, state: T) => void;
|
||||
|
||||
export const persistStateToConfigInjectionToken = getInjectionToken<PersistStateToConfig>({
|
||||
id: "persist-state-to-config-token",
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
|
||||
import kubernetesClusterCategoryInjectable from "../../catalog/categories/kubernetes-cluster.injectable";
|
||||
import type { KubernetesClusterCategory } from "../kubernetes-cluster";
|
||||
|
||||
|
||||
describe("kubernetesClusterCategory", () => {
|
||||
let kubernetesClusterCategory: KubernetesClusterCategory;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
kubernetesClusterCategory = di.inject(kubernetesClusterCategoryInjectable);
|
||||
});
|
||||
|
||||
describe("filteredItems", () => {
|
||||
const item1 = {
|
||||
icon: "Icon",
|
||||
title: "Title",
|
||||
onClick: () => {},
|
||||
};
|
||||
const item2 = {
|
||||
icon: "Icon 2",
|
||||
title: "Title 2",
|
||||
onClick: () => {},
|
||||
};
|
||||
|
||||
it("returns all items if no filter set", () => {
|
||||
expect(kubernetesClusterCategory.filteredItems([item1, item2])).toEqual([item1, item2]);
|
||||
});
|
||||
|
||||
it("returns filtered items", () => {
|
||||
expect(kubernetesClusterCategory.filteredItems([item1, item2])).toEqual([item1, item2]);
|
||||
|
||||
const disposer1 = kubernetesClusterCategory.addMenuFilter(item => item.icon === "Icon");
|
||||
|
||||
expect(kubernetesClusterCategory.filteredItems([item1, item2])).toEqual([item1]);
|
||||
|
||||
const disposer2 = kubernetesClusterCategory.addMenuFilter(item => item.title === "Title 2");
|
||||
|
||||
expect(kubernetesClusterCategory.filteredItems([item1, item2])).toEqual([]);
|
||||
|
||||
disposer1();
|
||||
|
||||
expect(kubernetesClusterCategory.filteredItems([item1, item2])).toEqual([item2]);
|
||||
|
||||
disposer2();
|
||||
|
||||
expect(kubernetesClusterCategory.filteredItems([item1, item2])).toEqual([item1, item2]);
|
||||
});
|
||||
});
|
||||
});
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { GeneralEntity } from "../index";
|
||||
|
||||
export const generalCatalogEntityInjectionToken = getInjectionToken<GeneralEntity>({
|
||||
id: "general-catalog-entity-injection-token",
|
||||
});
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { generalCatalogEntityInjectionToken } from "../general-catalog-entity-injection-token";
|
||||
import { GeneralEntity } from "../../index";
|
||||
import { buildURL } from "../../../utils/buildUrl";
|
||||
import catalogRouteInjectable from "../../../front-end-routing/routes/catalog/catalog-route.injectable";
|
||||
|
||||
const catalogCatalogEntityInjectable = getInjectable({
|
||||
id: "general-catalog-entity-for-catalog",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(catalogRouteInjectable);
|
||||
const url = buildURL(route.path);
|
||||
|
||||
return new GeneralEntity({
|
||||
metadata: {
|
||||
uid: "catalog-entity",
|
||||
name: "Catalog",
|
||||
source: "app",
|
||||
labels: {},
|
||||
},
|
||||
spec: {
|
||||
path: url,
|
||||
icon: {
|
||||
material: "view_list",
|
||||
background: "#3d90ce",
|
||||
},
|
||||
},
|
||||
status: {
|
||||
phase: "active",
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
injectionToken: generalCatalogEntityInjectionToken,
|
||||
});
|
||||
|
||||
export default catalogCatalogEntityInjectable;
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { generalCatalogEntityInjectionToken } from "../general-catalog-entity-injection-token";
|
||||
import { GeneralEntity } from "../../index";
|
||||
import { buildURL } from "../../../utils/buildUrl";
|
||||
import welcomeRouteInjectable from "../../../front-end-routing/routes/welcome/welcome-route.injectable";
|
||||
|
||||
const welcomeCatalogEntityInjectable = getInjectable({
|
||||
id: "general-catalog-entity-for-welcome",
|
||||
|
||||
instantiate: (di) => {
|
||||
const route = di.inject(welcomeRouteInjectable);
|
||||
const url = buildURL(route.path);
|
||||
|
||||
return new GeneralEntity({
|
||||
metadata: {
|
||||
uid: "welcome-page-entity",
|
||||
name: "Welcome Page",
|
||||
source: "app",
|
||||
labels: {},
|
||||
},
|
||||
spec: {
|
||||
path: url,
|
||||
icon: {
|
||||
material: "meeting_room",
|
||||
background: "#3d90ce",
|
||||
},
|
||||
},
|
||||
status: {
|
||||
phase: "active",
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
injectionToken: generalCatalogEntityInjectionToken,
|
||||
});
|
||||
|
||||
export default welcomeCatalogEntityInjectable;
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { CatalogEntityMetadata, CatalogEntitySpec, CatalogEntityStatus } from "../catalog";
|
||||
import type { CatalogEntityActionContext } from "../catalog/catalog-entity";
|
||||
import { CatalogCategory, CatalogEntity, categoryVersion } from "../catalog/catalog-entity";
|
||||
|
||||
interface GeneralEntitySpec extends CatalogEntitySpec {
|
||||
path: string;
|
||||
icon?: {
|
||||
material?: string;
|
||||
background?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export class GeneralEntity extends CatalogEntity<CatalogEntityMetadata, CatalogEntityStatus, GeneralEntitySpec> {
|
||||
public readonly apiVersion = "entity.k8slens.dev/v1alpha1";
|
||||
public readonly kind = "General";
|
||||
|
||||
async onRun(context: CatalogEntityActionContext) {
|
||||
context.navigate(this.spec.path);
|
||||
}
|
||||
}
|
||||
|
||||
export class GeneralCategory extends CatalogCategory {
|
||||
public readonly apiVersion = "catalog.k8slens.dev/v1alpha1";
|
||||
public readonly kind = "CatalogCategory";
|
||||
public metadata = {
|
||||
name: "General",
|
||||
icon: "settings",
|
||||
};
|
||||
public spec = {
|
||||
group: "entity.k8slens.dev",
|
||||
versions: [
|
||||
categoryVersion("v1alpha1", GeneralEntity),
|
||||
],
|
||||
names: {
|
||||
kind: "General",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 256 249" style="enable-background:new 0 0 256 249;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M247.7,151.3L247.7,151.3L247.7,151.3C247.4,151.3,247.4,151.3,247.7,151.3h-0.2c-0.2,0-0.5,0-0.5-0.2
|
||||
c-0.5,0-1-0.2-1.4-0.2c-1.7-0.2-3.1-0.5-4.5-0.5c-0.7,0-1.4,0-2.4-0.2h-0.2c-5-0.5-9-1-12.8-2.1c-1.7-0.7-2.1-1.7-2.6-2.6
|
||||
c0-0.2-0.2-0.2-0.2-0.5l0,0l-3.1-1c1.4-10.9,1-22.3-1.7-33.5c-2.6-11.2-7.1-21.6-13.3-31.1l2.4-2.1v-0.5c0-1.2,0.2-2.4,1.2-3.6
|
||||
c2.9-2.6,6.4-4.8,10.7-7.4l0,0c0.7-0.5,1.4-0.7,2.1-1.2c1.4-0.7,2.6-1.4,4-2.4c0.2-0.2,0.7-0.5,1.2-1c0.2-0.2,0.5-0.2,0.5-0.5l0,0
|
||||
c3.3-2.9,4-7.6,1.7-10.7c-1.2-1.7-3.3-2.6-5.5-2.6c-1.9,0-3.6,0.7-5.2,1.9l0,0l0,0c-0.2,0.2-0.2,0.2-0.5,0.5c-0.5,0.2-0.7,0.7-1.2,1
|
||||
c-1.2,1.2-2.1,2.1-3.1,3.3c-0.5,0.5-1,1.2-1.7,1.7l0,0c-3.3,3.6-6.4,6.4-9.5,8.6c-0.7,0.5-1.4,0.7-2.1,0.7c-0.5,0-1,0-1.4-0.2h-0.5
|
||||
l0,0l-2.9,1.9c-3.1-3.3-6.4-6.2-9.7-9c-14.3-11.2-31.6-18.1-49.6-19.7l-0.2-3.1c-0.2-0.2-0.2-0.2-0.5-0.5c-0.7-0.7-1.7-1.4-1.9-3.1
|
||||
c-0.2-3.8,0.2-8.1,0.7-12.8v-0.2c0-0.7,0.2-1.7,0.5-2.4c0.2-1.4,0.5-2.9,0.7-4.5V10V9.3l0,0l0,0c0-4.3-3.3-7.8-7.4-7.8
|
||||
c-1.9,0-3.8,1-5.2,2.4c-1.4,1.4-2.1,3.3-2.1,5.5l0,0l0,0v0.5v1.4c0,1.7,0.2,3.1,0.7,4.5c0.2,0.7,0.2,1.4,0.5,2.4v0.2
|
||||
c0.5,4.8,1.2,9,0.7,12.8c-0.2,1.7-1.2,2.4-1.9,3.1c-0.2,0.2-0.2,0.2-0.5,0.5l0,0l-0.2,3.1c-4.3,0.5-8.6,1-12.8,1.9
|
||||
c-18.3,4-34.4,13.3-47,26.6l-2.4-1.7h-0.5c-0.5,0-1,0.2-1.4,0.2c-0.7,0-1.4-0.2-2.1-0.7c-3.1-2.1-6.2-5.2-9.5-8.8l0,0
|
||||
c-0.5-0.5-1-1.2-1.7-1.7c-1-1.2-1.9-2.1-3.1-3.3c-0.2-0.2-0.7-0.5-1.2-1c-0.2-0.2-0.5-0.2-0.5-0.5l0,0c-1.4-1.2-3.3-1.9-5.2-1.9
|
||||
c-2.1,0-4.3,1-5.5,2.6c-2.4,3.1-1.7,7.8,1.7,10.7l0,0l0,0c0.2,0,0.2,0.2,0.5,0.2c0.5,0.2,0.7,0.7,1.2,1c1.4,1,2.6,1.7,4,2.4
|
||||
c0.7,0.2,1.4,0.7,2.1,1.2l0,0c4.3,2.6,7.8,4.8,10.7,7.4c1.2,1.2,1.2,2.4,1.2,3.6v0.5l0,0l2.4,2.1c-0.5,0.7-1,1.2-1.2,1.9
|
||||
c-11.9,18.8-16.4,40.9-13.3,62.7l-3.1,1c0,0.2-0.2,0.2-0.2,0.5c-0.5,1-1.2,1.9-2.6,2.6c-3.6,1.2-7.8,1.7-12.8,2.1h-0.2
|
||||
c-0.7,0-1.7,0-2.4,0.2c-1.4,0-2.9,0.2-4.5,0.5c-0.5,0-1,0.2-1.4,0.2c-0.2,0-0.5,0-0.7,0.2l0,0l0,0c-4.3,1-6.9,5-6.2,8.8
|
||||
c0.7,3.3,3.8,5.5,7.6,5.5c0.7,0,1.2,0,1.9-0.2l0,0l0,0c0.2,0,0.5,0,0.5-0.2c0.5,0,1-0.2,1.4-0.2c1.7-0.5,2.9-1,4.3-1.7
|
||||
c0.7-0.2,1.4-0.7,2.1-1h0.2c4.5-1.7,8.6-3.1,12.4-3.6h0.5c1.4,0,2.4,0.7,3.1,1.2c0.2,0,0.2,0.2,0.5,0.2l0,0l3.3-0.5
|
||||
c5.7,17.6,16.6,33.3,31.1,44.7c3.3,2.6,6.7,4.8,10.2,6.9l-1.4,3.1c0,0.2,0.2,0.2,0.2,0.5c0.5,1,1,2.1,0.5,3.8
|
||||
c-1.4,3.6-3.6,7.1-6.2,11.2v0.2c-0.5,0.7-1,1.2-1.4,1.9c-1,1.2-1.7,2.4-2.6,3.8c-0.2,0.2-0.5,0.7-0.7,1.2c0,0.2-0.2,0.5-0.2,0.5l0,0
|
||||
l0,0c-1.9,4-0.5,8.6,3.1,10.2c1,0.5,1.9,0.7,2.9,0.7c2.9,0,5.7-1.9,7.1-4.5l0,0l0,0c0-0.2,0.2-0.5,0.2-0.5c0.2-0.5,0.5-1,0.7-1.2
|
||||
c0.7-1.7,1-2.9,1.4-4.3c0.2-0.7,0.5-1.4,0.7-2.1l0,0c1.7-4.8,2.9-8.6,5-11.9c1-1.4,2.1-1.7,3.1-2.1c0.2,0,0.2,0,0.5-0.2l0,0l1.7-3.1
|
||||
c10.5,4,21.9,6.2,33.3,6.2c6.9,0,14-0.7,20.7-2.4c4.3-1,8.3-2.1,12.4-3.6l1.4,2.6c0.2,0,0.2,0,0.5,0.2c1.2,0.2,2.1,0.7,3.1,2.1
|
||||
c1.9,3.3,3.3,7.4,5,11.9v0.2c0.2,0.7,0.5,1.4,0.7,2.1c0.5,1.4,0.7,2.9,1.4,4.3c0.2,0.5,0.5,0.7,0.7,1.2c0,0.2,0.2,0.5,0.2,0.5l0,0
|
||||
l0,0c1.4,2.9,4.3,4.5,7.1,4.5c1,0,1.9-0.2,2.9-0.7c1.7-1,3.1-2.4,3.6-4.3s0.5-4-0.5-5.9l0,0l0,0c0-0.2-0.2-0.2-0.2-0.5
|
||||
c-0.2-0.5-0.5-1-0.7-1.2c-0.7-1.4-1.7-2.6-2.6-3.8c-0.5-0.7-1-1.2-1.4-1.9V229c-2.6-4-5-7.6-6.2-11.2c-0.5-1.7,0-2.6,0.2-3.8
|
||||
c0-0.2,0.2-0.2,0.2-0.5l0,0l-1.2-2.9c12.6-7.4,23.3-17.8,31.4-30.6c4.3-6.7,7.6-14,10-21.4l2.9,0.5c0.2,0,0.2-0.2,0.5-0.2
|
||||
c1-0.5,1.7-1.2,3.1-1.2h0.5c3.8,0.5,7.8,1.9,12.4,3.6h0.2c0.7,0.2,1.4,0.7,2.1,1c1.4,0.7,2.6,1.2,4.3,1.7c0.5,0,1,0.2,1.4,0.2
|
||||
c0.2,0,0.5,0,0.7,0.2l0,0c0.7,0.2,1.2,0.2,1.9,0.2c3.6,0,6.7-2.4,7.6-5.5C254.6,156.3,251.9,152.5,247.7,151.3L247.7,151.3z
|
||||
M137.7,139.7l-10.5,5l-10.5-5l-2.6-11.2l7.1-9h11.6l7.1,9L137.7,139.7L137.7,139.7z M199.7,115c1.9,8.1,2.4,16.2,1.7,24L165,128.5
|
||||
c-3.3-1-5.2-4.3-4.5-7.6c0.2-1,0.7-1.9,1.4-2.6l28.7-25.9C194.7,99.1,197.8,106.7,199.7,115L199.7,115z M179.3,78.2l-31.1,22.1
|
||||
c-2.6,1.7-6.2,1.2-8.3-1.4c-0.7-0.7-1-1.7-1.2-2.6l-2.1-38.7C152.9,59.4,167.8,66.8,179.3,78.2L179.3,78.2z M110.4,58.7
|
||||
c2.6-0.5,5-1,7.6-1.4l-2.1,38c-0.2,3.3-2.9,6.2-6.4,6.2c-1,0-2.1-0.2-2.9-0.7L75,78.2C84.7,68.4,96.8,61.8,110.4,58.7L110.4,58.7z
|
||||
M63.6,92.4l28.3,25.2c2.6,2.1,2.9,6.2,0.7,8.8c-0.7,1-1.7,1.7-2.9,1.9L52.9,139C51.4,122.8,55,106.4,63.6,92.4L63.6,92.4z
|
||||
M57.1,156.8l37.8-6.4c3.1-0.2,5.9,1.9,6.7,5c0.2,1.4,0.2,2.6-0.2,3.8l0,0l-14.5,34.9C73.5,185.6,62.8,172.5,57.1,156.8L57.1,156.8z
|
||||
M143.9,204.1c-5.5,1.2-10.9,1.9-16.6,1.9c-8.3,0-16.4-1.4-24-3.8l18.8-34c1.9-2.1,5-3.1,7.6-1.7c1.2,0.7,2.1,1.7,2.9,2.6l0,0
|
||||
l18.3,33C148.6,202.9,146.2,203.4,143.9,204.1L143.9,204.1z M190.2,171.1c-5.9,9.5-13.8,17.1-22.8,23l-15-35.9
|
||||
c-0.7-2.9,0.5-5.9,3.3-7.4c1-0.5,2.1-0.7,3.3-0.7l38,6.4C195.6,161.8,193.3,166.5,190.2,171.1L190.2,171.1z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./general";
|
||||
export * from "./kubernetes-cluster";
|
||||
export * from "./web-link";
|
||||
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { CatalogEntityActionContext, CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus, CatalogCategorySpec } from "../catalog";
|
||||
import { CatalogEntity, CatalogCategory, categoryVersion } from "../catalog/catalog-entity";
|
||||
import { broadcastMessage } from "../ipc";
|
||||
import { app } from "electron";
|
||||
import type { CatalogEntityConstructor, CatalogEntitySpec } from "../catalog/catalog-entity";
|
||||
import { IpcRendererNavigationEvents } from "../../renderer/navigation/events";
|
||||
import { requestClusterActivation, requestClusterDisconnection } from "../../renderer/ipc";
|
||||
import KubeClusterCategoryIcon from "./icons/kubernetes.svg";
|
||||
import getClusterByIdInjectable from "../cluster-store/get-by-id.injectable";
|
||||
import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||
|
||||
export interface KubernetesClusterPrometheusMetrics {
|
||||
address?: {
|
||||
namespace: string;
|
||||
service: string;
|
||||
port: number;
|
||||
prefix: string;
|
||||
};
|
||||
type?: string;
|
||||
}
|
||||
|
||||
export interface KubernetesClusterSpec extends CatalogEntitySpec {
|
||||
kubeconfigPath: string;
|
||||
kubeconfigContext: string;
|
||||
metrics?: {
|
||||
source: string;
|
||||
prometheus?: KubernetesClusterPrometheusMetrics;
|
||||
};
|
||||
icon?: {
|
||||
// TODO: move to CatalogEntitySpec once any-entity icons are supported
|
||||
src?: string;
|
||||
material?: string;
|
||||
background?: string;
|
||||
};
|
||||
accessibleNamespaces?: string[];
|
||||
}
|
||||
|
||||
export enum LensKubernetesClusterStatus {
|
||||
DELETING = "deleting",
|
||||
CONNECTING = "connecting",
|
||||
CONNECTED = "connected",
|
||||
DISCONNECTED = "disconnected",
|
||||
}
|
||||
|
||||
export interface KubernetesClusterMetadata extends CatalogEntityMetadata {
|
||||
distro?: string;
|
||||
kubeVersion?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This is no longer used as it is incorrect. Other sources can add more values
|
||||
*/
|
||||
export type KubernetesClusterStatusPhase = "connected" | "connecting" | "disconnected" | "deleting";
|
||||
|
||||
export interface KubernetesClusterStatus extends CatalogEntityStatus {
|
||||
}
|
||||
|
||||
export function isKubernetesCluster(item: unknown): item is KubernetesCluster {
|
||||
return item instanceof KubernetesCluster;
|
||||
}
|
||||
|
||||
export class KubernetesCluster<
|
||||
Metadata extends KubernetesClusterMetadata = KubernetesClusterMetadata,
|
||||
Status extends KubernetesClusterStatus = KubernetesClusterStatus,
|
||||
Spec extends KubernetesClusterSpec = KubernetesClusterSpec,
|
||||
> extends CatalogEntity<Metadata, Status, Spec> {
|
||||
public static readonly apiVersion: string = "entity.k8slens.dev/v1alpha1";
|
||||
public static readonly kind: string = "KubernetesCluster";
|
||||
|
||||
public readonly apiVersion = KubernetesCluster.apiVersion;
|
||||
public readonly kind = KubernetesCluster.kind;
|
||||
|
||||
async connect(): Promise<void> {
|
||||
if (app) {
|
||||
const di = getLegacyGlobalDiForExtensionApi();
|
||||
const getClusterById = di.inject(getClusterByIdInjectable);
|
||||
|
||||
await getClusterById(this.getId())?.activate();
|
||||
} else {
|
||||
await requestClusterActivation(this.getId(), false);
|
||||
}
|
||||
}
|
||||
|
||||
async disconnect(): Promise<void> {
|
||||
if (app) {
|
||||
const di = getLegacyGlobalDiForExtensionApi();
|
||||
const getClusterById = di.inject(getClusterByIdInjectable);
|
||||
|
||||
getClusterById(this.getId())?.disconnect();
|
||||
} else {
|
||||
await requestClusterDisconnection(this.getId(), false);
|
||||
}
|
||||
}
|
||||
|
||||
async onRun(context: CatalogEntityActionContext) {
|
||||
context.navigate(`/cluster/${this.getId()}`);
|
||||
}
|
||||
|
||||
onDetailsOpen(): void {
|
||||
//
|
||||
}
|
||||
|
||||
onSettingsOpen(): void {
|
||||
//
|
||||
}
|
||||
|
||||
onContextMenuOpen(context: CatalogEntityContextMenuContext) {
|
||||
if (!this.metadata.source || this.metadata.source === "local") {
|
||||
context.menuItems.push({
|
||||
title: "Settings",
|
||||
icon: "settings",
|
||||
onClick: () => broadcastMessage(
|
||||
IpcRendererNavigationEvents.NAVIGATE_IN_APP,
|
||||
`/entity/${this.getId()}/settings`,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
switch (this.status.phase) {
|
||||
case LensKubernetesClusterStatus.CONNECTED:
|
||||
case LensKubernetesClusterStatus.CONNECTING:
|
||||
context.menuItems.push({
|
||||
title: "Disconnect",
|
||||
icon: "link_off",
|
||||
onClick: () => requestClusterDisconnection(this.getId()),
|
||||
});
|
||||
break;
|
||||
case LensKubernetesClusterStatus.DISCONNECTED:
|
||||
context.menuItems.push({
|
||||
title: "Connect",
|
||||
icon: "link",
|
||||
onClick: () => context.navigate(`/cluster/${this.getId()}`),
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class KubernetesClusterCategory extends CatalogCategory {
|
||||
public readonly apiVersion = "catalog.k8slens.dev/v1alpha1";
|
||||
public readonly kind = "CatalogCategory";
|
||||
public metadata = {
|
||||
name: "Clusters",
|
||||
icon: KubeClusterCategoryIcon,
|
||||
};
|
||||
public spec: CatalogCategorySpec = {
|
||||
group: "entity.k8slens.dev",
|
||||
versions: [
|
||||
categoryVersion("v1alpha1", KubernetesCluster as CatalogEntityConstructor<KubernetesCluster>),
|
||||
],
|
||||
names: {
|
||||
kind: "KubernetesCluster",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { Environments, getEnvironmentSpecificLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||
import type { CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog";
|
||||
import { CatalogCategory, CatalogEntity, categoryVersion } from "../catalog/catalog-entity";
|
||||
import productNameInjectable from "../vars/product-name.injectable";
|
||||
import weblinkStoreInjectable from "../weblinks-store/weblink-store.injectable";
|
||||
|
||||
export type WebLinkStatusPhase = "available" | "unavailable";
|
||||
|
||||
export interface WebLinkStatus extends CatalogEntityStatus {
|
||||
phase: WebLinkStatusPhase;
|
||||
}
|
||||
|
||||
export interface WebLinkSpec {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export class WebLink extends CatalogEntity<CatalogEntityMetadata, WebLinkStatus, WebLinkSpec> {
|
||||
public static readonly apiVersion = "entity.k8slens.dev/v1alpha1";
|
||||
public static readonly kind = "WebLink";
|
||||
|
||||
public readonly apiVersion = WebLink.apiVersion;
|
||||
public readonly kind = WebLink.kind;
|
||||
|
||||
async onRun() {
|
||||
window.open(this.spec.url, "_blank");
|
||||
}
|
||||
|
||||
onContextMenuOpen(context: CatalogEntityContextMenuContext) {
|
||||
// NOTE: this is safe because `onContextMenuOpen` is only supposed to be called in the renderer
|
||||
const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi(Environments.renderer);
|
||||
const productName = di.inject(productNameInjectable);
|
||||
const weblinkStore = di.inject(weblinkStoreInjectable);
|
||||
|
||||
if (this.metadata.source === "local") {
|
||||
context.menuItems.push({
|
||||
title: "Delete",
|
||||
icon: "delete",
|
||||
onClick: async () => weblinkStore.removeById(this.getId()),
|
||||
confirm: {
|
||||
message: `Remove Web Link "${this.getName()}" from ${productName}?`,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class WebLinkCategory extends CatalogCategory {
|
||||
public readonly apiVersion = "catalog.k8slens.dev/v1alpha1";
|
||||
public readonly kind = "CatalogCategory";
|
||||
public metadata = {
|
||||
name: "Web Links",
|
||||
icon: "public",
|
||||
};
|
||||
public spec = {
|
||||
group: "entity.k8slens.dev",
|
||||
versions: [
|
||||
categoryVersion("v1alpha1", WebLink),
|
||||
],
|
||||
names: {
|
||||
kind: "WebLink",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import EventEmitter from "events";
|
||||
import type TypedEmitter from "typed-emitter";
|
||||
import { observable, makeObservable } from "mobx";
|
||||
import { once } from "lodash";
|
||||
import type { Disposer } from "../utils";
|
||||
import { iter } from "../utils";
|
||||
import type { CategoryColumnRegistration } from "../../renderer/components/+catalog/custom-category-columns";
|
||||
|
||||
export type CatalogEntityDataFor<Entity> = Entity extends CatalogEntity<infer Metadata, infer Status, infer Spec>
|
||||
? CatalogEntityData<Metadata, Status, Spec>
|
||||
: never;
|
||||
|
||||
export type CatalogEntityInstanceFrom<Constructor> = Constructor extends CatalogEntityConstructor<infer Entity>
|
||||
? Entity
|
||||
: never;
|
||||
|
||||
export type CatalogEntityConstructor<Entity extends CatalogEntity> = (
|
||||
new (data: CatalogEntityDataFor<Entity>) => Entity
|
||||
);
|
||||
|
||||
export interface CatalogCategoryVersion {
|
||||
/**
|
||||
* The specific version that the associated constructor is for. This MUST be
|
||||
* a DNS label and SHOULD be of the form `vN`, `vNalphaY`, or `vNbetaY` where
|
||||
* `N` and `Y` are both integers greater than 0.
|
||||
*
|
||||
* Examples: The following are valid values for this field.
|
||||
* - `v1`
|
||||
* - `v1beta1`
|
||||
* - `v1alpha2`
|
||||
* - `v3beta2`
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* The constructor for the entities.
|
||||
*/
|
||||
readonly entityClass: CatalogEntityConstructor<CatalogEntity>;
|
||||
}
|
||||
|
||||
export interface CatalogCategorySpec {
|
||||
/**
|
||||
* The grouping for for the category. This MUST be a DNS label.
|
||||
*/
|
||||
readonly group: string;
|
||||
|
||||
/**
|
||||
* The specific versions of the constructors.
|
||||
*
|
||||
* NOTE: the field `.apiVersion` after construction MUST match `{.group}/{.versions.[] | .name}`.
|
||||
* For example, if `group = "entity.k8slens.dev"` and there is an entry in `.versions` with
|
||||
* `name = "v1alpha1"` then the resulting `.apiVersion` MUST be `entity.k8slens.dev/v1alpha1`
|
||||
*/
|
||||
readonly versions: CatalogCategoryVersion[];
|
||||
|
||||
/**
|
||||
* This is the concerning the category
|
||||
*/
|
||||
readonly names: {
|
||||
/**
|
||||
* The kind of entity that this category is for. This value MUST be a DNS
|
||||
* label and MUST be equal to the `kind` fields that are produced by the
|
||||
* `.versions.[] | .entityClass` fields.
|
||||
*/
|
||||
readonly kind: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* These are the columns used for displaying entities when in the catalog.
|
||||
*
|
||||
* If this is not provided then some default columns will be used, similar in
|
||||
* scope to the columns in the "Browse" view.
|
||||
*
|
||||
* Even if you provide columns, a "Name" column will be provided as well with
|
||||
* `priority: 0`.
|
||||
*
|
||||
* These columns will not be used in the "Browse" view.
|
||||
*/
|
||||
readonly displayColumns?: CategoryColumnRegistration[];
|
||||
}
|
||||
|
||||
/**
|
||||
* If the filter return a thruthy value, the menu item is displayed
|
||||
*/
|
||||
export type AddMenuFilter = (menu: CatalogEntityAddMenu) => any;
|
||||
|
||||
export interface CatalogCategoryEvents {
|
||||
/**
|
||||
* This event will be emitted when the category is loaded in the catalog
|
||||
* view.
|
||||
*/
|
||||
load: () => void;
|
||||
|
||||
/**
|
||||
* This event will be emitted when the catalog add menu is opened and is the
|
||||
* way to added entries to that menu.
|
||||
*/
|
||||
catalogAddMenu: (context: CatalogEntityAddMenuContext) => void;
|
||||
|
||||
/**
|
||||
* This event will be emitted when the context menu for an entity is declared
|
||||
* by this category is opened.
|
||||
*/
|
||||
contextMenuOpen: (entity: CatalogEntity, context: CatalogEntityContextMenuContext) => void;
|
||||
}
|
||||
|
||||
export interface CatalogCategoryMetadata {
|
||||
/**
|
||||
* The name of your category. The category can be searched for by this
|
||||
* value. This will also be used for the catalog menu.
|
||||
*/
|
||||
readonly name: string;
|
||||
/**
|
||||
* Either an `<svg>` or the name of an icon from {@link IconProps}
|
||||
*/
|
||||
readonly icon: string;
|
||||
}
|
||||
|
||||
export function categoryVersion<
|
||||
T extends CatalogEntity<Metadata, Status, Spec>,
|
||||
Metadata extends CatalogEntityMetadata,
|
||||
Status extends CatalogEntityStatus,
|
||||
Spec extends CatalogEntitySpec,
|
||||
>(name: string, entityClass: new (data: CatalogEntityData<Metadata, Status, Spec>) => T): CatalogCategoryVersion {
|
||||
return {
|
||||
name,
|
||||
entityClass: entityClass as CatalogEntityConstructor<T>,
|
||||
};
|
||||
}
|
||||
|
||||
export abstract class CatalogCategory extends (EventEmitter as new () => TypedEmitter<CatalogCategoryEvents>) {
|
||||
/**
|
||||
* The version of category that you are wanting to declare.
|
||||
*
|
||||
* Currently supported values:
|
||||
*
|
||||
* - `"catalog.k8slens.dev/v1alpha1"`
|
||||
*/
|
||||
abstract readonly apiVersion: string;
|
||||
|
||||
/**
|
||||
* The kind of item you wish to declare.
|
||||
*
|
||||
* Currently supported values:
|
||||
*
|
||||
* - `"CatalogCategory"`
|
||||
*/
|
||||
abstract readonly kind: string;
|
||||
|
||||
/**
|
||||
* The data about the category itself
|
||||
*/
|
||||
abstract readonly metadata: CatalogCategoryMetadata;
|
||||
|
||||
/**
|
||||
* The most important part of a category, as it is where entity versions are declared.
|
||||
*/
|
||||
abstract readonly spec: CatalogCategorySpec;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected readonly filters = observable.set<AddMenuFilter>([], {
|
||||
deep: false,
|
||||
});
|
||||
|
||||
/**
|
||||
* Parse a category ID into parts.
|
||||
* @param id The id of a category is parse
|
||||
* @returns The group and kind parts of the ID
|
||||
*/
|
||||
public static parseId(id: string): { group?: string; kind?: string } {
|
||||
const [group, kind] = id.split("/") ?? [];
|
||||
|
||||
return { group, kind };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ID of this category
|
||||
*/
|
||||
public getId(): string {
|
||||
return `${this.spec.group}/${this.spec.names.kind}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of this category
|
||||
*/
|
||||
public getName(): string {
|
||||
return this.metadata.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the badge of this category.
|
||||
* Defaults to no badge.
|
||||
* The badge is displayed next to the Category name in the Catalog Category menu
|
||||
*/
|
||||
public getBadge(): React.ReactNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a filter for menu items of catalogAddMenu
|
||||
* @param fn The function that should return a truthy value if that menu item should be displayed
|
||||
* @returns A function to remove that filter
|
||||
*/
|
||||
public addMenuFilter(fn: AddMenuFilter): Disposer {
|
||||
this.filters.add(fn);
|
||||
|
||||
return once(() => void this.filters.delete(fn));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter menuItems according to the Category's set filters
|
||||
* @param menuItems menu items to filter
|
||||
* @returns filtered menu items
|
||||
*/
|
||||
public filteredItems(menuItems: CatalogEntityAddMenu[]) {
|
||||
return Array.from(
|
||||
iter.reduce(
|
||||
this.filters,
|
||||
iter.filter,
|
||||
menuItems.values(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export type EntityMetadataObject = { [Key in string]?: EntityMetadataValue };
|
||||
export type EntityMetadataValue = string | number | boolean | EntityMetadataObject | undefined;
|
||||
|
||||
export interface CatalogEntityMetadata extends EntityMetadataObject {
|
||||
uid: string;
|
||||
name: string;
|
||||
shortName?: string;
|
||||
description?: string;
|
||||
source?: string;
|
||||
labels: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface CatalogEntityStatus {
|
||||
phase: string;
|
||||
reason?: string;
|
||||
|
||||
/**
|
||||
* @default true
|
||||
*/
|
||||
enabled?: boolean;
|
||||
message?: string;
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
export interface CatalogEntityActionContext {
|
||||
navigate: (url: string) => void;
|
||||
setCommandPaletteContext: (context?: CatalogEntity) => void;
|
||||
}
|
||||
|
||||
export interface CatalogEntityContextMenu {
|
||||
/**
|
||||
* Menu title
|
||||
*/
|
||||
title: string;
|
||||
/**
|
||||
* Menu icon
|
||||
*/
|
||||
icon?: string;
|
||||
/**
|
||||
* OnClick handler
|
||||
*/
|
||||
onClick: () => void | Promise<void>;
|
||||
/**
|
||||
* Confirm click with a message
|
||||
*/
|
||||
confirm?: {
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CatalogEntityAddMenu extends CatalogEntityContextMenu {
|
||||
icon: string;
|
||||
defaultAction?: boolean;
|
||||
}
|
||||
|
||||
export interface CatalogEntitySettingsMenu {
|
||||
group?: string;
|
||||
title: string;
|
||||
components: {
|
||||
View: React.ComponentType<any>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CatalogEntityContextMenuNavigate {
|
||||
/**
|
||||
* @param pathname The location to navigate to in the main iframe
|
||||
*/
|
||||
(pathname: string, forceMainFrame?: boolean): void;
|
||||
/**
|
||||
* @param pathname The location to navigate to in the current iframe. Useful for when called within the cluster frame
|
||||
*/
|
||||
(pathname: string, forceMainFrame: false): void;
|
||||
}
|
||||
|
||||
export interface CatalogEntityContextMenuContext {
|
||||
/**
|
||||
* Navigate to the specified pathname
|
||||
*/
|
||||
navigate: CatalogEntityContextMenuNavigate;
|
||||
menuItems: CatalogEntityContextMenu[];
|
||||
}
|
||||
|
||||
export interface CatalogEntitySettingsContext {
|
||||
menuItems: CatalogEntityContextMenu[];
|
||||
}
|
||||
|
||||
export interface CatalogEntityAddMenuContext {
|
||||
navigate: (url: string) => void;
|
||||
menuItems: CatalogEntityAddMenu[];
|
||||
}
|
||||
|
||||
export type CatalogEntitySpec = Record<string, any>;
|
||||
|
||||
|
||||
export interface CatalogEntityData<
|
||||
Metadata extends CatalogEntityMetadata = CatalogEntityMetadata,
|
||||
Status extends CatalogEntityStatus = CatalogEntityStatus,
|
||||
Spec extends CatalogEntitySpec = CatalogEntitySpec,
|
||||
> {
|
||||
metadata: Metadata;
|
||||
status: Status;
|
||||
spec: Spec;
|
||||
}
|
||||
|
||||
export interface CatalogEntityKindData {
|
||||
readonly apiVersion: string;
|
||||
readonly kind: string;
|
||||
}
|
||||
|
||||
export abstract class CatalogEntity<
|
||||
Metadata extends CatalogEntityMetadata = CatalogEntityMetadata,
|
||||
Status extends CatalogEntityStatus = CatalogEntityStatus,
|
||||
Spec extends CatalogEntitySpec = CatalogEntitySpec,
|
||||
> implements CatalogEntityKindData {
|
||||
/**
|
||||
* The group and version of this class.
|
||||
*/
|
||||
public abstract readonly apiVersion: string;
|
||||
|
||||
/**
|
||||
* A DNS label name of the entity.
|
||||
*/
|
||||
public abstract readonly kind: string;
|
||||
|
||||
@observable metadata: Metadata;
|
||||
@observable status: Status;
|
||||
@observable spec: Spec;
|
||||
|
||||
constructor({ metadata, status, spec }: CatalogEntityData<Metadata, Status, Spec>) {
|
||||
makeObservable(this);
|
||||
|
||||
if (!metadata || typeof metadata !== "object") {
|
||||
throw new TypeError("CatalogEntity's metadata must be a defined object");
|
||||
}
|
||||
|
||||
if (!status || typeof status !== "object") {
|
||||
throw new TypeError("CatalogEntity's status must be a defined object");
|
||||
}
|
||||
|
||||
if (!spec || typeof spec !== "object") {
|
||||
throw new TypeError("CatalogEntity's spec must be a defined object");
|
||||
}
|
||||
|
||||
this.metadata = metadata;
|
||||
this.status = status;
|
||||
this.spec = spec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the UID of this entity
|
||||
*/
|
||||
public getId(): string {
|
||||
return this.metadata.uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of this entity
|
||||
*/
|
||||
public getName(): string {
|
||||
return this.metadata.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified source of this entity, defaulting to `"unknown"` if not
|
||||
* provided
|
||||
*/
|
||||
public getSource(): string {
|
||||
return this.metadata.source ?? "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if this entity is enabled.
|
||||
*/
|
||||
public isEnabled(): boolean {
|
||||
return this.status.enabled ?? true;
|
||||
}
|
||||
|
||||
public onRun?(context: CatalogEntityActionContext): void | Promise<void>;
|
||||
public onContextMenuOpen?(context: CatalogEntityContextMenuContext): void | Promise<void>;
|
||||
public onSettingsOpen?(context: CatalogEntitySettingsContext): void | Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { CatalogEntity } from "../catalog";
|
||||
|
||||
export class CatalogRunEvent {
|
||||
#defaultPrevented: boolean;
|
||||
#target: CatalogEntity;
|
||||
|
||||
get defaultPrevented() {
|
||||
return this.#defaultPrevented;
|
||||
}
|
||||
|
||||
get target() {
|
||||
return this.#target;
|
||||
}
|
||||
|
||||
constructor({ target }: { target: CatalogEntity }) {
|
||||
this.#defaultPrevented = false;
|
||||
this.#target = target;
|
||||
}
|
||||
|
||||
preventDefault() {
|
||||
this.#defaultPrevented = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { GeneralCategory } from "../../catalog-entities";
|
||||
import { builtInCategoryInjectionToken } from "../category-registry.injectable";
|
||||
|
||||
const generalCategoryInjectable = getInjectable({
|
||||
id: "general-category",
|
||||
instantiate: () => new GeneralCategory(),
|
||||
injectionToken: builtInCategoryInjectionToken,
|
||||
});
|
||||
|
||||
export default generalCategoryInjectable;
|
||||
Loaded 100 of 3232 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user