From 007982bf488273f6f43787d9fe3e4852b3b0dada Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 14:40:53 +0300 Subject: [PATCH 01/23] Bump @kubernetes/client-node from 0.12.0 to 0.14.3 (#2990) Bumps [@kubernetes/client-node](https://github.com/kubernetes-client/javascript) from 0.12.0 to 0.14.3. - [Release notes](https://github.com/kubernetes-client/javascript/releases) - [Changelog](https://github.com/kubernetes-client/javascript/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes-client/javascript/compare/0.12.0...0.14.3) --- updated-dependencies: - dependency-name: "@kubernetes/client-node" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 468 ++++++++++++++++++++++----------------------------- 2 files changed, 202 insertions(+), 268 deletions(-) diff --git a/package.json b/package.json index 30befd8c73..728497a5d0 100644 --- a/package.json +++ b/package.json @@ -181,7 +181,7 @@ "dependencies": { "@hapi/call": "^8.0.1", "@hapi/subtext": "^7.0.3", - "@kubernetes/client-node": "^0.12.0", + "@kubernetes/client-node": "^0.14.3", "abort-controller": "^3.0.0", "array-move": "^3.0.1", "auto-bind": "^4.0.0", diff --git a/yarn.lock b/yarn.lock index 5f82a3b326..c7fe91b635 100644 --- a/yarn.lock +++ b/yarn.lock @@ -789,14 +789,16 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" -"@kubernetes/client-node@^0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@kubernetes/client-node/-/client-node-0.12.0.tgz#79120311bced206ac8fa36435fb4cc2c1828fff2" - integrity sha512-u57q5IaZl91f7YZoZOsgCa31hHyowHxFG88XZXd8arI8heSxbdHWHineo/8mLZbeSbHkge9Awae1stQZzuTnjg== +"@kubernetes/client-node@^0.14.3": + version "0.14.3" + resolved "https://registry.yarnpkg.com/@kubernetes/client-node/-/client-node-0.14.3.tgz#5ed9b88873419080547f22cb74eb502bf6671fca" + integrity sha512-9hHGDNm2JEFQcRTpDxVoAVr0fowU+JH/l5atCXY9VXwvFM18pW5wr2LzLP+Q2Rh+uQv7Moz4gEjEKSCgVKykEQ== dependencies: "@types/js-yaml" "^3.12.1" "@types/node" "^10.12.0" "@types/request" "^2.47.1" + "@types/stream-buffers" "^3.0.3" + "@types/tar" "^4.0.3" "@types/underscore" "^1.8.9" "@types/ws" "^6.0.1" byline "^5.0.0" @@ -804,13 +806,16 @@ isomorphic-ws "^4.0.1" js-yaml "^3.13.1" jsonpath-plus "^0.19.0" - openid-client "2.5.0" + openid-client "^4.1.1" request "^2.88.0" rfc4648 "^1.3.0" shelljs "^0.8.2" + stream-buffers "^3.0.2" + tar "^6.0.2" + tmp-promise "^3.0.2" tslib "^1.9.3" underscore "^1.9.1" - ws "^6.1.0" + ws "^7.3.1" "@malept/cross-spawn-promise@^1.1.0": version "1.1.1" @@ -961,10 +966,10 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@sindresorhus/is@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" - integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== +"@sindresorhus/is@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.1.tgz#d26729db850fa327b7cacc5522252194404226f5" + integrity sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g== "@sinonjs/commons@^1.7.0": version "1.8.0" @@ -987,6 +992,13 @@ dependencies: defer-to-connect "^1.0.1" +"@szmarczak/http-timer@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152" + integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== + dependencies: + defer-to-connect "^2.0.0" + "@testing-library/dom@>=7", "@testing-library/dom@^7.28.1": version "7.30.3" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.30.3.tgz#779ea9bbb92d63302461800a388a5a890ac22519" @@ -1086,6 +1098,16 @@ dependencies: "@types/node" "*" +"@types/cacheable-request@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" + integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "*" + "@types/node" "*" + "@types/responselike" "*" + "@types/caseless@*": version "0.12.2" resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8" @@ -1300,6 +1322,11 @@ "@types/tapable" "*" "@types/webpack" "*" +"@types/http-cache-semantics@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" + integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== + "@types/http-proxy-middleware@*": version "0.19.3" resolved "https://registry.yarnpkg.com/@types/http-proxy-middleware/-/http-proxy-middleware-0.19.3.tgz#b2eb96fbc0f9ac7250b5d9c4c53aade049497d03" @@ -1408,6 +1435,13 @@ resolved "https://registry.yarnpkg.com/@types/jsonpath/-/jsonpath-0.2.0.tgz#13c62db22a34d9c411364fac79fd374d63445aa1" integrity sha512-v7qlPA0VpKUlEdhghbDqRoKMxFB3h3Ch688TApBJ6v+XLDdvWCGLJIYiPKGZnS6MAOie+IorCfNYVHOPIHSWwQ== +"@types/keyv@*": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" + integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw== + dependencies: + "@types/node" "*" + "@types/lodash@^4.14.155": version "4.14.155" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.155.tgz#e2b4514f46a261fd11542e47519c20ebce7bc23a" @@ -1476,7 +1510,7 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.41.tgz#d0b939d94c1d7bd53d04824af45f1139b8c45615" integrity sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g== -"@types/node@12.20": +"@types/node@12.20", "@types/node@^12.0.12": version "12.20.11" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.11.tgz#980832cd56efafff8c18aa148c4085eb02a483f4" integrity sha512-gema+apZ6qLQK7k7F0dGkGCWQYsL0qqKORWOQO6tq46q+x+1C0vbOiOqOwRVlh4RAdbQwV/j/ryr3u5NOG1fPQ== @@ -1486,11 +1520,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.24.tgz#c57511e3a19c4b5e9692bb2995c40a3a52167944" integrity sha512-5SCfvCxV74kzR3uWgTYiGxrd69TbT1I6+cMx1A5kEly/IVveJBimtAMlXiEyVFn5DvUFewQWxOOiJhlxeQwxgA== -"@types/node@^12.0.12": - version "12.12.45" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.45.tgz#33d550d6da243652004b00cbf4f15997456a38e3" - integrity sha512-9w50wqeS0qQH9bo1iIRcQhDXRxoDzyAqCL5oJG+Nuu7cAoe6omGo+YDE0spAGK5sPrdLDhQLbQxq0DnxyndPKA== - "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" @@ -1682,6 +1711,13 @@ "@types/tough-cookie" "*" form-data "^2.5.0" +"@types/responselike@*", "@types/responselike@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + "@types/retry@*": version "0.12.0" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" @@ -1746,12 +1782,19 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== +"@types/stream-buffers@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/stream-buffers/-/stream-buffers-3.0.3.tgz#34e565bf64e3e4bdeee23fd4aa58d4636014a02b" + integrity sha512-NeFeX7YfFZDYsCfbuaOmFQ0OjSmHreKBpp7MQ4alWQBHeh2USLsj7qyMyn9t82kjqIX516CR/5SRHnARduRtbQ== + dependencies: + "@types/node" "*" + "@types/tapable@*", "@types/tapable@^1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.5.tgz#9adbc12950582aa65ead76bffdf39fe0c27a3c02" integrity sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ== -"@types/tar@^4.0.4": +"@types/tar@^4.0.3", "@types/tar@^4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@types/tar/-/tar-4.0.4.tgz#d680de60855e7778a51c672b755869a3b8d2889f" integrity sha512-0Xv+xcmkTsOZdIF4yCnd7RkOOyfyqPaqJ7RZFKnwdxfDbkN3eAAE9sHl8zJFqBz4VhxolW9EErbjR1oyH7jK2A== @@ -2269,14 +2312,6 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" -aggregate-error@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-1.0.0.tgz#888344dad0220a72e3af50906117f48771925fac" - integrity sha1-iINE2tAiCnLjr1CQYRf0h3GSX6w= - dependencies: - clean-stack "^1.0.0" - indent-string "^3.0.0" - aggregate-error@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0" @@ -2285,6 +2320,14 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" +aggregate-error@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv-errors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" @@ -2855,7 +2898,7 @@ base64-js@^1.5.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -base64url@^3.0.0, base64url@^3.0.1: +base64url@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== @@ -3324,18 +3367,10 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -cacheable-request@^2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" - integrity sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0= - dependencies: - clone-response "1.0.2" - get-stream "3.0.0" - http-cache-semantics "3.8.1" - keyv "3.0.0" - lowercase-keys "1.0.0" - normalize-url "2.0.1" - responselike "1.0.2" +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== cacheable-request@^6.0.0: version "6.1.0" @@ -3350,6 +3385,19 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" +cacheable-request@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58" + integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^2.0.0" + call-bind@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce" @@ -3652,11 +3700,6 @@ clean-css@^4.2.3: dependencies: source-map "~0.6.0" -clean-stack@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-1.3.0.tgz#9e821501ae979986c46b1d66d2d432db2fd4ae31" - integrity sha1-noIVAa6XmYbEax1m0tQy2y/UrjE= - clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -3760,7 +3803,7 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -clone-response@1.0.2, clone-response@^1.0.2: +clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= @@ -4643,6 +4686,11 @@ defer-to-connect@^1.0.1: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +defer-to-connect@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -5381,7 +5429,7 @@ es6-error@^4.1.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -es6-promise@^4.0.3, es6-promise@^4.2.8: +es6-promise@^4.0.3: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== @@ -6265,7 +6313,7 @@ from2@^1.3.0: inherits "~2.0.1" readable-stream "~1.1.10" -from2@^2.1.0, from2@^2.1.1: +from2@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= @@ -6480,7 +6528,7 @@ get-stdin@^4.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= -get-stream@3.0.0, get-stream@^3.0.0: +get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= @@ -6689,6 +6737,23 @@ globule@^1.0.0: lodash "~4.17.10" minimatch "~3.0.2" +got@^11.8.0: + version "11.8.2" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599" + integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.1" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + got@^6.7.1: version "6.7.1" resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" @@ -6706,29 +6771,6 @@ got@^6.7.1: unzip-response "^2.0.1" url-parse-lax "^1.0.0" -got@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/got/-/got-8.3.2.tgz#1d23f64390e97f776cac52e5b936e5f514d2e937" - integrity sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw== - dependencies: - "@sindresorhus/is" "^0.7.0" - cacheable-request "^2.1.1" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - into-stream "^3.1.0" - is-retry-allowed "^1.1.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - mimic-response "^1.0.0" - p-cancelable "^0.4.0" - p-timeout "^2.0.1" - pify "^3.0.0" - safe-buffer "^5.1.1" - timed-out "^4.0.1" - url-parse-lax "^3.0.0" - url-to-options "^1.0.1" - got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -6828,23 +6870,11 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbol-support-x@^1.4.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" - integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== - has-symbols@^1.0.0, has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== -has-to-string-tag-x@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" - integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== - dependencies: - has-symbol-support-x "^1.4.1" - has-unicode@^2.0.0, has-unicode@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -7041,7 +7071,7 @@ htmlparser2@^3.3.0: inherits "^2.0.1" readable-stream "^3.1.1" -http-cache-semantics@3.8.1, http-cache-semantics@^3.8.1: +http-cache-semantics@^3.8.1: version "3.8.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== @@ -7129,6 +7159,14 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" @@ -7342,11 +7380,6 @@ indent-string@^2.1.0: dependencies: repeating "^2.0.0" -indent-string@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" - integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= - indent-string@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" @@ -7456,14 +7489,6 @@ interpret@^1.0.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== -into-stream@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6" - integrity sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY= - dependencies: - from2 "^2.1.1" - p-is-promise "^1.1.0" - invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" @@ -7785,11 +7810,6 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" - integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= - is-path-cwd@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" @@ -7826,11 +7846,6 @@ is-path-inside@^3.0.2: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== -is-plain-obj@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= - is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -7862,7 +7877,7 @@ is-regex@^1.0.5: dependencies: has-symbols "^1.0.1" -is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: +is-retry-allowed@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== @@ -8027,14 +8042,6 @@ istextorbinary@^5.12.0: editions "^6.1.0" textextensions "^5.11.0" -isurl@^1.0.0-alpha5: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" - integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== - dependencies: - has-to-string-tag-x "^1.2.0" - is-object "^1.0.1" - jake@^10.6.1: version "10.8.1" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.1.tgz#0f6f5ef13ebe014104527fb4b1b24f44cd1f04d6" @@ -8499,6 +8506,13 @@ jose@^1.27.1: dependencies: "@panva/asn1.js" "^1.0.0" +jose@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/jose/-/jose-2.0.5.tgz#29746a18d9fff7dcf9d5d2a6f62cb0c7cd27abd3" + integrity sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA== + dependencies: + "@panva/asn1.js" "^1.0.0" + js-base64@^2.1.8: version "2.5.2" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.2.tgz#313b6274dda718f714d00b3330bbae6e38e90209" @@ -8514,7 +8528,7 @@ js-sha3@^0.8.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.13.1: +js-yaml@^3.13.1, js-yaml@^3.14.0: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -8522,14 +8536,6 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^3.14.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" - integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - js-yaml@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f" @@ -8616,6 +8622,11 @@ json-buffer@3.0.0: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -8809,13 +8820,6 @@ jszip@^3.1.0: readable-stream "~2.3.6" set-immediate-shim "~1.0.1" -keyv@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" - integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA== - dependencies: - json-buffer "3.0.0" - keyv@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" @@ -8823,6 +8827,13 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" +keyv@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254" + integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA== + dependencies: + json-buffer "3.0.1" + killable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" @@ -9295,11 +9306,6 @@ loglevel@^1.6.8: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0" integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ== -long@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" - integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== - loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -9322,11 +9328,6 @@ lower-case@^2.0.1: dependencies: tslib "^1.10.0" -lowercase-keys@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" - integrity sha1-TjNms55/VFfjXxMkvfb4jQv8cwY= - lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" @@ -10019,7 +10020,7 @@ node-forge@^0.7.5: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw== -node-forge@^0.8.2, node-forge@^0.8.5: +node-forge@^0.8.2: version "0.8.5" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.8.5.tgz#57906f07614dc72762c84cef442f427c0e1b86ee" integrity sha512-vFMQIWt+J/7FLNyKouZ9TazT74PRV3wgv9UT4cRjC8BffxFbKXkgIWR42URCPSnHm/QDz6BOlb2Q0U4+VQT67Q== @@ -10064,22 +10065,6 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-jose@^1.1.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/node-jose/-/node-jose-1.1.4.tgz#af3f44a392e586d26b123b0e12dc09bef1e9863b" - integrity sha512-L31IFwL3pWWcMHxxidCY51ezqrDXMkvlT/5pLTfNw5sXmmOLJuN6ug7txzF/iuZN55cRpyOmoJrotwBQIoo5Lw== - dependencies: - base64url "^3.0.1" - browserify-zlib "^0.2.0" - buffer "^5.5.0" - es6-promise "^4.2.8" - lodash "^4.17.15" - long "^4.0.0" - node-forge "^0.8.5" - process "^0.11.10" - react-zlib-js "^1.0.4" - uuid "^3.3.3" - node-libs-browser@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" @@ -10239,15 +10224,6 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" - integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== - dependencies: - prepend-http "^2.0.0" - query-string "^5.0.1" - sort-keys "^2.0.0" - normalize-url@^4.1.0: version "4.5.0" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" @@ -10553,11 +10529,6 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-hash@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" - integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== - object-hash@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea" @@ -10667,16 +10638,16 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -oidc-token-hash@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-3.0.2.tgz#5bd4716cc48ad433f4e4e99276811019b165697e" - integrity sha512-dTzp80/y/da+um+i+sOucNqiPpwRL7M/xPwj7pH1TFA2/bqQ+OK2sJahSXbemEoLtPkHcFLyhLhLWZa9yW5+RA== - oidc-token-hash@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.0.tgz#acdfb1f4310f58e64d5d74a4e8671a426986e888" integrity sha512-8Yr4CZSv+Tn8ZkN3iN2i2w2G92mUKClp4z7EGUfdsERiYSbj7P4i/NHm72ft+aUdsiFx9UdIPSTwbyzQ6C4URg== +oidc-token-hash@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz#ae6beec3ec20f0fd885e5400d175191d6e2f10c6" + integrity sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ== + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -10735,20 +10706,6 @@ opener@^1.5.2: resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== -openid-client@2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-2.5.0.tgz#7d4cf552b30dbad26917d7e2722422eda057ea93" - integrity sha512-t3hFD7xEoW1U25RyBcRFaL19fGGs6hNVTysq9pgmiltH0IVUPzH/bQV9w24pM5Q7MunnGv2/5XjIru6BQcWdxg== - dependencies: - base64url "^3.0.0" - got "^8.3.2" - lodash "^4.17.11" - lru-cache "^5.1.1" - node-jose "^1.1.0" - object-hash "^1.3.1" - oidc-token-hash "^3.0.1" - p-any "^1.1.0" - openid-client@^3.15.2: version "3.15.2" resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-3.15.2.tgz#d48a6d7991d5d6117f4598bb1d19399262164128" @@ -10765,6 +10722,19 @@ openid-client@^3.15.2: oidc-token-hash "^5.0.0" p-any "^3.0.0" +openid-client@^4.1.1: + version "4.7.4" + resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-4.7.4.tgz#bd9978456d53d38adb89856b14a8fbd094f7732e" + integrity sha512-n+RURXYuR0bBZo9i0pn+CXZSyg5JYQ1nbwEwPQvLE7EcJt/vMZ2iIMjLehl5DvCN53XUoPVZs9KAE5r6d9fxsw== + dependencies: + aggregate-error "^3.1.0" + got "^11.8.0" + jose "^2.0.5" + lru-cache "^6.0.0" + make-error "^1.3.6" + object-hash "^2.0.1" + oidc-token-hash "^5.0.1" + opn@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" @@ -10852,13 +10822,6 @@ osenv@0, osenv@^0.1.4, osenv@^0.1.5: os-homedir "^1.0.0" os-tmpdir "^1.0.0" -p-any@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-any/-/p-any-1.1.0.tgz#1d03835c7eed1e34b8e539c47b7b60d0d015d4e1" - integrity sha512-Ef0tVa4CZ5pTAmKn+Cg3w8ABBXh+hHO1aV8281dKOoUHfX+3tjG2EaFcC+aZyagg9b4EYGsHEjz21DnEE8Og2g== - dependencies: - p-some "^2.0.0" - p-any@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-any/-/p-any-3.0.0.tgz#79847aeed70b5d3a10ea625296c0c3d2e90a87b9" @@ -10867,11 +10830,6 @@ p-any@^3.0.0: p-cancelable "^2.0.0" p-some "^5.0.0" -p-cancelable@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0" - integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ== - p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -10897,11 +10855,6 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= -p-is-promise@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" - integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4= - p-is-promise@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" @@ -10961,13 +10914,6 @@ p-retry@^3.0.1: dependencies: retry "^0.12.0" -p-some@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/p-some/-/p-some-2.0.1.tgz#65d87c8b154edbcf5221d167778b6d2e150f6f06" - integrity sha1-Zdh8ixVO289SIdFnd4ttLhUPbwY= - dependencies: - aggregate-error "^1.0.0" - p-some@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/p-some/-/p-some-5.0.0.tgz#8b730c74b4fe5169d7264a240ad010b6ebc686a4" @@ -10976,13 +10922,6 @@ p-some@^5.0.0: aggregate-error "^3.0.0" p-cancelable "^2.0.0" -p-timeout@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" - integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== - dependencies: - p-finally "^1.0.0" - p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -11846,15 +11785,6 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== -query-string@^5.0.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" - integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== - dependencies: - decode-uri-component "^0.2.0" - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - query-string@^6.8.2: version "6.14.1" resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" @@ -12077,11 +12007,6 @@ react-window@^1.8.5: "@babel/runtime" "^7.0.0" memoize-one ">=3.1.1 <6" -react-zlib-js@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/react-zlib-js/-/react-zlib-js-1.0.4.tgz#dd2b9fbf56d5ab224fa7a99affbbedeba9aa3dc7" - integrity sha512-ynXD9DFxpE7vtGoa3ZwBtPmZrkZYw2plzHGbanUjBOSN4RtuXdektSfABykHtTiWEHMh7WdYj45LHtp228ZF1A== - react@^16.8.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" @@ -12493,6 +12418,11 @@ reserved-words@^0.1.2: resolved "https://registry.yarnpkg.com/reserved-words/-/reserved-words-0.1.2.tgz#00a0940f98cd501aeaaac316411d9adc52b31ab1" integrity sha1-AKCUD5jNUBrqqsMWQR2a3FKzGrE= +resolve-alpn@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.1.2.tgz#30b60cfbb0c0b8dc897940fe13fe255afcdd4d28" + integrity sha512-8OyfzhAtA32LVUsJSke3auIyINcwdh5l3cvYKdKO0nvsYSKuiLfTM5i78PJswFPT8y6cPW+L1v6/hE95chcpDA== + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -12563,13 +12493,20 @@ resolve@^1.20.0: is-core-module "^2.2.0" path-parse "^1.0.6" -responselike@1.0.2, responselike@^1.0.2: +responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= dependencies: lowercase-keys "^1.0.0" +responselike@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" + integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== + dependencies: + lowercase-keys "^2.0.0" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -13214,13 +13151,6 @@ socks@~2.3.2: ip "1.1.5" smart-buffer "^4.1.0" -sort-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" - integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= - dependencies: - is-plain-obj "^1.0.0" - sorted-object@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/sorted-object/-/sorted-object-2.0.1.tgz#7d631f4bd3a798a24af1dffcfbfe83337a5df5fc" @@ -13472,6 +13402,11 @@ stream-browserify@^2.0.1: inherits "~2.0.1" readable-stream "^2.0.2" +stream-buffers@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.2.tgz#5249005a8d5c2d00b3a32e6e0a6ea209dc4f3521" + integrity sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ== + stream-each@^1.1.0: version "1.2.3" resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" @@ -13504,11 +13439,6 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -strict-uri-encode@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" - integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= - strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" @@ -13919,10 +13849,10 @@ tar@^4.4.10, tar@^4.4.12, tar@^4.4.13: safe-buffer "^5.1.2" yallist "^3.0.3" -tar@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f" - integrity sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg== +tar@^6.0.2, tar@^6.0.5: + version "6.1.0" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" + integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" @@ -14053,7 +13983,7 @@ thunky@^1.0.2: resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== -timed-out@^4.0.0, timed-out@^4.0.1: +timed-out@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= @@ -14080,6 +14010,13 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.2, tiny-warning@^1.0.3: resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== +tmp-promise@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-3.0.2.tgz#6e933782abff8b00c3119d63589ca1fb9caaa62a" + integrity sha512-OyCLAKU1HzBjL6Ev3gxUeraJNlbNingmi8IrHHEsYH8LTmEuhvYfqvhn2F/je+mjf4N58UmZ96OMEy1JanSCpA== + dependencies: + tmp "^0.2.0" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -14087,6 +14024,13 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" +tmp@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + tmpl@1.0.x: version "1.0.4" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" @@ -14270,12 +14214,7 @@ tsconfig-paths@^3.9.0: minimist "^1.2.0" strip-bom "^3.0.0" -tslib@^1.10.0, tslib@^1.9.0, tslib@^1.9.3: - version "1.13.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" - integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== - -tslib@^1.8.1: +tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -14673,11 +14612,6 @@ url-parse@^1.4.3, url-parse@^1.5.1: querystringify "^2.1.1" requires-port "^1.0.0" -url-to-options@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" - integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= - url@^0.11.0, url@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -15279,14 +15213,14 @@ write@1.0.3: dependencies: mkdirp "^0.5.1" -ws@^6.1.0, ws@^6.2.1: +ws@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== dependencies: async-limiter "~1.0.0" -ws@^7.2.3, ws@^7.4.6: +ws@^7.2.3, ws@^7.3.1, ws@^7.4.6: version "7.4.6" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== From 757fed47fa6fc7754ee03060219b065ad23c222f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 07:41:25 -0400 Subject: [PATCH 02/23] Bump @types/sharp from 0.26.0 to 0.28.3 (#2993) Bumps [@types/sharp](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/sharp) from 0.26.0 to 0.28.3. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/sharp) --- updated-dependencies: - dependency-name: "@types/sharp" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 728497a5d0..3f66175f3e 100644 --- a/package.json +++ b/package.json @@ -290,7 +290,7 @@ "@types/request": "^2.48.5", "@types/request-promise-native": "^1.0.17", "@types/semver": "^7.2.0", - "@types/sharp": "^0.26.0", + "@types/sharp": "^0.28.3", "@types/shelljs": "^0.8.8", "@types/spdy": "^3.4.4", "@types/tar": "^4.0.4", diff --git a/yarn.lock b/yarn.lock index c7fe91b635..78491269c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1743,10 +1743,10 @@ "@types/mime" "*" "@types/node" "*" -"@types/sharp@^0.26.0": - version "0.26.0" - resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.26.0.tgz#2fa8419dbdaca8dd38f73888b27b207f188a8669" - integrity sha512-oJrR8eiwpL7qykn2IeFRduXM4za7z+7yOUEbKVtuDQ/F6htDLHYO6IbzhaJQHV5n6O3adIh4tJvtgPyLyyydqg== +"@types/sharp@^0.28.3": + version "0.28.3" + resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.28.3.tgz#0e57ede34d3e334632ab7a68a6af070aa0f51ceb" + integrity sha512-y3mxUj3jukIWgdu9CrSTSCo5HruTzDxdjn5SqdIEALdTszkcot9r8HX/7q9FMx3YjuXifTD0OI+d4wA6Pogqmw== dependencies: "@types/node" "*" From ae2fa15b20e3bdf56def5ddf4f060d2448e177e6 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 8 Jun 2021 07:41:36 -0400 Subject: [PATCH 03/23] Make customizing ItemListLayout header explicitly reducable (#2956) - Add search placeholder text for helm releases Signed-off-by: Sebastian Malton --- .../+apps-helm-charts/helm-charts.tsx | 10 ++- .../components/+apps-releases/releases.tsx | 8 +- src/renderer/components/+catalog/catalog.tsx | 2 - .../+custom-resources/crd-list.scss | 6 +- .../components/+custom-resources/crd-list.tsx | 46 ++++++------ .../+custom-resources/crd-resources.tsx | 7 ++ src/renderer/components/+events/events.tsx | 20 +++-- .../components/input/search-input-url.tsx | 6 +- .../item-object-list/item-list-layout.tsx | 73 ++++++++----------- .../kube-object/kube-object-list-layout.tsx | 21 +++++- src/renderer/utils/rbac.ts | 7 +- 11 files changed, 122 insertions(+), 84 deletions(-) diff --git a/src/renderer/components/+apps-helm-charts/helm-charts.tsx b/src/renderer/components/+apps-helm-charts/helm-charts.tsx index d8cb43a98d..0bf80577bd 100644 --- a/src/renderer/components/+apps-helm-charts/helm-charts.tsx +++ b/src/renderer/components/+apps-helm-charts/helm-charts.tsx @@ -30,7 +30,6 @@ import type { HelmChart } from "../../api/endpoints/helm-charts.api"; import { HelmChartDetails } from "./helm-chart-details"; import { navigation } from "../../navigation"; import { ItemListLayout } from "../item-object-list/item-list-layout"; -import { SearchInputUrl } from "../input"; enum columnId { name = "name", @@ -92,9 +91,12 @@ export class HelmCharts extends Component { (chart: HelmChart) => chart.getAppVersion(), (chart: HelmChart) => chart.getKeywords(), ]} - customizeHeader={() => ( - - )} + customizeHeader={({ searchProps }) => ({ + searchProps: { + ...searchProps, + placeholder: "Search Helm Charts...", + }, + })} renderTableHeader={[ { className: "icon", showWithColumn: columnId.name }, { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, diff --git a/src/renderer/components/+apps-releases/releases.tsx b/src/renderer/components/+apps-releases/releases.tsx index bdc8cf22b6..bb16a4e723 100644 --- a/src/renderer/components/+apps-releases/releases.tsx +++ b/src/renderer/components/+apps-releases/releases.tsx @@ -117,16 +117,20 @@ export class HelmReleases extends Component { (release: HelmRelease) => release.getStatus(), (release: HelmRelease) => release.getVersion(), ]} - renderHeaderTitle="Releases" - customizeHeader={({ filters, ...headerPlaceholders }) => ({ + customizeHeader={({ filters, searchProps, ...headerPlaceholders }) => ({ filters: ( <> {filters} ), + searchProps: { + ...searchProps, + placeholder: "Search Releases...", + }, ...headerPlaceholders, })} + renderHeaderTitle="Releases" renderTableHeader={[ { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index c2d180d2e2..2414d90a49 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -205,7 +205,6 @@ export class Catalog extends React.Component { return ( { return ( { + customizeHeader={({ filters, ...headerPlaceholders }) => { let placeholder = <>All groups; if (selectedGroups.length == 1) placeholder = <>Group: {selectedGroups[0]}; @@ -104,26 +104,30 @@ export class CrdList extends React.Component { return { // todo: move to global filters filters: ( - this.toggleSelection(group)} + closeMenuOnSelect={false} + controlShouldRenderValue={false} + formatOptionLabel={({ value: group }: SelectOption) => { + const isSelected = selectedGroups.includes(group); + + return ( +
+ + {group} + {isSelected && } +
+ ); + }} + /> + + ), + ...headerPlaceholders, }; }} renderTableHeader={[ diff --git a/src/renderer/components/+custom-resources/crd-resources.tsx b/src/renderer/components/+custom-resources/crd-resources.tsx index b42703aa33..76ea8607b5 100644 --- a/src/renderer/components/+custom-resources/crd-resources.tsx +++ b/src/renderer/components/+custom-resources/crd-resources.tsx @@ -101,6 +101,13 @@ export class CrdResources extends React.Component { (item: KubeObject) => item.getSearchFields(), ]} renderHeaderTitle={crd.getResourceTitle()} + customizeHeader={({ searchProps, ...headerPlaceholders }) => ({ + searchProps: { + ...searchProps, + placeholder: `Search ${crd.getResourceTitle()}...`, + }, + ...headerPlaceholders + })} renderTableHeader={[ { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, isNamespaced && { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, diff --git a/src/renderer/components/+events/events.tsx b/src/renderer/components/+events/events.tsx index adf7b2d105..aa0ba475a9 100644 --- a/src/renderer/components/+events/events.tsx +++ b/src/renderer/components/+events/events.tsx @@ -30,7 +30,7 @@ import { EventStore, eventStore } from "./event.store"; import { getDetailsUrl, KubeObjectListLayout, KubeObjectListLayoutProps } from "../kube-object"; import type { KubeEvent } from "../../api/endpoints/events.api"; import type { TableSortCallbacks, TableSortParams, TableProps } from "../table"; -import type { IHeaderPlaceholders } from "../item-object-list"; +import type { HeaderCustomizer } from "../item-object-list"; import { Tooltip } from "../tooltip"; import { Link } from "react-router-dom"; import { cssNames, IClassName, stopPropagation } from "../../utils"; @@ -112,19 +112,21 @@ export class Events extends React.Component { return this.items; } - customizeHeader = ({ info, title }: IHeaderPlaceholders) => { + customizeHeader: HeaderCustomizer = ({ info, title, ...headerPlaceholders }) => { const { compact } = this.props; const { store, items, visibleItems } = this; const allEventsAreShown = visibleItems.length === items.length; // handle "compact"-mode header if (compact) { - if (allEventsAreShown) return title; // title == "Events" + if (allEventsAreShown) { + return { title }; + } - return <> - {title} - ({visibleItems.length} of {items.length}) - ; + return { + title, + info: ({visibleItems.length} of {items.length}), + }; } return { @@ -136,7 +138,9 @@ export class Events extends React.Component { className="help-icon" tooltip={`Limited to ${store.limit}`} /> - + , + title, + ...headerPlaceholders }; }; diff --git a/src/renderer/components/input/search-input-url.tsx b/src/renderer/components/input/search-input-url.tsx index 131f17d383..328f189d6d 100644 --- a/src/renderer/components/input/search-input-url.tsx +++ b/src/renderer/components/input/search-input-url.tsx @@ -32,12 +32,12 @@ export const searchUrlParam = createPageParam({ defaultValue: "", }); -interface Props extends InputProps { +export interface SearchInputUrlProps extends InputProps { compact?: boolean; // show only search-icon when not focused } @observer -export class SearchInputUrl extends React.Component { +export class SearchInputUrl extends React.Component { @observable inputVal = ""; // fix: use empty string on init to avoid react warnings @disposeOnUnmount @@ -62,7 +62,7 @@ export class SearchInputUrl extends React.Component { } }; - constructor(props: Props) { + constructor(props: SearchInputUrlProps) { super(props); makeObservable(this); } diff --git a/src/renderer/components/item-object-list/item-list-layout.tsx b/src/renderer/components/item-object-list/item-list-layout.tsx index 647cbcaec9..72dafbd90e 100644 --- a/src/renderer/components/item-object-list/item-list-layout.tsx +++ b/src/renderer/components/item-object-list/item-list-layout.tsx @@ -32,7 +32,7 @@ import { AddRemoveButtons, AddRemoveButtonsProps } from "../add-remove-buttons"; import { NoItems } from "../no-items"; import { Spinner } from "../spinner"; import type { ItemObject, ItemStore } from "../../item.store"; -import { SearchInputUrl } from "../input"; +import { SearchInputUrlProps, SearchInputUrl } from "../input"; import { Filter, FilterType, pageFilters } from "./page-filters.store"; import { PageFiltersList } from "./page-filters-list"; import { ThemeStore } from "../../theme.store"; @@ -41,21 +41,21 @@ import { MenuItem } from "../menu"; import { Checkbox } from "../checkbox"; import { UserStore } from "../../../common/user-store"; import { namespaceStore } from "../+namespaces/namespace.store"; -import { KubeObjectStore } from "../../kube-object.store"; -import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter"; -// todo: refactor, split to small re-usable components + export type SearchFilter = (item: T) => string | number | (string | number)[]; export type ItemsFilter = (items: T[]) => T[]; -export interface IHeaderPlaceholders { - title: ReactNode; - search: ReactNode; - filters: ReactNode; - info: ReactNode; +export interface HeaderPlaceholders { + title?: ReactNode; + searchProps?: SearchInputUrlProps; + filters?: ReactNode; + info?: ReactNode; } +export type HeaderCustomizer = (placeholders: HeaderPlaceholders) => HeaderPlaceholders; + export interface ItemListLayoutProps { tableId?: string; className: IClassName; @@ -72,12 +72,11 @@ export interface ItemListLayoutProps { showHeader?: boolean; headerClassName?: IClassName; renderHeaderTitle?: ReactNode | ((parent: ItemListLayout) => ReactNode); - customizeHeader?: (placeholders: IHeaderPlaceholders, content: ReactNode) => Partial | ReactNode; + customizeHeader?: HeaderCustomizer | HeaderCustomizer[]; // items list configuration isReady?: boolean; // show loading indicator while not ready isSelectable?: boolean; // show checkbox in rows for selecting items - isSearchable?: boolean; // apply search-filter & add search-input isConfigurable?: boolean; copyClassNameFromHeadCells?: boolean; sortingCallbacks?: { [sortBy: string]: TableSortCallback }; @@ -101,12 +100,13 @@ export interface ItemListLayoutProps { const defaultProps: Partial = { showHeader: true, - isSearchable: true, isSelectable: true, isConfigurable: false, copyClassNameFromHeadCells: true, preloadStores: true, dependentStores: [], + searchFilters: [], + customizeHeader: [], filterItems: [], hasDetailsView: true, onDetails: noop, @@ -160,10 +160,10 @@ export class ItemListLayout extends React.Component { private filterCallbacks: { [type: string]: ItemsFilter } = { [FilterType.SEARCH]: items => { - const { searchFilters, isSearchable } = this.props; + const { searchFilters } = this.props; const search = pageFilters.getValues(FilterType.SEARCH)[0] || ""; - if (search && isSearchable && searchFilters) { + if (search && searchFilters.length) { const normalizeText = (text: string) => String(text).toLowerCase(); const searchTexts = [search].map(normalizeText); @@ -190,9 +190,9 @@ export class ItemListLayout extends React.Component { @computed get filters() { let { activeFilters } = pageFilters; - const { isSearchable, searchFilters } = this.props; + const { searchFilters } = this.props; - if (!(isSearchable && searchFilters)) { + if (searchFilters.length === 0) { activeFilters = activeFilters.filter(({ type }) => type !== FilterType.SEARCH); } @@ -348,18 +348,22 @@ export class ItemListLayout extends React.Component { return this.items.map(item => this.getRow(item.getId())); } - renderHeaderContent(placeholders: IHeaderPlaceholders): ReactNode { - const { isSearchable, searchFilters } = this.props; - const { title, filters, search, info } = placeholders; + renderHeaderContent(placeholders: HeaderPlaceholders): ReactNode { + const { searchFilters } = this.props; + const { title, filters, searchProps, info } = placeholders; return ( <> {title} -
- {info} -
+ { + info && ( +
+ {info} +
+ ) + } {filters} - {isSearchable && searchFilters && search} + {searchFilters.length > 0 && searchProps && } ); } @@ -385,28 +389,15 @@ export class ItemListLayout extends React.Component { return null; } - const showNamespaceSelectFilter = this.props.store instanceof KubeObjectStore && this.props.store.api.isNamespaced; const title = typeof renderHeaderTitle === "function" ? renderHeaderTitle(this) : renderHeaderTitle; - const placeholders: IHeaderPlaceholders = { + const customizeHeaders = [customizeHeader].flat().filter(Boolean); + const initialPlaceholders: HeaderPlaceholders = { title:
{title}
, info: this.renderInfo(), - filters: showNamespaceSelectFilter && , - search: , + searchProps: {}, }; - let header = this.renderHeaderContent(placeholders); - - if (customizeHeader) { - const modifiedHeader = customizeHeader(placeholders, header) ?? {}; - - if (isReactNode(modifiedHeader)) { - header = modifiedHeader; - } else { - header = this.renderHeaderContent({ - ...placeholders, - ...modifiedHeader as IHeaderPlaceholders, - }); - } - } + const headerPlaceholders = customizeHeaders.reduce((prevPlaceholders, customizer) => customizer(prevPlaceholders), initialPlaceholders); + const header = this.renderHeaderContent(headerPlaceholders); return (
diff --git a/src/renderer/components/kube-object/kube-object-list-layout.tsx b/src/renderer/components/kube-object/kube-object-list-layout.tsx index 52c886f6a2..39b0ee5019 100644 --- a/src/renderer/components/kube-object/kube-object-list-layout.tsx +++ b/src/renderer/components/kube-object/kube-object-list-layout.tsx @@ -30,6 +30,8 @@ import { KubeObjectMenu } from "./kube-object-menu"; import { kubeSelectedUrlParam, showDetails } from "./kube-object-details"; import { kubeWatchApi } from "../../api/kube-watch-api"; import { clusterContext } from "../context"; +import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter"; +import { ResourceKindMap, ResourceNames } from "../../utils/rbac"; export interface KubeObjectListLayoutProps extends ItemListLayoutProps { store: KubeObjectStore; @@ -66,7 +68,8 @@ export class KubeObjectListLayout extends React.Component ({ + filters: ( + <> + {filters} + {store.api.isNamespaced && } + + ), + searchProps: { + ...searchProps, + placeholder: `Search ${placeholderString}...`, + }, + ...headerPlaceHolders, + }), + ...[customizeHeader].flat(), + ]} renderItemMenu={(item: KubeObject) => } // safe because we are dealing with KubeObjects here /> ); diff --git a/src/renderer/utils/rbac.ts b/src/renderer/utils/rbac.ts index ca264f050c..da167541fc 100644 --- a/src/renderer/utils/rbac.ts +++ b/src/renderer/utils/rbac.ts @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import type { KubeResource } from "../../common/rbac"; +import { apiResourceRecord, KubeResource } from "../../common/rbac"; export const ResourceNames: Record = { "namespaces": "Namespaces", @@ -53,3 +53,8 @@ export const ResourceNames: Record = { "clusterroles": "Cluster Roles", "serviceaccounts": "Service Accounts" }; + +export const ResourceKindMap: Record = Object.fromEntries( + Object.entries(apiResourceRecord) + .map(([resource, { kind }]) => [kind, resource as KubeResource]) +); From f38b310cb9e84866f72168ce9dd361dd2f8abbdd Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Tue, 8 Jun 2021 18:22:36 +0300 Subject: [PATCH 04/23] Release v5.0.0-beta.7 (#2912) Signed-off-by: Jari Kolehmainen --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3f66175f3e..6ebfa55219 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "OpenLens", "description": "OpenLens - Open Source IDE for Kubernetes", "homepage": "https://github.com/lensapp/lens", - "version": "5.0.0-beta.6", + "version": "5.0.0-beta.7", "main": "static/build/main.js", "copyright": "© 2021 OpenLens Authors", "license": "MIT", From 434e5ff6355b271eb86c78262c9e5cc2cd5b1c51 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 8 Jun 2021 11:23:11 -0400 Subject: [PATCH 05/23] Turn off --strict in verify-docs (#2998) Signed-off-by: Sebastian Malton --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6ebfa55219..49f7cb0653 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "lint": "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", + "verify-docs": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -v ${PWD}:/docs mkdocs-serve-local:latest build", "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 -", From 6044d08a7470fae52ebb9d3b5d6be510c5e4bf94 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 06:49:16 +0300 Subject: [PATCH 06/23] Bump request-promise-native from 1.0.8 to 1.0.9 (#2991) Bumps [request-promise-native](https://github.com/request/request-promise-native) from 1.0.8 to 1.0.9. - [Release notes](https://github.com/request/request-promise-native/releases) - [Commits](https://github.com/request/request-promise-native/compare/v1.0.8...v1.0.9) --- updated-dependencies: - dependency-name: request-promise-native dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 49f7cb0653..22170a5842 100644 --- a/package.json +++ b/package.json @@ -228,7 +228,7 @@ "react-router": "^5.2.0", "readable-stream": "^3.6.0", "request": "^2.88.2", - "request-promise-native": "^1.0.8", + "request-promise-native": "^1.0.9", "semver": "^7.3.2", "serializr": "^2.0.3", "shell-env": "^3.0.1", diff --git a/yarn.lock b/yarn.lock index 78491269c1..729f51115e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12346,19 +12346,19 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request-promise-core@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" - integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ== +request-promise-core@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" + integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== dependencies: - lodash "^4.17.15" + lodash "^4.17.19" -request-promise-native@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" - integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== +request-promise-native@^1.0.8, request-promise-native@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" + integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== dependencies: - request-promise-core "1.1.3" + request-promise-core "1.1.4" stealthy-require "^1.1.1" tough-cookie "^2.3.3" From 6677e22371d0d08aba1756bedac9e986908029c3 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 9 Jun 2021 00:01:26 -0400 Subject: [PATCH 07/23] Remove unnecessary 'onlyVisibleForSource' (#2982) Signed-off-by: Sebastian Malton --- .../catalog-entities/kubernetes-cluster.ts | 10 ++++----- src/common/catalog/catalog-entity.ts | 4 ---- .../+catalog/catalog-entity-drawer-menu.tsx | 21 ++++++++++--------- src/renderer/components/+catalog/catalog.tsx | 4 +--- .../components/hotbar/hotbar-entity-icon.tsx | 2 +- 5 files changed, 17 insertions(+), 24 deletions(-) diff --git a/src/common/catalog-entities/kubernetes-cluster.ts b/src/common/catalog-entities/kubernetes-cluster.ts index 67d82e3501..2781c5dfb0 100644 --- a/src/common/catalog-entities/kubernetes-cluster.ts +++ b/src/common/catalog-entities/kubernetes-cluster.ts @@ -101,20 +101,18 @@ export class KubernetesCluster extends CatalogEntity context.navigate(`/entity/${this.metadata.uid}/settings`) - }, - ]; + }); + } if (this.metadata.labels["file"]?.startsWith(ClusterStore.storedKubeConfigFolder)) { context.menuItems.push({ title: "Delete", icon: "delete", - onlyVisibleForSource: "local", onClick: async () => ClusterStore.getInstance().removeById(this.metadata.uid), confirm: { message: `Remove Kubernetes Cluster "${this.metadata.name} from ${productName}?` diff --git a/src/common/catalog/catalog-entity.ts b/src/common/catalog/catalog-entity.ts index 6c58497204..9acc2a47c9 100644 --- a/src/common/catalog/catalog-entity.ts +++ b/src/common/catalog/catalog-entity.ts @@ -104,10 +104,6 @@ export interface CatalogEntityContextMenu { * Menu icon */ icon?: string; - /** - * Show only if empty or if value matches with entity.metadata.source - */ - onlyVisibleForSource?: string; /** * OnClick handler */ diff --git a/src/renderer/components/+catalog/catalog-entity-drawer-menu.tsx b/src/renderer/components/+catalog/catalog-entity-drawer-menu.tsx index 77c9df5d47..f7aacba970 100644 --- a/src/renderer/components/+catalog/catalog-entity-drawer-menu.tsx +++ b/src/renderer/components/+catalog/catalog-entity-drawer-menu.tsx @@ -78,23 +78,24 @@ export class CatalogEntityDrawerMenu extends React.Comp return []; } - const menuItems = this.contextMenu.menuItems.filter((menuItem) => { - return menuItem.icon && !menuItem.onlyVisibleForSource || menuItem.onlyVisibleForSource === entity.metadata.source; - }); + const items: React.ReactChild[] = []; - const items = menuItems.map((menuItem, index) => { - const props = menuItem.icon.includes(" this.onMenuItemClick(menuItem)}> + const key = menuItem.icon.includes(" this.onMenuItemClick(menuItem)}> ); - - }); + } items.unshift( this.addToHotbar(entity) }> diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index 2414d90a49..b01ad3010a 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -171,12 +171,10 @@ export class Catalog extends React.Component { } renderItemMenu = (item: CatalogEntityItem) => { - const menuItems = this.contextMenu.menuItems.filter((menuItem) => !menuItem.onlyVisibleForSource || menuItem.onlyVisibleForSource === item.entity.metadata.source); - return ( item.onContextMenuOpen(this.contextMenu)}> { - menuItems.map((menuItem, index) => ( + this.contextMenu.menuItems.map((menuItem, index) => ( this.onMenuItemClick(menuItem)}> {menuItem.title} diff --git a/src/renderer/components/hotbar/hotbar-entity-icon.tsx b/src/renderer/components/hotbar/hotbar-entity-icon.tsx index f346727523..f1e22d2e79 100644 --- a/src/renderer/components/hotbar/hotbar-entity-icon.tsx +++ b/src/renderer/components/hotbar/hotbar-entity-icon.tsx @@ -106,7 +106,7 @@ export class HotbarEntityIcon extends React.Component { }; const isActive = this.isActive(entity); const isPersisted = this.isPersisted(entity); - const menuItems = this.contextMenu?.menuItems.filter((menuItem) => !menuItem.onlyVisibleForSource || menuItem.onlyVisibleForSource === entity.metadata.source); + const menuItems = this.contextMenu?.menuItems ?? []; if (!isPersisted) { menuItems.unshift({ From 839fe0ac188c75722ed4fe4bb85cf74166eea88f Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 9 Jun 2021 00:01:53 -0400 Subject: [PATCH 08/23] Fix tag-release script (#2983) Signed-off-by: Sebastian Malton --- scripts/tag-release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tag-release.sh b/scripts/tag-release.sh index 3a32bd3189..82b7f14e6c 100755 --- a/scripts/tag-release.sh +++ b/scripts/tag-release.sh @@ -1,6 +1,6 @@ #!/bin/bash -if [[ ${git branch --show-current} =~ ^release/v ]] +if [[ `git branch --show-current` =~ ^release/v ]] then VERSION_STRING=$(cat package.json | jq '.version' -r | xargs printf "v%s") git tag ${VERSION_STRING} From b7e3868f1e76eac725bc0207b35afe35ae896a2f Mon Sep 17 00:00:00 2001 From: steve richards Date: Wed, 9 Jun 2021 07:09:45 +0100 Subject: [PATCH 09/23] Add new temp workflow to help with docs sync (#2897) Signed-off-by: Steve Richards --- .github/workflows/mkdocs-manual.yml | 58 +++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .github/workflows/mkdocs-manual.yml diff --git a/.github/workflows/mkdocs-manual.yml b/.github/workflows/mkdocs-manual.yml new file mode 100644 index 0000000000..29e359a02c --- /dev/null +++ b/.github/workflows/mkdocs-manual.yml @@ -0,0 +1,58 @@ +name: Manual update to sync Version with Master +on: + workflow_dispatch: + inputs: + version: + description: 'Version string to use (e.g."v0.0.1")' + required: true +jobs: + build: + name: Manual update to sync Tag with Master + runs-on: ubuntu-latest + steps: + - name: Set up Python 3.7 + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install git+https://${{ secrets.GH_TOKEN }}@github.com/lensapp/mkdocs-material-insiders.git + pip install mike + + - name: Checkout Version from lens + uses: actions/checkout@v2 + with: + fetch-depth: 0 + ref: '${{ github.event.inputs.version }}' + + - name: Generate Extensions API Reference using typedocs + run: | + yarn install + yarn typedocs-extensions-api + + - name: Checkout master branch from lens + uses: actions/checkout@v2 + with: + path: 'master' + ref: 'master' + + - name: Bring in latest mkdocs.yml from master + run: | + cp -p ./master/mkdocs.yml . + rm -fr ./docs/clusters ./docs/contributing ./docs/faq ./docs/getting-started ./docs/helm ./docs/support ./docs/supporting + sed -i '/Protocol Handlers/d' ./mkdocs.yml + sed -i '/IPC/d' ./mkdocs.yml + sed -i 's#../../clusters/adding-clusters.md#https://docs.k8slens.dev/latest/clusters/adding-clusters/#g' ./docs/extensions/get-started/your-first-extension.md + sed -i 's#clusters/adding-clusters.md#https://docs.k8slens.dev/latest/clusters/adding-clusters/#g' ./docs/README.md + sed -i 's#../../contributing/README.md#https://docs.k8slens.dev/latest/contributing/#g' ./docs/extensions/guides/generator.md + + - name: git config + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + + - name: mkdocs deploy new release + run: | + mike deploy --push --force From 78427e64e837b122bacbbf756454f12bd4047c94 Mon Sep 17 00:00:00 2001 From: steve richards Date: Wed, 9 Jun 2021 13:23:37 +0100 Subject: [PATCH 10/23] Amend the docs nav menu (#2896) * Amend the docs nav menu Moved all the nav items up one layer Signed-off-by: Steve Richards * updated site title Signed-off-by: Steve Richards --- mkdocs.yml | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index eea8ffc5ea..7ef283c348 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,5 @@ -site_name: Lens Extension API -site_description: Documentation for Lens Extension API. +site_name: Lens Extension Development +site_description: Documentation for Lens Extension Development and API. site_author: Mirantis, Inc. site_url: https://api-docs.k8slens.dev docs_dir: docs/ @@ -12,28 +12,27 @@ google_analytics: - auto nav: - Overview: README.md - - Extension Development: - - Getting Started: - - Overview: extensions/get-started/overview.md - - Your First Extension: extensions/get-started/your-first-extension.md - - Extension Anatomy: extensions/get-started/anatomy.md - - Wrapping Up: extensions/get-started/wrapping-up.md - - Extension Capabilities: - - Common Capabilities: extensions/capabilities/common-capabilities.md - - Styling: extensions/capabilities/styling.md - - Extension Guides: - - Overview: extensions/guides/README.md - - Generator: extensions/guides/generator.md - - Main Extension: extensions/guides/main-extension.md - - Renderer Extension: extensions/guides/renderer-extension.md - - Stores: extensions/guides/stores.md - - Working with MobX: extensions/guides/working-with-mobx.md - - Protocol Handlers: extensions/guides/protocol-handlers.md - - IPC: extensions/guides/ipc.md - - Testing and Publishing: - - Testing Extensions: extensions/testing-and-publishing/testing.md - - Publishing Extensions: extensions/testing-and-publishing/publishing.md - - API Reference: extensions/api/README.md + - Getting Started: + - Overview: extensions/get-started/overview.md + - Your First Extension: extensions/get-started/your-first-extension.md + - Extension Anatomy: extensions/get-started/anatomy.md + - Wrapping Up: extensions/get-started/wrapping-up.md + - Extension Capabilities: + - Common Capabilities: extensions/capabilities/common-capabilities.md + - Styling: extensions/capabilities/styling.md + - Extension Guides: + - Overview: extensions/guides/README.md + - Generator: extensions/guides/generator.md + - Main Extension: extensions/guides/main-extension.md + - Renderer Extension: extensions/guides/renderer-extension.md + - Stores: extensions/guides/stores.md + - Working with MobX: extensions/guides/working-with-mobx.md + - Protocol Handlers: extensions/guides/protocol-handlers.md + - IPC: extensions/guides/ipc.md + - Testing and Publishing: + - Testing Extensions: extensions/testing-and-publishing/testing.md + - Publishing Extensions: extensions/testing-and-publishing/publishing.md + - API Reference: extensions/api/README.md theme: name: 'material' highlightjs: true From c20f30145b2bbccc657c33df4896b4a64533e892 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 08:23:42 -0400 Subject: [PATCH 11/23] Bump @testing-library/jest-dom from 5.11.10 to 5.13.0 (#3002) Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.11.10 to 5.13.0. - [Release notes](https://github.com/testing-library/jest-dom/releases) - [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/jest-dom/compare/v5.11.10...v5.13.0) --- updated-dependencies: - dependency-name: "@testing-library/jest-dom" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 22170a5842..b17a14815d 100644 --- a/package.json +++ b/package.json @@ -249,7 +249,7 @@ "@material-ui/icons": "^4.11.2", "@material-ui/lab": "^4.0.0-alpha.57", "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3", - "@testing-library/jest-dom": "^5.11.10", + "@testing-library/jest-dom": "^5.13.0", "@testing-library/react": "^11.2.6", "@types/byline": "^4.2.32", "@types/chart.js": "^2.9.21", diff --git a/yarn.lock b/yarn.lock index 729f51115e..9c4493bef6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1013,10 +1013,10 @@ lz-string "^1.4.4" pretty-format "^26.6.2" -"@testing-library/jest-dom@^5.11.10": - version "5.11.10" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.11.10.tgz#1cd90715023e1627f5ed26ab3b38e6f22d77046c" - integrity sha512-FuKiq5xuk44Fqm0000Z9w0hjOdwZRNzgx7xGGxQYepWFZy+OYUMOT/wPI4nLYXCaVltNVpU1W/qmD88wLWDsqQ== +"@testing-library/jest-dom@^5.13.0": + version "5.13.0" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.13.0.tgz#0a365684e2c1159f857f5915be50089fc5657df0" + integrity sha512-+jXXTn8GjRnZkJfzG/tqK/2Q7dGlBInR412WE7Aml7CT3wdSpx5dMQC0HOwVQoZ3cNTmQUy8fCVGUV/Zhoyvcw== dependencies: "@babel/runtime" "^7.9.2" "@types/testing-library__jest-dom" "^5.9.1" From 5a01ef08b1aae1c059380fa6dc107877a310b34e Mon Sep 17 00:00:00 2001 From: steve richards Date: Wed, 9 Jun 2021 13:44:54 +0100 Subject: [PATCH 12/23] Update job to use node version 12 (#3011) Signed-off-by: Steve Richards --- .github/workflows/mkdocs-manual.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/mkdocs-manual.yml b/.github/workflows/mkdocs-manual.yml index 29e359a02c..f3f1f11fd0 100644 --- a/.github/workflows/mkdocs-manual.yml +++ b/.github/workflows/mkdocs-manual.yml @@ -9,6 +9,9 @@ jobs: build: name: Manual update to sync Tag with Master runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] steps: - name: Set up Python 3.7 uses: actions/setup-python@v2 @@ -26,6 +29,11 @@ jobs: with: fetch-depth: 0 ref: '${{ github.event.inputs.version }}' + + - name: Using Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} - name: Generate Extensions API Reference using typedocs run: | From 16be9215e2ef0182fddbb25c707b6d0778bd6f9a Mon Sep 17 00:00:00 2001 From: steve richards Date: Wed, 9 Jun 2021 13:58:38 +0100 Subject: [PATCH 13/23] Update job to add in version of docs to be deployed by mike (#3012) Signed-off-by: Steve Richards --- .github/workflows/mkdocs-manual.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mkdocs-manual.yml b/.github/workflows/mkdocs-manual.yml index f3f1f11fd0..11a69b364e 100644 --- a/.github/workflows/mkdocs-manual.yml +++ b/.github/workflows/mkdocs-manual.yml @@ -63,4 +63,4 @@ jobs: - name: mkdocs deploy new release run: | - mike deploy --push --force + mike deploy --push --force ${{ github.event.inputs.version }} From f11fb118452c6361eb05935df13efd3c5cfb457e Mon Sep 17 00:00:00 2001 From: steve richards Date: Wed, 9 Jun 2021 14:22:29 +0100 Subject: [PATCH 14/23] Update job to change title and sync css from master branch (#3013) Signed-off-by: Steve Richards --- .github/workflows/mkdocs-manual.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/mkdocs-manual.yml b/.github/workflows/mkdocs-manual.yml index 11a69b364e..632fccfa7b 100644 --- a/.github/workflows/mkdocs-manual.yml +++ b/.github/workflows/mkdocs-manual.yml @@ -1,4 +1,4 @@ -name: Manual update to sync Version with Master +name: Manual documentation update to sync a deployed Version with Master branch on: workflow_dispatch: inputs: @@ -7,7 +7,7 @@ on: required: true jobs: build: - name: Manual update to sync Tag with Master + name: Manual documentation update to sync a deployed Version with Master branch runs-on: ubuntu-latest strategy: matrix: @@ -49,6 +49,7 @@ jobs: - name: Bring in latest mkdocs.yml from master run: | cp -p ./master/mkdocs.yml . + cp -p ./master/docs/stylesheets/extra.css ./docs/stylesheets/extra.css rm -fr ./docs/clusters ./docs/contributing ./docs/faq ./docs/getting-started ./docs/helm ./docs/support ./docs/supporting sed -i '/Protocol Handlers/d' ./mkdocs.yml sed -i '/IPC/d' ./mkdocs.yml From 512946a397da63434ce84e0573a3d5291199d5bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luca=20Kr=C3=B6ger?= Date: Wed, 9 Jun 2021 15:23:48 +0200 Subject: [PATCH 15/23] Allow to close tabs using middle-clicks (#2995) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Luca Kröger --- src/renderer/components/dock/dock-tab.tsx | 6 ++-- src/renderer/utils/index.ts | 1 + src/renderer/utils/isMiddleClick.ts | 36 +++++++++++++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 src/renderer/utils/isMiddleClick.ts diff --git a/src/renderer/components/dock/dock-tab.tsx b/src/renderer/components/dock/dock-tab.tsx index 4160073882..bf3fea3016 100644 --- a/src/renderer/components/dock/dock-tab.tsx +++ b/src/renderer/components/dock/dock-tab.tsx @@ -23,7 +23,7 @@ import "./dock-tab.scss"; import React from "react"; import { observer } from "mobx-react"; -import { boundMethod, cssNames, prevDefault } from "../../utils"; +import { boundMethod, cssNames, prevDefault, isMiddleClick } from "../../utils"; import { dockStore, IDockTab } from "./dock.store"; import { Tab, TabProps } from "../tabs"; import { Icon } from "../icon"; @@ -88,13 +88,13 @@ export class DockTab extends React.Component { const { className, moreActions, ...tabProps } = this.props; const { title, pinned } = tabProps.value; const label = ( -
+
{title} {moreActions} {!pinned && ( )} diff --git a/src/renderer/utils/index.ts b/src/renderer/utils/index.ts index 3933ff94b6..a0b20d1e2d 100755 --- a/src/renderer/utils/index.ts +++ b/src/renderer/utils/index.ts @@ -38,3 +38,4 @@ export * from "./convertMemory"; export * from "./convertCpu"; export * from "./metricUnitsToNumber"; export * from "./display-booleans"; +export * from "./isMiddleClick"; diff --git a/src/renderer/utils/isMiddleClick.ts b/src/renderer/utils/isMiddleClick.ts new file mode 100644 index 0000000000..1fcb3ccff8 --- /dev/null +++ b/src/renderer/utils/isMiddleClick.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import type React from "react"; + +// Helper for inlining middleClick checks +//
console.log('do some action'))}> +// +// +//
+ +export function isMiddleClick(callback: (evt: E) => any) { + return function (evt: E) { + if(evt.button === 1) { + return callback(evt); + } + }; +} From d6e72ddc1c1d1cb9f513c2ec6539020bb4e5910e Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Wed, 9 Jun 2021 16:24:42 +0300 Subject: [PATCH 16/23] Clean up empty pod log entries (#3010) Signed-off-by: Alex Andreev --- src/renderer/components/dock/log.store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/components/dock/log.store.ts b/src/renderer/components/dock/log.store.ts index 1010c018cf..91682e8c0c 100644 --- a/src/renderer/components/dock/log.store.ts +++ b/src/renderer/components/dock/log.store.ts @@ -107,7 +107,7 @@ export class LogStore { }); // Add newly received logs to bottom - this.podLogs.set(tabId, [...oldLogs, ...logs]); + this.podLogs.set(tabId, [...oldLogs, ...logs.filter(Boolean)]); } catch (error) { this.handlerError(tabId, error); } From 3847a917586c478b04e7cff1134f627dd0337abb Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Wed, 9 Jun 2021 16:24:58 +0300 Subject: [PATCH 17/23] Initial support for validating extension engines.lens version (#2884) --- src/common/vars.ts | 3 ++ .../__tests__/extension-discovery.test.ts | 1 + .../__tests__/lens-extension.test.ts | 3 +- src/extensions/extension-discovery.ts | 16 ++++-- src/extensions/extension-installer.ts | 9 +--- src/extensions/extension-loader.ts | 2 +- src/extensions/lens-extension.ts | 7 ++- .../__tests__/page-registry.test.ts | 6 ++- .../protocol-handler/__test__/router.test.ts | 4 ++ .../+extensions/__tests__/extensions.test.tsx | 3 +- .../installed-extensions.module.css | 6 ++- .../+extensions/installed-extensions.tsx | 53 +++++++++++-------- 12 files changed, 70 insertions(+), 43 deletions(-) diff --git a/src/common/vars.ts b/src/common/vars.ts index b8aa69e4d5..a49c8da910 100644 --- a/src/common/vars.ts +++ b/src/common/vars.ts @@ -21,6 +21,7 @@ // App's common configuration for any process (main, renderer, build pipeline, etc.) import path from "path"; +import { SemVer } from "semver"; import packageInfo from "../../package.json"; import { defineGlobal } from "./utils/defineGlobal"; @@ -66,4 +67,6 @@ export const apiKubePrefix = "/api-kube" as string; // k8s cluster apis export const issuesTrackerUrl = "https://github.com/lensapp/lens/issues" as string; export const slackUrl = "https://join.slack.com/t/k8slens/shared_invite/enQtOTc5NjAyNjYyOTk4LWU1NDQ0ZGFkOWJkNTRhYTc2YjVmZDdkM2FkNGM5MjhiYTRhMDU2NDQ1MzIyMDA4ZGZlNmExOTc0N2JmY2M3ZGI" as string; export const supportUrl = "https://docs.k8slens.dev/latest/support/" as string; + +export const appSemVer = new SemVer(packageInfo.version); export const docsUrl = `https://docs.k8slens.dev/main/` as string; diff --git a/src/extensions/__tests__/extension-discovery.test.ts b/src/extensions/__tests__/extension-discovery.test.ts index 5663a1c13a..347487e556 100644 --- a/src/extensions/__tests__/extension-discovery.test.ts +++ b/src/extensions/__tests__/extension-discovery.test.ts @@ -93,6 +93,7 @@ describe("ExtensionDiscovery", () => { id: path.normalize("node_modules/my-extension/package.json"), isBundled: false, isEnabled: false, + isCompatible: false, manifest: { name: "my-extension", }, diff --git a/src/extensions/__tests__/lens-extension.test.ts b/src/extensions/__tests__/lens-extension.test.ts index 57ae8b8a68..c9c52d1637 100644 --- a/src/extensions/__tests__/lens-extension.test.ts +++ b/src/extensions/__tests__/lens-extension.test.ts @@ -38,7 +38,8 @@ describe("lens extension", () => { absolutePath: "/absolute/fake/", manifestPath: "/this/is/fake/package.json", isBundled: false, - isEnabled: true + isEnabled: true, + isCompatible: true }); }); diff --git a/src/extensions/extension-discovery.ts b/src/extensions/extension-discovery.ts index 829e8a617f..bfe8dc1536 100644 --- a/src/extensions/extension-discovery.ts +++ b/src/extensions/extension-discovery.ts @@ -30,10 +30,13 @@ import { broadcastMessage, handleRequest, requestMain, subscribeToBroadcast } fr import { Singleton, toJS } from "../common/utils"; import logger from "../main/logger"; import { ExtensionInstallationStateStore } from "../renderer/components/+extensions/extension-install.store"; -import { extensionInstaller, PackageJson } from "./extension-installer"; +import { extensionInstaller } from "./extension-installer"; import { ExtensionsStore } from "./extensions-store"; import { ExtensionLoader } from "./extension-loader"; import type { LensExtensionId, LensExtensionManifest } from "./lens-extension"; +import type { PackageJson } from "type-fest"; +import semver from "semver"; +import { appSemVer } from "../common/vars"; import { isProduction } from "../common/vars"; export interface InstalledExtension { @@ -48,6 +51,7 @@ export interface InstalledExtension { // Absolute to the symlinked package.json file readonly manifestPath: string; readonly isBundled: boolean; // defined in project root's package.json + readonly isCompatible: boolean; isEnabled: boolean; } @@ -349,12 +353,17 @@ export class ExtensionDiscovery extends Singleton { */ protected async getByManifest(manifestPath: string, { isBundled = false } = {}): Promise { try { - const manifest = await fse.readJson(manifestPath); + const manifest = await fse.readJson(manifestPath) as LensExtensionManifest; const installedManifestPath = this.getInstalledManifestPath(manifest.name); const isEnabled = isBundled || ExtensionsStore.getInstance().isEnabled(installedManifestPath); const extensionDir = path.dirname(manifestPath); const npmPackage = path.join(extensionDir, `${manifest.name}-${manifest.version}.tgz`); const absolutePath = (isProduction && await fse.pathExists(npmPackage)) ? npmPackage : extensionDir; + let isCompatible = isBundled; + + if (manifest.engines?.lens) { + isCompatible = semver.satisfies(appSemVer, manifest.engines.lens); + } return { id: installedManifestPath, @@ -362,7 +371,8 @@ export class ExtensionDiscovery extends Singleton { manifestPath: installedManifestPath, manifest, isBundled, - isEnabled + isEnabled, + isCompatible }; } catch (error) { if (error.code === "ENOTDIR") { diff --git a/src/extensions/extension-installer.ts b/src/extensions/extension-installer.ts index 797da91c5f..57e67183bf 100644 --- a/src/extensions/extension-installer.ts +++ b/src/extensions/extension-installer.ts @@ -25,17 +25,10 @@ import fs from "fs-extra"; import path from "path"; import logger from "../main/logger"; import { extensionPackagesRoot } from "./extension-loader"; +import type { PackageJson } from "type-fest"; const logModule = "[EXTENSION-INSTALLER]"; -type Dependencies = { - [name: string]: string; -}; - -// Type for the package.json file that is written by ExtensionInstaller -export type PackageJson = { - dependencies: Dependencies; -}; /** * Installs dependencies for extensions diff --git a/src/extensions/extension-loader.ts b/src/extensions/extension-loader.ts index da2669550f..af85d11e7f 100644 --- a/src/extensions/extension-loader.ts +++ b/src/extensions/extension-loader.ts @@ -301,7 +301,7 @@ export class ExtensionLoader extends Singleton { for (const [extId, extension] of installedExtensions) { const alreadyInit = this.instances.has(extId); - if (extension.isEnabled && !alreadyInit) { + if (extension.isCompatible && extension.isEnabled && !alreadyInit) { try { const LensExtensionClass = this.requireExtension(extension); diff --git a/src/extensions/lens-extension.ts b/src/extensions/lens-extension.ts index f3910308fd..ac77a3b229 100644 --- a/src/extensions/lens-extension.ts +++ b/src/extensions/lens-extension.ts @@ -24,18 +24,17 @@ import { action, observable, makeObservable } from "mobx"; import { FilesystemProvisionerStore } from "../main/extension-filesystem"; import logger from "../main/logger"; import type { ProtocolHandlerRegistration } from "./registries"; +import type { PackageJson } from "type-fest"; import { Disposer, disposer } from "../common/utils"; export type LensExtensionId = string; // path to manifest (package.json) export type LensExtensionConstructor = new (...args: ConstructorParameters) => LensExtension; -export interface LensExtensionManifest { +export interface LensExtensionManifest extends PackageJson { name: string; version: string; - description?: string; main?: string; // path to %ext/dist/main.js renderer?: string; // path to %ext/dist/renderer.js - lens?: object; // fixme: add more required fields for validation } export const Disposers = Symbol(); @@ -91,7 +90,7 @@ export class LensExtension { try { await this.onActivate(); this.isEnabled = true; - + this[Disposers].push(...await register(this)); logger.info(`[EXTENSION]: enabled ${this.name}@${this.version}`); } catch (error) { diff --git a/src/extensions/registries/__tests__/page-registry.test.ts b/src/extensions/registries/__tests__/page-registry.test.ts index 8c6582b437..497d92e723 100644 --- a/src/extensions/registries/__tests__/page-registry.test.ts +++ b/src/extensions/registries/__tests__/page-registry.test.ts @@ -40,7 +40,8 @@ describe("getPageUrl", () => { absolutePath: "/absolute/fake/", manifestPath: "/this/is/fake/package.json", isBundled: false, - isEnabled: true + isEnabled: true, + isCompatible: true }); globalPageRegistry.add({ id: "page-with-params", @@ -107,7 +108,8 @@ describe("globalPageRegistry", () => { absolutePath: "/absolute/fake/", manifestPath: "/this/is/fake/package.json", isBundled: false, - isEnabled: true + isEnabled: true, + isCompatible: true }); globalPageRegistry.add([ { diff --git a/src/main/protocol-handler/__test__/router.test.ts b/src/main/protocol-handler/__test__/router.test.ts index 597be0c124..6c1d9e777a 100644 --- a/src/main/protocol-handler/__test__/router.test.ts +++ b/src/main/protocol-handler/__test__/router.test.ts @@ -87,6 +87,7 @@ describe("protocol router tests", () => { }, isBundled: false, isEnabled: true, + isCompatible: true, absolutePath: "/foo/bar", }); const lpr = LensProtocolRouterMain.getInstance(); @@ -165,6 +166,7 @@ describe("protocol router tests", () => { }, isBundled: false, isEnabled: true, + isCompatible: true, absolutePath: "/foo/bar", }); @@ -206,6 +208,7 @@ describe("protocol router tests", () => { }, isBundled: false, isEnabled: true, + isCompatible: true, absolutePath: "/foo/bar", }); @@ -230,6 +233,7 @@ describe("protocol router tests", () => { }, isBundled: false, isEnabled: true, + isCompatible: true, absolutePath: "/foo/bar", }); diff --git a/src/renderer/components/+extensions/__tests__/extensions.test.tsx b/src/renderer/components/+extensions/__tests__/extensions.test.tsx index e092949ab7..3361362b4b 100644 --- a/src/renderer/components/+extensions/__tests__/extensions.test.tsx +++ b/src/renderer/components/+extensions/__tests__/extensions.test.tsx @@ -76,7 +76,8 @@ describe("Extensions", () => { absolutePath: "/absolute/path", manifestPath: "/symlinked/path/package.json", isBundled: false, - isEnabled: true + isEnabled: true, + isCompatible: true }); }); diff --git a/src/renderer/components/+extensions/installed-extensions.module.css b/src/renderer/components/+extensions/installed-extensions.module.css index 831ece9024..b685f029fe 100644 --- a/src/renderer/components/+extensions/installed-extensions.module.css +++ b/src/renderer/components/+extensions/installed-extensions.module.css @@ -11,6 +11,10 @@ color: var(--colorOk); } +.invalid { + color: var(--colorWarning); +} + .title { margin-bottom: 0!important; } @@ -22,4 +26,4 @@ .frozenRow { @apply opacity-30 pointer-events-none; -} \ No newline at end of file +} diff --git a/src/renderer/components/+extensions/installed-extensions.tsx b/src/renderer/components/+extensions/installed-extensions.tsx index f896f410fd..0da09e5ec9 100644 --- a/src/renderer/components/+extensions/installed-extensions.tsx +++ b/src/renderer/components/+extensions/installed-extensions.tsx @@ -39,14 +39,18 @@ interface Props { uninstall: (extension: InstalledExtension) => void; } -function getStatus(isEnabled: boolean) { - return isEnabled ? "Enabled" : "Disabled"; +function getStatus(extension: InstalledExtension) { + if (!extension.isCompatible) { + return "Incompatible"; + } + + return extension.isEnabled ? "Enabled" : "Disabled"; } export const InstalledExtensions = observer(({ extensions, uninstall, enable, disable }: Props) => { const filters = [ (extension: InstalledExtension) => extension.manifest.name, - (extension: InstalledExtension) => getStatus(extension.isEnabled), + (extension: InstalledExtension) => getStatus(extension), (extension: InstalledExtension) => extension.manifest.version, ]; @@ -87,7 +91,7 @@ export const InstalledExtensions = observer(({ extensions, uninstall, enable, di const data = useMemo( () => { return extensions.map(extension => { - const { id, isEnabled, manifest } = extension; + const { id, isEnabled, isCompatible, manifest } = extension; const { name, description, version } = manifest; const isUninstalling = ExtensionInstallationStateStore.isExtensionUninstalling(id); @@ -102,29 +106,34 @@ export const InstalledExtensions = observer(({ extensions, uninstall, enable, di ), version, status: ( -
- {getStatus(isEnabled)} +
+ {getStatus(extension)}
), actions: ( - {isEnabled ? ( - disable(id)} - > - - Disable - - ) : ( - enable(id)} - > - - Enable - + { isCompatible && ( + <> + {isEnabled ? ( + disable(id)} + > + + Disable + + ) : ( + enable(id)} + > + + Enable + + )} + )} + uninstall(extension)} From 1ed7892b26ce80d56468556bfe32ce1587c00705 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Jun 2021 09:54:34 -0400 Subject: [PATCH 18/23] Bump css-loader from 3.6.0 to 5.2.6 (#2871) Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 3.6.0 to 5.2.6. - [Release notes](https://github.com/webpack-contrib/css-loader/releases) - [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack-contrib/css-loader/compare/v3.6.0...v5.2.6) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 141 +++++++++++++++++++++++++-------------------------- 2 files changed, 70 insertions(+), 73 deletions(-) diff --git a/package.json b/package.json index b17a14815d..d96d65aec7 100644 --- a/package.json +++ b/package.json @@ -312,7 +312,7 @@ "color": "^3.1.2", "concurrently": "^5.2.0", "css-element-queries": "^1.2.3", - "css-loader": "^3.5.3", + "css-loader": "^5.2.6", "deepdash": "^5.3.5", "dompurify": "^2.0.11", "electron": "^9.4.4", diff --git a/yarn.lock b/yarn.lock index 9c4493bef6..f4bc8f613b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4363,24 +4363,21 @@ css-element-queries@^1.2.3: resolved "https://registry.yarnpkg.com/css-element-queries/-/css-element-queries-1.2.3.tgz#e14940b1fcd4bf0da60ea4145d05742d7172e516" integrity sha512-QK9uovYmKTsV2GXWQiMOByVNrLn2qz6m3P7vWpOR4IdD6I3iXoDw5qtgJEN3Xq7gIbdHVKvzHjdAtcl+4Arc4Q== -css-loader@^3.5.3: - version "3.6.0" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645" - integrity sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ== +css-loader@^5.2.6: + version "5.2.6" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.6.tgz#c3c82ab77fea1f360e587d871a6811f4450cc8d1" + integrity sha512-0wyN5vXMQZu6BvjbrPdUJvkCzGEO24HC7IS7nW4llc6BBFC+zwR9CKtYGv63Puzsg10L/o12inMY5/2ByzfD6w== dependencies: - camelcase "^5.3.1" - cssesc "^3.0.0" - icss-utils "^4.1.1" - loader-utils "^1.2.3" - normalize-path "^3.0.0" - postcss "^7.0.32" - postcss-modules-extract-imports "^2.0.0" - postcss-modules-local-by-default "^3.0.2" - postcss-modules-scope "^2.2.0" - postcss-modules-values "^3.0.0" + icss-utils "^5.1.0" + loader-utils "^2.0.0" + postcss "^8.2.15" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" postcss-value-parser "^4.1.0" - schema-utils "^2.7.0" - semver "^6.3.0" + schema-utils "^3.0.0" + semver "^7.3.5" css-parse@^2.0.0, css-parse@~2.0.0: version "2.0.0" @@ -7226,13 +7223,18 @@ icss-utils@^3.0.1: dependencies: postcss "^6.0.2" -icss-utils@^4.0.0, icss-utils@^4.1.1: +icss-utils@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== dependencies: postcss "^7.0.14" +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + identity-obj-proxy@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz#94d2bda96084453ef36fbc5aaec37e0f79f1fc14" @@ -9905,10 +9907,10 @@ nan@^2.12.1, nan@^2.13.2, nan@^2.14.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== -nanoid@^3.1.22: - version "3.1.22" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844" - integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ== +nanoid@^3.1.23: + version "3.1.23" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" + integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== nanomatch@^1.2.9: version "1.2.13" @@ -11372,38 +11374,33 @@ postcss-loader@~3.0.0: postcss-load-config "^2.0.0" schema-utils "^1.0.0" -postcss-modules-extract-imports@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" - integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== - dependencies: - postcss "^7.0.5" +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== -postcss-modules-local-by-default@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" - integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== +postcss-modules-local-by-default@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" + integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== dependencies: - icss-utils "^4.1.1" - postcss "^7.0.32" + icss-utils "^5.0.0" postcss-selector-parser "^6.0.2" postcss-value-parser "^4.1.0" -postcss-modules-scope@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" - integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== - dependencies: - postcss "^7.0.6" - postcss-selector-parser "^6.0.0" - -postcss-modules-values@^3.0.0: +postcss-modules-scope@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" - integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" + integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== dependencies: - icss-utils "^4.0.0" - postcss "^7.0.6" + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" postcss-nested@5.0.5: version "5.0.5" @@ -11412,14 +11409,6 @@ postcss-nested@5.0.5: dependencies: postcss-selector-parser "^6.0.4" -postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.4: - version "6.0.5" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.5.tgz#042d74e137db83e6f294712096cb413f5aa612c4" - integrity sha512-aFYPoYmXbZ1V6HZaSvat08M97A8HqO6Pjz+PiNpw/DhuRrC72XWAdp3hL6wusDCN31sSmcZyMGa2hZEuX+Xfhg== - dependencies: - cssesc "^3.0.0" - util-deprecate "^1.0.2" - postcss-selector-parser@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" @@ -11429,6 +11418,14 @@ postcss-selector-parser@^6.0.2: indexes-of "^1.0.1" uniq "^1.0.1" +postcss-selector-parser@^6.0.4: + version "6.0.5" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.5.tgz#042d74e137db83e6f294712096cb413f5aa612c4" + integrity sha512-aFYPoYmXbZ1V6HZaSvat08M97A8HqO6Pjz+PiNpw/DhuRrC72XWAdp3hL6wusDCN31sSmcZyMGa2hZEuX+Xfhg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + postcss-value-parser@^3.3.0: version "3.3.1" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" @@ -11448,7 +11445,7 @@ postcss@^6.0.14, postcss@^6.0.2, postcss@^6.0.9: source-map "^0.6.1" supports-color "^5.4.0" -postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: +postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.27: version "7.0.35" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24" integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg== @@ -11457,14 +11454,14 @@ postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0. source-map "^0.6.1" supports-color "^6.1.0" -postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.14: - version "8.2.14" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.14.tgz#dcf313eb8247b3ce8078d048c0e8262ca565ad2b" - integrity sha512-+jD0ZijcvyCqPQo/m/CW0UcARpdFylq04of+Q7RKX6f/Tu+dvpUI/9Sp81+i6/vJThnOBX09Quw0ZLOVwpzX3w== +postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.14, postcss@^8.2.15: + version "8.3.0" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.0.tgz#b1a713f6172ca427e3f05ef1303de8b65683325f" + integrity sha512-+ogXpdAjWGa+fdYY5BQ96V/6tAo+TdSSIMP5huJBIygdWwKtVoB5JWZ7yUd4xZ8r+8Kvvx4nyg/PQ071H4UtcQ== dependencies: colorette "^1.2.2" - nanoid "^3.1.22" - source-map "^0.6.1" + nanoid "^3.1.23" + source-map-js "^0.6.2" postinstall-postinstall@^2.1.0: version "2.1.0" @@ -12790,23 +12787,18 @@ semver-diff@^3.1.1: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.x, semver@^7.2.1, semver@^7.3.2: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== - -semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.3.4: +semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" +semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -13169,6 +13161,11 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== +source-map-js@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" + integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== + source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" From a1a1c240e9293070386fd52ef86e0d9994d11c05 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 9 Jun 2021 13:40:55 -0400 Subject: [PATCH 19/23] Split UserManagement into seperate Cluster and Namespace Roles and RoleBindings (#2755) --- src/common/utils/__tests__/hash-set.test.ts | 528 ++++++++++++++++++ src/common/utils/__tests__/n-fircate.test.ts | 55 ++ src/common/utils/hash-set.ts | 260 +++++++++ src/common/utils/index.ts | 5 +- src/common/utils/n-fircate.ts | 52 ++ src/extensions/renderer-api/k8s-api.ts | 6 +- .../api/endpoints/cluster-role-binding.api.ts | 31 +- .../api/endpoints/cluster-role.api.ts | 17 +- .../api/endpoints/role-binding.api.ts | 8 +- .../+namespaces/namespace-select.tsx | 6 +- .../components/+namespaces/namespace.store.ts | 13 +- .../add-role-binding-dialog.tsx | 318 ----------- .../role-bindings.store.ts | 100 ---- .../role-bindings.tsx | 85 --- .../+user-management-roles/roles.store.ts | 75 --- .../+cluster-role-bindings/details.scss} | 0 .../+cluster-role-bindings/details.tsx | 157 ++++++ .../+cluster-role-bindings/dialog.scss | 11 + .../+cluster-role-bindings/dialog.tsx | 286 ++++++++++ .../+cluster-role-bindings/hashers.ts} | 13 +- .../+cluster-role-bindings}/index.ts | 7 +- .../+cluster-role-bindings/store.ts | 63 +++ .../+cluster-role-bindings/view.scss | 11 + .../+cluster-role-bindings/view.tsx | 88 +++ .../+cluster-roles/add-dialog.scss} | 0 .../+cluster-roles/add-dialog.tsx | 105 ++++ .../+cluster-roles/details.scss} | 0 .../+cluster-roles/details.tsx | 104 ++++ .../+cluster-roles}/index.ts | 7 +- .../+user-management/+cluster-roles/store.ts | 44 ++ .../+user-management/+cluster-roles/view.scss | 11 + .../+user-management/+cluster-roles/view.tsx | 80 +++ .../+role-bindings/details.scss | 2 + .../+role-bindings/details.tsx} | 94 ++-- .../+role-bindings/dialog.scss} | 0 .../+role-bindings/dialog.tsx | 303 ++++++++++ .../+role-bindings/hashers.ts | 32 ++ .../+user-management/+role-bindings/index.ts | 23 + .../+user-management/+role-bindings/store.ts | 62 ++ .../+role-bindings/view.scss} | 0 .../+user-management/+role-bindings/view.tsx | 91 +++ .../+user-management/+roles/add-dialog.scss | 5 + .../+roles/add-dialog.tsx} | 41 +- .../+user-management/+roles/details.scss | 21 + .../+roles/details.tsx} | 34 +- .../+user-management/+roles/index.ts | 23 + .../+user-management/+roles/store.ts | 48 ++ .../+roles/view.scss} | 0 .../+roles/view.tsx} | 20 +- .../+service-accounts/create-dialog.scss} | 2 +- .../+service-accounts/create-dialog.tsx} | 43 +- .../+service-accounts/details.scss} | 2 +- .../+service-accounts/details.tsx} | 25 +- .../+service-accounts/index.ts | 23 + .../+service-accounts/secret.scss} | 0 .../+service-accounts/secret.tsx} | 11 +- .../+service-accounts/store.ts} | 8 +- .../+service-accounts/view.scss} | 0 .../+service-accounts/view.tsx} | 28 +- .../+user-management/select-options.tsx | 49 ++ .../+user-management/user-management.route.ts | 37 +- .../+user-management/user-management.tsx | 61 +- src/renderer/components/app.tsx | 2 - .../editable-list/editable-list.scss | 8 +- .../editable-list/editable-list.tsx | 20 +- src/renderer/components/input/input.scss | 30 +- src/renderer/components/input/input.tsx | 45 +- src/renderer/components/select/select.tsx | 14 +- src/renderer/kube-object.store.ts | 44 +- 69 files changed, 2896 insertions(+), 901 deletions(-) create mode 100644 src/common/utils/__tests__/hash-set.test.ts create mode 100644 src/common/utils/__tests__/n-fircate.test.ts create mode 100644 src/common/utils/hash-set.ts create mode 100644 src/common/utils/n-fircate.ts delete mode 100644 src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx delete mode 100644 src/renderer/components/+user-management-roles-bindings/role-bindings.store.ts delete mode 100644 src/renderer/components/+user-management-roles-bindings/role-bindings.tsx delete mode 100644 src/renderer/components/+user-management-roles/roles.store.ts rename src/renderer/components/{+user-management-roles-bindings/role-binding-details.scss => +user-management/+cluster-role-bindings/details.scss} (100%) create mode 100644 src/renderer/components/+user-management/+cluster-role-bindings/details.tsx create mode 100644 src/renderer/components/+user-management/+cluster-role-bindings/dialog.scss create mode 100644 src/renderer/components/+user-management/+cluster-role-bindings/dialog.tsx rename src/renderer/components/{+user-management-service-accounts/index.ts => +user-management/+cluster-role-bindings/hashers.ts} (76%) rename src/renderer/components/{+user-management-roles => +user-management/+cluster-role-bindings}/index.ts (92%) create mode 100644 src/renderer/components/+user-management/+cluster-role-bindings/store.ts create mode 100644 src/renderer/components/+user-management/+cluster-role-bindings/view.scss create mode 100644 src/renderer/components/+user-management/+cluster-role-bindings/view.tsx rename src/renderer/components/{+user-management-roles/add-role-dialog.scss => +user-management/+cluster-roles/add-dialog.scss} (100%) create mode 100644 src/renderer/components/+user-management/+cluster-roles/add-dialog.tsx rename src/renderer/components/{+user-management-roles/role-details.scss => +user-management/+cluster-roles/details.scss} (100%) create mode 100644 src/renderer/components/+user-management/+cluster-roles/details.tsx rename src/renderer/components/{+user-management-roles-bindings => +user-management/+cluster-roles}/index.ts (90%) create mode 100644 src/renderer/components/+user-management/+cluster-roles/store.ts create mode 100644 src/renderer/components/+user-management/+cluster-roles/view.scss create mode 100644 src/renderer/components/+user-management/+cluster-roles/view.tsx create mode 100644 src/renderer/components/+user-management/+role-bindings/details.scss rename src/renderer/components/{+user-management-roles-bindings/role-binding-details.tsx => +user-management/+role-bindings/details.tsx} (59%) rename src/renderer/components/{+user-management-roles-bindings/add-role-binding-dialog.scss => +user-management/+role-bindings/dialog.scss} (100%) create mode 100644 src/renderer/components/+user-management/+role-bindings/dialog.tsx create mode 100644 src/renderer/components/+user-management/+role-bindings/hashers.ts create mode 100644 src/renderer/components/+user-management/+role-bindings/index.ts create mode 100644 src/renderer/components/+user-management/+role-bindings/store.ts rename src/renderer/components/{+user-management-roles-bindings/role-bindings.scss => +user-management/+role-bindings/view.scss} (100%) create mode 100644 src/renderer/components/+user-management/+role-bindings/view.tsx create mode 100644 src/renderer/components/+user-management/+roles/add-dialog.scss rename src/renderer/components/{+user-management-roles/add-role-dialog.tsx => +user-management/+roles/add-dialog.tsx} (79%) create mode 100644 src/renderer/components/+user-management/+roles/details.scss rename src/renderer/components/{+user-management-roles/role-details.tsx => +user-management/+roles/details.tsx} (80%) create mode 100644 src/renderer/components/+user-management/+roles/index.ts create mode 100644 src/renderer/components/+user-management/+roles/store.ts rename src/renderer/components/{+user-management-roles/roles.scss => +user-management/+roles/view.scss} (100%) rename src/renderer/components/{+user-management-roles/roles.tsx => +user-management/+roles/view.tsx} (85%) rename src/renderer/components/{+user-management-service-accounts/create-service-account-dialog.scss => +user-management/+service-accounts/create-dialog.scss} (99%) rename src/renderer/components/{+user-management-service-accounts/create-service-account-dialog.tsx => +user-management/+service-accounts/create-dialog.tsx} (76%) rename src/renderer/components/{+user-management-service-accounts/service-accounts-details.scss => +user-management/+service-accounts/details.scss} (99%) rename src/renderer/components/{+user-management-service-accounts/service-accounts-details.tsx => +user-management/+service-accounts/details.tsx} (88%) create mode 100644 src/renderer/components/+user-management/+service-accounts/index.ts rename src/renderer/components/{+user-management-service-accounts/service-accounts-secret.scss => +user-management/+service-accounts/secret.scss} (100%) rename src/renderer/components/{+user-management-service-accounts/service-accounts-secret.tsx => +user-management/+service-accounts/secret.tsx} (94%) rename src/renderer/components/{+user-management-service-accounts/service-accounts.store.ts => +user-management/+service-accounts/store.ts} (87%) rename src/renderer/components/{+user-management-service-accounts/service-accounts.scss => +user-management/+service-accounts/view.scss} (100%) rename src/renderer/components/{+user-management-service-accounts/service-accounts.tsx => +user-management/+service-accounts/view.tsx} (80%) create mode 100644 src/renderer/components/+user-management/select-options.tsx diff --git a/src/common/utils/__tests__/hash-set.test.ts b/src/common/utils/__tests__/hash-set.test.ts new file mode 100644 index 0000000000..38614b7d3e --- /dev/null +++ b/src/common/utils/__tests__/hash-set.test.ts @@ -0,0 +1,528 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { HashSet, ObservableHashSet } from "../hash-set"; + +describe("ObservableHashSet", () => { + it("should not throw on creation", () => { + expect(() => new ObservableHashSet<{ a: number }>([], item => item.a.toString())).not.toThrowError(); + }); + + it("should be initializable", () => { + const res = new ObservableHashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.size).toBe(4); + }); + + it("has should work as expected", () => { + const res = new ObservableHashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.has({ a: 1 })).toBe(true); + expect(res.has({ a: 5 })).toBe(false); + }); + + it("forEach should work as expected", () => { + const res = new ObservableHashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + let a = 1; + + res.forEach((item) => { + expect(item.a).toEqual(a++); + }); + }); + + it("delete should work as expected", () => { + const res = new ObservableHashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.has({ a: 1 })).toBe(true); + expect(res.delete({ a: 1 })).toBe(true); + expect(res.has({ a: 1 })).toBe(false); + + expect(res.has({ a: 5 })).toBe(false); + expect(res.delete({ a: 5 })).toBe(false); + expect(res.has({ a: 5 })).toBe(false); + }); + + it("toggle should work as expected", () => { + const res = new ObservableHashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.has({ a: 1 })).toBe(true); + res.toggle({ a: 1 }); + expect(res.has({ a: 1 })).toBe(false); + + expect(res.has({ a: 6 })).toBe(false); + res.toggle({ a: 6 }); + expect(res.has({ a: 6 })).toBe(true); + }); + + it("add should work as expected", () => { + const res = new ObservableHashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.has({ a: 6 })).toBe(false); + res.add({ a: 6 }); + expect(res.has({ a: 6 })).toBe(true); + }); + + it("add should treat the hash to be the same as equality", () => { + const res = new ObservableHashSet([ + { a: 1, foobar: "hello" }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.has({ a: 1 })).toBe(true); + res.add({ a: 1, foobar: "goodbye" }); + expect(res.has({ a: 1 })).toBe(true); + }); + + it("clear should work as expected", () => { + const res = new ObservableHashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.size).toBe(4); + res.clear(); + expect(res.size).toBe(0); + }); + + it("replace should work as expected", () => { + const res = new ObservableHashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.size).toBe(4); + res.replace([{ a: 13 }]); + expect(res.size).toBe(1); + expect(res.has({ a: 1 })).toBe(false); + expect(res.has({ a: 13 })).toBe(true); + }); + + it("toJSON should work as expected", () => { + const res = new ObservableHashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.toJSON()).toStrictEqual([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ]); + }); + + it("values should work as expected", () => { + const res = new ObservableHashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + const iter = res.values(); + + expect(iter.next()).toStrictEqual({ + value: { a: 1 }, + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: { a: 2 }, + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: { a: 3 }, + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: { a: 4 }, + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: undefined, + done: true, + }); + }); + + it("keys should work as expected", () => { + const res = new ObservableHashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + const iter = res.keys(); + + expect(iter.next()).toStrictEqual({ + value: { a: 1 }, + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: { a: 2 }, + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: { a: 3 }, + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: { a: 4 }, + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: undefined, + done: true, + }); + }); + + it("entries should work as expected", () => { + const res = new ObservableHashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + const iter = res.entries(); + + expect(iter.next()).toStrictEqual({ + value: [{ a: 1 }, { a: 1 }], + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: [{ a: 2 }, { a: 2 }], + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: [{ a: 3 }, { a: 3 }], + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: [{ a: 4 }, { a: 4 }], + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: undefined, + done: true, + }); + }); +}); + +describe("HashSet", () => { + it("should not throw on creation", () => { + expect(() => new HashSet<{ a: number }>([], item => item.a.toString())).not.toThrowError(); + }); + + it("should be initializable", () => { + const res = new HashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.size).toBe(4); + }); + + it("has should work as expected", () => { + const res = new HashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.has({ a: 1 })).toBe(true); + expect(res.has({ a: 5 })).toBe(false); + }); + + it("forEach should work as expected", () => { + const res = new HashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + let a = 1; + + res.forEach((item) => { + expect(item.a).toEqual(a++); + }); + }); + + it("delete should work as expected", () => { + const res = new HashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.has({ a: 1 })).toBe(true); + expect(res.delete({ a: 1 })).toBe(true); + expect(res.has({ a: 1 })).toBe(false); + + expect(res.has({ a: 5 })).toBe(false); + expect(res.delete({ a: 5 })).toBe(false); + expect(res.has({ a: 5 })).toBe(false); + }); + + it("toggle should work as expected", () => { + const res = new HashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.has({ a: 1 })).toBe(true); + res.toggle({ a: 1 }); + expect(res.has({ a: 1 })).toBe(false); + + expect(res.has({ a: 6 })).toBe(false); + res.toggle({ a: 6 }); + expect(res.has({ a: 6 })).toBe(true); + }); + + it("add should work as expected", () => { + const res = new HashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.has({ a: 6 })).toBe(false); + res.add({ a: 6 }); + expect(res.has({ a: 6 })).toBe(true); + }); + + it("add should treat the hash to be the same as equality", () => { + const res = new HashSet([ + { a: 1, foobar: "hello" }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.has({ a: 1 })).toBe(true); + res.add({ a: 1, foobar: "goodbye" }); + expect(res.has({ a: 1 })).toBe(true); + }); + + it("clear should work as expected", () => { + const res = new HashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.size).toBe(4); + res.clear(); + expect(res.size).toBe(0); + }); + + it("replace should work as expected", () => { + const res = new HashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.size).toBe(4); + res.replace([{ a: 13 }]); + expect(res.size).toBe(1); + expect(res.has({ a: 1 })).toBe(false); + expect(res.has({ a: 13 })).toBe(true); + }); + + it("toJSON should work as expected", () => { + const res = new HashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + + expect(res.toJSON()).toStrictEqual([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ]); + }); + + it("values should work as expected", () => { + const res = new HashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + const iter = res.values(); + + expect(iter.next()).toStrictEqual({ + value: { a: 1 }, + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: { a: 2 }, + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: { a: 3 }, + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: { a: 4 }, + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: undefined, + done: true, + }); + }); + + it("keys should work as expected", () => { + const res = new HashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + const iter = res.keys(); + + expect(iter.next()).toStrictEqual({ + value: { a: 1 }, + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: { a: 2 }, + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: { a: 3 }, + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: { a: 4 }, + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: undefined, + done: true, + }); + }); + + it("entries should work as expected", () => { + const res = new HashSet([ + { a: 1 }, + { a: 2 }, + { a: 3 }, + { a: 4 }, + ], item => item.a.toString()); + const iter = res.entries(); + + expect(iter.next()).toStrictEqual({ + value: [{ a: 1 }, { a: 1 }], + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: [{ a: 2 }, { a: 2 }], + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: [{ a: 3 }, { a: 3 }], + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: [{ a: 4 }, { a: 4 }], + done: false, + }); + + expect(iter.next()).toStrictEqual({ + value: undefined, + done: true, + }); + }); +}); diff --git a/src/common/utils/__tests__/n-fircate.test.ts b/src/common/utils/__tests__/n-fircate.test.ts new file mode 100644 index 0000000000..ad502d05e4 --- /dev/null +++ b/src/common/utils/__tests__/n-fircate.test.ts @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { nFircate } from "../n-fircate"; + +describe("nFircate", () => { + it("should produce an empty array if no parts are provided", () => { + expect(nFircate([{ a: 1 }, { a: 2 }], "a", []).length).toBe(0); + }); + + it("should ignore non-matching parts", () => { + const res = nFircate([{ a: 1 }, { a: 2 }], "a", [1]); + + expect(res.length).toBe(1); + expect(res[0].length).toBe(1); + }); + + it("should include all matching parts in each type", () => { + const res = nFircate([{ a: 1, b: "a" }, { a: 2, b: "b" }, { a: 1, b: "c" }], "a", [1, 2]); + + expect(res.length).toBe(2); + expect(res[0].length).toBe(2); + expect(res[0][0].b).toBe("a"); + expect(res[0][1].b).toBe("c"); + expect(res[1].length).toBe(1); + expect(res[1][0].b).toBe("b"); + }); + + it("should throw a type error if the same part is provided more than once", () => { + try { + nFircate([{ a: 1, b: "a" }, { a: 2, b: "b" }, { a: 1, b: "c" }], "a", [1, 2, 1]); + fail("Expected error"); + } catch (error) { + expect(error).toBeInstanceOf(TypeError); + } + }); +}); diff --git a/src/common/utils/hash-set.ts b/src/common/utils/hash-set.ts new file mode 100644 index 0000000000..87fa8daa4c --- /dev/null +++ b/src/common/utils/hash-set.ts @@ -0,0 +1,260 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { action, IInterceptable, IInterceptor, IListenable, ISetWillChange, observable, ObservableMap, ObservableSet } from "mobx"; + +export function makeIterableIterator(iterator: Iterator): IterableIterator { + (iterator as IterableIterator)[Symbol.iterator] = () => iterator as IterableIterator; + + return iterator as IterableIterator; +} + +export class HashSet implements Set { + #hashmap: Map; + + constructor(initialValues: Iterable, protected hasher: (item: T) => string) { + this.#hashmap = new Map(Array.from(initialValues, value => [this.hasher(value), value])); + } + + replace(other: ObservableHashSet | ObservableSet | Set | readonly T[]): this { + if (other === null || other === undefined) { + return this; + } + + if (!(Array.isArray(other) || other instanceof Set || other instanceof ObservableHashSet || other instanceof ObservableSet)) { + throw new Error(`ObservableHashSet: Cannot initialize set from ${other}`); + } + + this.clear(); + + for (const value of other) { + this.add(value); + } + + return this; + } + + clear(): void { + this.#hashmap.clear(); + } + + add(value: T): this { + this.#hashmap.set(this.hasher(value), value); + + return this; + } + + toggle(value: T): void { + const hash = this.hasher(value); + + if (this.#hashmap.has(hash)) { + this.#hashmap.delete(hash); + } else { + this.#hashmap.set(hash, value); + } + } + + delete(value: T): boolean { + return this.#hashmap.delete(this.hasher(value)); + } + + forEach(callbackfn: (value: T, key: T, set: Set) => void, thisArg?: any): void { + this.#hashmap.forEach(value => callbackfn(value, value, thisArg ?? this)); + } + + has(value: T): boolean { + return this.#hashmap.has(this.hasher(value)); + } + + get size(): number { + return this.#hashmap.size; + } + + entries(): IterableIterator<[T, T]> { + let nextIndex = 0; + const keys = Array.from(this.keys()); + const values = Array.from(this.values()); + + return makeIterableIterator<[T, T]>({ + next() { + const index = nextIndex++; + + return index < values.length + ? { value: [keys[index], values[index]], done: false } + : { done: true, value: undefined }; + } + }); + } + + keys(): IterableIterator { + return this.values(); + } + + values(): IterableIterator { + let nextIndex = 0; + const observableValues = Array.from(this.#hashmap.values()); + + return makeIterableIterator({ + next: () => { + return nextIndex < observableValues.length + ? { value: observableValues[nextIndex++], done: false } + : { done: true, value: undefined }; + } + }); + } + + [Symbol.iterator](): IterableIterator { + return this.#hashmap.values(); + } + + get [Symbol.toStringTag](): string { + return "Set"; + } + + toJSON(): T[] { + return Array.from(this); + } + + toString(): string { + return "[object Set]"; + } +} + +export class ObservableHashSet implements Set, IInterceptable, IListenable { + #hashmap: ObservableMap; + + get interceptors_(): IInterceptor>[] { + return []; + } + + get changeListeners_(): Function[] { + return []; + } + + constructor(initialValues: Iterable, protected hasher: (item: T) => string) { + this.#hashmap = observable.map(Array.from(initialValues, value => [this.hasher(value), value]), undefined); + } + + @action + replace(other: ObservableHashSet | ObservableSet | Set | readonly T[]): this { + if (other === null || other === undefined) { + return this; + } + + if (!(Array.isArray(other) || other instanceof Set || other instanceof ObservableHashSet || other instanceof ObservableSet)) { + throw new Error(`ObservableHashSet: Cannot initialize set from ${other}`); + } + + this.clear(); + + for (const value of other) { + this.add(value); + } + + return this; + } + + clear(): void { + this.#hashmap.clear(); + } + + add(value: T): this { + this.#hashmap.set(this.hasher(value), value); + + return this; + } + + @action + toggle(value: T): void { + const hash = this.hasher(value); + + if (this.#hashmap.has(hash)) { + this.#hashmap.delete(hash); + } else { + this.#hashmap.set(hash, value); + } + } + + delete(value: T): boolean { + return this.#hashmap.delete(this.hasher(value)); + } + + forEach(callbackfn: (value: T, key: T, set: Set) => void, thisArg?: any): void { + this.#hashmap.forEach(value => callbackfn(value, value, thisArg ?? this)); + } + + has(value: T): boolean { + return this.#hashmap.has(this.hasher(value)); + } + + get size(): number { + return this.#hashmap.size; + } + + entries(): IterableIterator<[T, T]> { + let nextIndex = 0; + const keys = Array.from(this.keys()); + const values = Array.from(this.values()); + + return makeIterableIterator<[T, T]>({ + next() { + const index = nextIndex++; + + return index < values.length + ? { value: [keys[index], values[index]], done: false } + : { done: true, value: undefined }; + } + }); + } + + keys(): IterableIterator { + return this.values(); + } + + values(): IterableIterator { + let nextIndex = 0; + const observableValues = Array.from(this.#hashmap.values()); + + return makeIterableIterator({ + next: () => { + return nextIndex < observableValues.length + ? { value: observableValues[nextIndex++], done: false } + : { done: true, value: undefined }; + } + }); + } + + [Symbol.iterator](): IterableIterator { + return this.#hashmap.values(); + } + + get [Symbol.toStringTag](): string { + return "Set"; + } + + toJSON(): T[] { + return Array.from(this); + } + + toString(): string { + return "[object ObservableSet]"; + } +} diff --git a/src/common/utils/index.ts b/src/common/utils/index.ts index cc33ab332e..34bd65755b 100644 --- a/src/common/utils/index.ts +++ b/src/common/utils/index.ts @@ -29,17 +29,17 @@ export * from "./app-version"; export * from "./autobind"; export * from "./base64"; export * from "./camelCase"; -export * from "./toJS"; export * from "./cloneJson"; export * from "./debouncePromise"; export * from "./defineGlobal"; export * from "./delay"; export * from "./disposer"; -export * from "./disposer"; export * from "./downloadFile"; export * from "./escapeRegExp"; export * from "./extended-map"; export * from "./getRandId"; +export * from "./hash-set"; +export * from "./n-fircate"; export * from "./openExternal"; export * from "./paths"; export * from "./reject-promise"; @@ -47,6 +47,7 @@ export * from "./singleton"; export * from "./splitArray"; export * from "./tar"; export * from "./toggle-set"; +export * from "./toJS"; export * from "./type-narrowing"; import * as iter from "./iter"; diff --git a/src/common/utils/n-fircate.ts b/src/common/utils/n-fircate.ts new file mode 100644 index 0000000000..289d391e3e --- /dev/null +++ b/src/common/utils/n-fircate.ts @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * Split an iterable into several arrays with matching fields + * @param from The iterable of items to split up + * @param field The field of each item to split over + * @param parts What each array will be filtered to + * @returns A `parts.length` tuple of `T[]` where each array has matching `field` values + */ +export function nFircate(from: Iterable, field: keyof T, parts: []): []; +export function nFircate(from: Iterable, field: keyof T, parts: [T[typeof field]]): [T[]]; +export function nFircate(from: Iterable, field: keyof T, parts: [T[typeof field], T[typeof field]]): [T[], T[]]; +export function nFircate(from: Iterable, field: keyof T, parts: [T[typeof field], T[typeof field], T[typeof field]]): [T[], T[], T[]]; + +export function nFircate(from: Iterable, field: keyof T, parts: T[typeof field][]): T[][] { + if (new Set(parts).size !== parts.length) { + throw new TypeError("Duplicate parts entries"); + } + + const res = Array.from(parts, () => [] as T[]); + + for (const item of from) { + const index = parts.indexOf(item[field]); + + if (index < 0) { + continue; + } + + res[index].push(item); + } + + return res; +} diff --git a/src/extensions/renderer-api/k8s-api.ts b/src/extensions/renderer-api/k8s-api.ts index d16dbe83f5..c579aafbb6 100644 --- a/src/extensions/renderer-api/k8s-api.ts +++ b/src/extensions/renderer-api/k8s-api.ts @@ -86,8 +86,8 @@ export type { PersistentVolumesStore } from "../../renderer/components/+storage- export type { VolumeClaimStore } from "../../renderer/components/+storage-volume-claims/volume-claim.store"; export type { StorageClassStore } from "../../renderer/components/+storage-classes/storage-class.store"; export type { NamespaceStore } from "../../renderer/components/+namespaces/namespace.store"; -export type { ServiceAccountsStore } from "../../renderer/components/+user-management-service-accounts/service-accounts.store"; -export type { RolesStore } from "../../renderer/components/+user-management-roles/roles.store"; -export type { RoleBindingsStore } from "../../renderer/components/+user-management-roles-bindings/role-bindings.store"; +export type { ServiceAccountsStore } from "../../renderer/components/+user-management/+service-accounts/store"; +export type { RolesStore } from "../../renderer/components/+user-management/+roles/store"; +export type { RoleBindingsStore } from "../../renderer/components/+user-management/+role-bindings/store"; export type { CRDStore } from "../../renderer/components/+custom-resources/crd.store"; export type { CRDResourceStore } from "../../renderer/components/+custom-resources/crd-resource.store"; diff --git a/src/renderer/api/endpoints/cluster-role-binding.api.ts b/src/renderer/api/endpoints/cluster-role-binding.api.ts index 6f165316ef..868f00261a 100644 --- a/src/renderer/api/endpoints/cluster-role-binding.api.ts +++ b/src/renderer/api/endpoints/cluster-role-binding.api.ts @@ -18,14 +18,39 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -import { RoleBinding } from "./role-binding.api"; import { KubeApi } from "../kube-api"; +import { KubeObject } from "../kube-object"; -export class ClusterRoleBinding extends RoleBinding { +export type ClusterRoleBindingSubjectKind = "Group" | "ServiceAccount" | "User"; + +export interface ClusterRoleBindingSubject { + kind: ClusterRoleBindingSubjectKind; + name: string; + apiGroup?: string; + namespace?: string; +} + +export interface ClusterRoleBinding { + subjects?: ClusterRoleBindingSubject[]; + roleRef: { + kind: string; + name: string; + apiGroup?: string; + }; +} + +export class ClusterRoleBinding extends KubeObject { static kind = "ClusterRoleBinding"; static namespaced = false; static apiBase = "/apis/rbac.authorization.k8s.io/v1/clusterrolebindings"; + + getSubjects() { + return this.subjects || []; + } + + getSubjectNames(): string { + return this.getSubjects().map(subject => subject.name).join(", "); + } } export const clusterRoleBindingApi = new KubeApi({ diff --git a/src/renderer/api/endpoints/cluster-role.api.ts b/src/renderer/api/endpoints/cluster-role.api.ts index 6471b9c5e1..55a2f5283d 100644 --- a/src/renderer/api/endpoints/cluster-role.api.ts +++ b/src/renderer/api/endpoints/cluster-role.api.ts @@ -19,13 +19,26 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { Role } from "./role.api"; import { KubeApi } from "../kube-api"; +import { KubeObject } from "../kube-object"; -export class ClusterRole extends Role { +export interface ClusterRole { + rules: { + verbs: string[]; + apiGroups: string[]; + resources: string[]; + resourceNames?: string[]; + }[]; +} + +export class ClusterRole extends KubeObject { static kind = "ClusterRole"; static namespaced = false; static apiBase = "/apis/rbac.authorization.k8s.io/v1/clusterroles"; + + getRules() { + return this.rules || []; + } } export const clusterRoleApi = new KubeApi({ diff --git a/src/renderer/api/endpoints/role-binding.api.ts b/src/renderer/api/endpoints/role-binding.api.ts index 4e53752df1..c8c83d3151 100644 --- a/src/renderer/api/endpoints/role-binding.api.ts +++ b/src/renderer/api/endpoints/role-binding.api.ts @@ -24,15 +24,17 @@ import { KubeObject } from "../kube-object"; import { KubeApi } from "../kube-api"; import type { KubeJsonApiData } from "../kube-json-api"; -export interface IRoleBindingSubject { - kind: string; +export type RoleBindingSubjectKind = "Group" | "ServiceAccount" | "User"; + +export interface RoleBindingSubject { + kind: RoleBindingSubjectKind; name: string; namespace?: string; apiGroup?: string; } export interface RoleBinding { - subjects?: IRoleBindingSubject[]; + subjects?: RoleBindingSubject[]; roleRef: { kind: string; name: string; diff --git a/src/renderer/components/+namespaces/namespace-select.tsx b/src/renderer/components/+namespaces/namespace-select.tsx index c35ae72513..c1fe06c258 100644 --- a/src/renderer/components/+namespaces/namespace-select.tsx +++ b/src/renderer/components/+namespaces/namespace-select.tsx @@ -32,14 +32,12 @@ import { kubeWatchApi } from "../../api/kube-watch-api"; interface Props extends SelectProps { showIcons?: boolean; - showClusterOption?: boolean; // show "Cluster" option on the top (default: false) showAllNamespacesOption?: boolean; // show "All namespaces" option on the top (default: false) customizeOptions?(options: SelectOption[]): SelectOption[]; } const defaultProps: Partial = { showIcons: true, - showClusterOption: false, }; @observer @@ -61,13 +59,11 @@ export class NamespaceSelect extends React.Component { } @computed.struct get options(): SelectOption[] { - const { customizeOptions, showClusterOption, showAllNamespacesOption } = this.props; + const { customizeOptions, showAllNamespacesOption } = this.props; let options: SelectOption[] = namespaceStore.items.map(ns => ({ value: ns.getName() })); if (showAllNamespacesOption) { options.unshift({ label: "All Namespaces", value: "" }); - } else if (showClusterOption) { - options.unshift({ label: "Cluster", value: "" }); } if (customizeOptions) { diff --git a/src/renderer/components/+namespaces/namespace.store.ts b/src/renderer/components/+namespaces/namespace.store.ts index 29098c8a49..f865787d76 100644 --- a/src/renderer/components/+namespaces/namespace.store.ts +++ b/src/renderer/components/+namespaces/namespace.store.ts @@ -20,7 +20,7 @@ */ import { action, comparer, computed, IReactionDisposer, IReactionOptions, makeObservable, reaction, } from "mobx"; -import { autoBind, createStorage } from "../../utils"; +import { autoBind, createStorage, noop } from "../../utils"; import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../kube-object.store"; import { Namespace, namespacesApi } from "../../api/endpoints/namespaces.api"; import { apiManager } from "../../api/api-manager"; @@ -97,13 +97,16 @@ export class NamespaceStore extends KubeObjectStore { return this.selectedNamespaces; } - getSubscribeApis() { - // if user has given static list of namespaces let's not start watches because watch adds stuff that's not wanted + subscribe() { + /** + * if user has given static list of namespaces let's not start watches + * because watch adds stuff that's not wanted or will just fail + */ if (this.context?.cluster.accessibleNamespaces.length > 0) { - return []; + return noop; } - return super.getSubscribeApis(); + return super.subscribe(); } protected async loadItems(params: KubeObjectStoreLoadingParams) { diff --git a/src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx b/src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx deleted file mode 100644 index 273a3c0d51..0000000000 --- a/src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.tsx +++ /dev/null @@ -1,318 +0,0 @@ -/** - * Copyright (c) 2021 OpenLens Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -import "./add-role-binding-dialog.scss"; - -import React from "react"; -import { computed, observable, makeObservable } from "mobx"; -import { observer } from "mobx-react"; -import { Dialog, DialogProps } from "../dialog"; -import { Wizard, WizardStep } from "../wizard"; -import { Select, SelectOption } from "../select"; -import { SubTitle } from "../layout/sub-title"; -import type { IRoleBindingSubject, Role, RoleBinding, ServiceAccount } from "../../api/endpoints"; -import { Icon } from "../icon"; -import { Input } from "../input"; -import { NamespaceSelect } from "../+namespaces/namespace-select"; -import { Checkbox } from "../checkbox"; -import { KubeObject } from "../../api/kube-object"; -import { Notifications } from "../notifications"; -import { rolesStore } from "../+user-management-roles/roles.store"; -import { namespaceStore } from "../+namespaces/namespace.store"; -import { serviceAccountsStore } from "../+user-management-service-accounts/service-accounts.store"; -import { roleBindingsStore } from "./role-bindings.store"; -import { showDetails } from "../kube-object"; -import type { KubeObjectStore } from "../../kube-object.store"; - -interface BindingSelectOption extends SelectOption { - value: string; // binding name - item?: ServiceAccount | any; - subject?: IRoleBindingSubject; // used for new user/group when users-management-api not available -} - -interface Props extends Partial { -} - -const dialogState = observable.object({ - isOpen: false, - data: null as RoleBinding, -}); - -@observer -export class AddRoleBindingDialog extends React.Component { - constructor(props: Props) { - super(props); - makeObservable(this); - } - - static open(roleBinding?: RoleBinding) { - dialogState.isOpen = true; - dialogState.data = roleBinding; - } - - static close() { - dialogState.isOpen = false; - } - - get roleBinding(): RoleBinding { - return dialogState.data; - } - - @observable isLoading = false; - @observable selectedRoleId = ""; - @observable useRoleForBindingName = true; - @observable bindingName = ""; // new role-binding name - @observable bindContext = ""; // empty value means "cluster-wide", otherwise bind to namespace - @observable selectedAccounts = observable.array([], { deep: false }); - - @computed get isEditing() { - return !!this.roleBinding; - } - - @computed get selectedRole() { - return rolesStore.items.find(role => role.getId() === this.selectedRoleId); - } - - @computed get selectedBindings() { - return [ - ...this.selectedAccounts, - ]; - } - - close = () => { - AddRoleBindingDialog.close(); - }; - - async loadData() { - const stores: KubeObjectStore[] = [ - namespaceStore, - rolesStore, - serviceAccountsStore, - ]; - - this.isLoading = true; - await Promise.all(stores.map(store => store.reloadAll())); - this.isLoading = false; - } - - onOpen = async () => { - await this.loadData(); - - if (this.roleBinding) { - const { name, kind } = this.roleBinding.roleRef; - const role = rolesStore.items.find(role => role.kind === kind && role.getName() === name); - - if (role) { - this.selectedRoleId = role.getId(); - this.bindContext = role.getNs() || ""; - } - } - }; - - reset = () => { - this.selectedRoleId = ""; - this.bindContext = ""; - this.selectedAccounts.clear(); - }; - - onBindContextChange = (namespace: string) => { - this.bindContext = namespace; - const roleContext = this.selectedRole && this.selectedRole.getNs() || ""; - - if (this.bindContext && this.bindContext !== roleContext) { - this.selectedRoleId = ""; // reset previously selected role for specific context - } - }; - - createBindings = async () => { - const { selectedRole, bindContext: namespace, selectedBindings, bindingName, useRoleForBindingName } = this; - - const subjects = selectedBindings.map((item: KubeObject | IRoleBindingSubject) => { - if (item instanceof KubeObject) { - return { - name: item.getName(), - kind: item.kind, - namespace: item.getNs(), - }; - } - - return item; - }); - - try { - let roleBinding: RoleBinding; - - if (this.isEditing) { - roleBinding = await roleBindingsStore.updateSubjects({ - roleBinding: this.roleBinding, - addSubjects: subjects, - }); - } else { - const name = useRoleForBindingName ? selectedRole.getName() : bindingName; - - roleBinding = await roleBindingsStore.create({ name, namespace }, { - subjects, - roleRef: { - name: selectedRole.getName(), - kind: selectedRole.kind, - } - }); - } - showDetails(roleBinding.selfLink); - this.close(); - } catch (err) { - Notifications.error(err); - } - }; - - @computed get roleOptions(): BindingSelectOption[] { - let roles = rolesStore.items as Role[]; - - if (this.bindContext) { - // show only cluster-roles or roles for selected context namespace - roles = roles.filter(role => !role.getNs() || role.getNs() === this.bindContext); - } - - return roles.map(role => { - const name = role.getName(); - const namespace = role.getNs(); - - return { - value: role.getId(), - label: name + (namespace ? ` (${namespace})` : "") - }; - }); - } - - @computed get serviceAccountOptions(): BindingSelectOption[] { - return serviceAccountsStore.items.map(account => { - const name = account.getName(); - const namespace = account.getNs(); - - return { - item: account, - value: name, - label: <> {name} ({namespace}) - }; - }); - } - - renderContents() { - const unwrapBindings = (options: BindingSelectOption[]) => options.map(option => option.item || option.subject); - - return ( - <> - - this.onBindContextChange(value)} - /> - - - this.bindingName = v} - /> - ) - } - - ) - } - - - ) => { + if (!this.selectedRoleRef || this.bindingName === this.selectedRoleRef.getName()) { + this.bindingName = value.getName(); + } + + this.selectedRoleRef = value; + }} + /> + + + this.bindingName = val} + /> + + + + Users + this.selectedUsers.add(newUser)} + items={Array.from(this.selectedUsers)} + remove={({ oldItem }) => this.selectedUsers.delete(oldItem)} + /> + + Groups + this.selectedGroups.add(newGroup)} + items={Array.from(this.selectedGroups)} + remove={({ oldItem }) => this.selectedGroups.delete(oldItem)} + /> + + Service Accounts + this.clusterRoleName = v} + /> + + + + ); + } +} diff --git a/src/renderer/components/+user-management-roles/role-details.scss b/src/renderer/components/+user-management/+cluster-roles/details.scss similarity index 100% rename from src/renderer/components/+user-management-roles/role-details.scss rename to src/renderer/components/+user-management/+cluster-roles/details.scss diff --git a/src/renderer/components/+user-management/+cluster-roles/details.tsx b/src/renderer/components/+user-management/+cluster-roles/details.tsx new file mode 100644 index 0000000000..52bee8dc8e --- /dev/null +++ b/src/renderer/components/+user-management/+cluster-roles/details.tsx @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import "./details.scss"; + +import { observer } from "mobx-react"; +import React from "react"; + +import { KubeEventDetails } from "../../+events/kube-event-details"; +import { kubeObjectDetailRegistry } from "../../../api/kube-object-detail-registry"; +import { DrawerTitle } from "../../drawer"; +import type { KubeObjectDetailsProps } from "../../kube-object"; +import { KubeObjectMeta } from "../../kube-object/kube-object-meta"; +import type { ClusterRole } from "../../../api/endpoints"; + +interface Props extends KubeObjectDetailsProps { +} + +@observer +export class ClusterRoleDetails extends React.Component { + render() { + const { object: clusterRole } = this.props; + + if (!clusterRole) return null; + const rules = clusterRole.getRules(); + + return ( +
+ + + + {rules.map(({ resourceNames, apiGroups, resources, verbs }, index) => { + return ( +
+ {resources && ( + <> +
Resources
+
{resources.join(", ")}
+ + )} + {verbs && ( + <> +
Verbs
+
{verbs.join(", ")}
+ + )} + {apiGroups && ( + <> +
Api Groups
+
+ {apiGroups + .map(apiGroup => apiGroup === "" ? `'${apiGroup}'` : apiGroup) + .join(", ") + } +
+ + )} + {resourceNames && ( + <> +
Resource Names
+
{resourceNames.join(", ")}
+ + )} +
+ ); + })} +
+ ); + } +} + +kubeObjectDetailRegistry.add({ + kind: "ClusterRole", + apiVersions: ["rbac.authorization.k8s.io/v1"], + components: { + Details: (props) => + } +}); +kubeObjectDetailRegistry.add({ + kind: "ClusterRole", + apiVersions: ["rbac.authorization.k8s.io/v1"], + priority: 5, + components: { + Details: (props) => + } +}); diff --git a/src/renderer/components/+user-management-roles-bindings/index.ts b/src/renderer/components/+user-management/+cluster-roles/index.ts similarity index 90% rename from src/renderer/components/+user-management-roles-bindings/index.ts rename to src/renderer/components/+user-management/+cluster-roles/index.ts index ca08a30691..e45d58073c 100644 --- a/src/renderer/components/+user-management-roles-bindings/index.ts +++ b/src/renderer/components/+user-management/+cluster-roles/index.ts @@ -18,7 +18,6 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -export * from "./role-bindings"; -export * from "./role-binding-details"; -export * from "./add-role-binding-dialog"; +export * from "./view"; +export * from "./details"; +export * from "./add-dialog"; diff --git a/src/renderer/components/+user-management/+cluster-roles/store.ts b/src/renderer/components/+user-management/+cluster-roles/store.ts new file mode 100644 index 0000000000..6fe25a2c42 --- /dev/null +++ b/src/renderer/components/+user-management/+cluster-roles/store.ts @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +import { apiManager } from "../../../api/api-manager"; +import { ClusterRole, clusterRoleApi } from "../../../api/endpoints"; +import { KubeObjectStore } from "../../../kube-object.store"; +import { autoBind } from "../../../utils"; + +export class ClusterRolesStore extends KubeObjectStore { + api = clusterRoleApi; + + constructor() { + super(); + autoBind(this); + } + + protected sortItems(items: ClusterRole[]) { + return super.sortItems(items, [ + clusterRole => clusterRole.kind, + clusterRole => clusterRole.getName(), + ]); + } +} + +export const clusterRolesStore = new ClusterRolesStore(); + +apiManager.registerStore(clusterRolesStore); diff --git a/src/renderer/components/+user-management/+cluster-roles/view.scss b/src/renderer/components/+user-management/+cluster-roles/view.scss new file mode 100644 index 0000000000..2225c17a06 --- /dev/null +++ b/src/renderer/components/+user-management/+cluster-roles/view.scss @@ -0,0 +1,11 @@ +.ClusterRoles { + .help-icon { + margin-left: $margin / 2; + } + + .TableCell { + &.warning { + @include table-cell-warning; + } + } +} diff --git a/src/renderer/components/+user-management/+cluster-roles/view.tsx b/src/renderer/components/+user-management/+cluster-roles/view.tsx new file mode 100644 index 0000000000..43d8dedcbb --- /dev/null +++ b/src/renderer/components/+user-management/+cluster-roles/view.tsx @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import "./view.scss"; + +import { observer } from "mobx-react"; +import React from "react"; +import type { RouteComponentProps } from "react-router"; +import type { ClusterRole } from "../../../api/endpoints"; +import { KubeObjectListLayout } from "../../kube-object"; +import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; +import type { ClusterRolesRouteParams } from "../user-management.route"; +import { AddClusterRoleDialog } from "./add-dialog"; +import { clusterRolesStore } from "./store"; + +enum columnId { + name = "name", + namespace = "namespace", + age = "age", +} + +interface Props extends RouteComponentProps { +} + +@observer +export class ClusterRoles extends React.Component { + render() { + return ( + <> + clusterRole.getName(), + [columnId.age]: (clusterRole: ClusterRole) => clusterRole.getTimeDiffFromNow(), + }} + searchFilters={[ + (clusterRole: ClusterRole) => clusterRole.getSearchFields(), + ]} + renderHeaderTitle="Cluster Roles" + renderTableHeader={[ + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + ]} + renderTableContents={(clusterRole: ClusterRole) => [ + clusterRole.getName(), + , + clusterRole.getAge(), + ]} + addRemoveButtons={{ + onAdd: () => AddClusterRoleDialog.open(), + addTooltip: "Create new ClusterRole", + }} + /> + + + ); + } +} diff --git a/src/renderer/components/+user-management/+role-bindings/details.scss b/src/renderer/components/+user-management/+role-bindings/details.scss new file mode 100644 index 0000000000..805e5e05fe --- /dev/null +++ b/src/renderer/components/+user-management/+role-bindings/details.scss @@ -0,0 +1,2 @@ +.RoleBindingDetails { +} \ No newline at end of file diff --git a/src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx b/src/renderer/components/+user-management/+role-bindings/details.tsx similarity index 59% rename from src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx rename to src/renderer/components/+user-management/+role-bindings/details.tsx index 3fb14c4bc4..39080d56b5 100644 --- a/src/renderer/components/+user-management-roles-bindings/role-binding-details.tsx +++ b/src/renderer/components/+user-management/+role-bindings/details.tsx @@ -19,35 +19,32 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import "./role-binding-details.scss"; +import "./details.scss"; -import React from "react"; -import { AddRemoveButtons } from "../add-remove-buttons"; -import type { IRoleBindingSubject, RoleBinding } from "../../api/endpoints"; -import { boundMethod, prevDefault } from "../../utils"; -import { Table, TableCell, TableHead, TableRow } from "../table"; -import { ConfirmDialog } from "../confirm-dialog"; -import { DrawerTitle } from "../drawer"; -import { KubeEventDetails } from "../+events/kube-event-details"; +import { reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; -import { observable, reaction, makeObservable } from "mobx"; -import { roleBindingsStore } from "./role-bindings.store"; -import { AddRoleBindingDialog } from "./add-role-binding-dialog"; -import type { KubeObjectDetailsProps } from "../kube-object"; -import { KubeObjectMeta } from "../kube-object/kube-object-meta"; -import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry"; +import React from "react"; +import { KubeEventDetails } from "../../+events/kube-event-details"; +import type { RoleBinding, RoleBindingSubject } from "../../../api/endpoints"; +import { kubeObjectDetailRegistry } from "../../../api/kube-object-detail-registry"; +import { prevDefault, boundMethod } from "../../../utils"; +import { AddRemoveButtons } from "../../add-remove-buttons"; +import { ConfirmDialog } from "../../confirm-dialog"; +import { DrawerTitle } from "../../drawer"; +import type { KubeObjectDetailsProps } from "../../kube-object"; +import { KubeObjectMeta } from "../../kube-object/kube-object-meta"; +import { Table, TableCell, TableHead, TableRow } from "../../table"; +import { RoleBindingDialog } from "./dialog"; +import { roleBindingsStore } from "./store"; +import { ObservableHashSet } from "../../../../common/utils/hash-set"; +import { hashRoleBindingSubject } from "./hashers"; interface Props extends KubeObjectDetailsProps { } @observer export class RoleBindingDetails extends React.Component { - @observable selectedSubjects = observable.array([], { deep: false }); - - constructor(props: Props) { - super(props); - makeObservable(this); - } + selectedSubjects = new ObservableHashSet([], hashRoleBindingSubject); async componentDidMount() { disposeOnUnmount(this, [ @@ -57,24 +54,13 @@ export class RoleBindingDetails extends React.Component { ]); } - selectSubject(subject: IRoleBindingSubject) { - const { selectedSubjects } = this; - const isSelected = selectedSubjects.includes(subject); - - selectedSubjects.replace( - isSelected - ? selectedSubjects.filter(sub => sub !== subject) // unselect - : selectedSubjects.concat(subject) // select - ); - } - @boundMethod removeSelectedSubjects() { const { object: roleBinding } = this.props; const { selectedSubjects } = this; ConfirmDialog.open({ - ok: () => roleBindingsStore.updateSubjects({ roleBinding, removeSubjects: selectedSubjects }), + ok: () => roleBindingsStore.removeSubjects(roleBinding, selectedSubjects.toJSON()), labelOk: `Remove`, message: (

Remove selected bindings for {roleBinding.getName()}?

@@ -94,9 +80,9 @@ export class RoleBindingDetails extends React.Component { return (
- + - + Kind @@ -110,26 +96,27 @@ export class RoleBindingDetails extends React.Component {
- + {subjects.length > 0 && ( - - Binding + + Name Type Namespace { subjects.map((subject, i) => { const { kind, name, namespace } = subject; - const isSelected = selectedSubjects.includes(subject); + const isSelected = selectedSubjects.has(subject); return ( this.selectSubject(subject))} + key={i} + selected={isSelected} + onClick={prevDefault(() => this.selectedSubjects.toggle(subject))} > - + {name} {kind} {namespace || "-"} @@ -141,9 +128,9 @@ export class RoleBindingDetails extends React.Component { )} AddRoleBindingDialog.open(roleBinding)} - onRemove={selectedSubjects.length ? this.removeSelectedSubjects : null} - addTooltip={`Add bindings to ${roleRef.name}`} + onAdd={() => RoleBindingDialog.open(roleBinding)} + onRemove={selectedSubjects.size ? this.removeSelectedSubjects : null} + addTooltip={`Edit bindings of ${roleRef.name}`} removeTooltip={`Remove selected bindings from ${roleRef.name}`} /> @@ -166,20 +153,3 @@ kubeObjectDetailRegistry.add({ Details: (props) => } }); - - -kubeObjectDetailRegistry.add({ - kind: "ClusterRoleBinding", - apiVersions: ["rbac.authorization.k8s.io/v1"], - components: { - Details: (props) => - } -}); -kubeObjectDetailRegistry.add({ - kind: "ClusterRoleBinding", - apiVersions: ["rbac.authorization.k8s.io/v1"], - priority: 5, - components: { - Details: (props) => - } -}); diff --git a/src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.scss b/src/renderer/components/+user-management/+role-bindings/dialog.scss similarity index 100% rename from src/renderer/components/+user-management-roles-bindings/add-role-binding-dialog.scss rename to src/renderer/components/+user-management/+role-bindings/dialog.scss diff --git a/src/renderer/components/+user-management/+role-bindings/dialog.tsx b/src/renderer/components/+user-management/+role-bindings/dialog.tsx new file mode 100644 index 0000000000..0bed732381 --- /dev/null +++ b/src/renderer/components/+user-management/+role-bindings/dialog.tsx @@ -0,0 +1,303 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import "./dialog.scss"; + +import { computed, observable, makeObservable, action } from "mobx"; +import { observer } from "mobx-react"; +import React from "react"; + +import { rolesStore } from "../+roles/store"; +import { serviceAccountsStore } from "../+service-accounts/store"; +import { NamespaceSelect } from "../../+namespaces/namespace-select"; +import { ClusterRole, Role, roleApi, RoleBinding, RoleBindingSubject, ServiceAccount } from "../../../api/endpoints"; +import { Dialog, DialogProps } from "../../dialog"; +import { EditableList } from "../../editable-list"; +import { Icon } from "../../icon"; +import { showDetails } from "../../kube-object"; +import { SubTitle } from "../../layout/sub-title"; +import { Notifications } from "../../notifications"; +import { Select, SelectOption } from "../../select"; +import { Wizard, WizardStep } from "../../wizard"; +import { roleBindingsStore } from "./store"; +import { clusterRolesStore } from "../+cluster-roles/store"; +import { Input } from "../../input"; +import { getRoleRefSelectOption, ServiceAccountOption } from "../select-options"; +import { ObservableHashSet, nFircate } from "../../../utils"; + +interface Props extends Partial { +} + +interface DialogState { + isOpen: boolean; + data?: RoleBinding; +} + +@observer +export class RoleBindingDialog extends React.Component { + static state = observable.object({ + isOpen: false, + }); + + constructor(props: Props) { + super(props); + makeObservable(this); + } + + static open(roleBinding?: RoleBinding) { + RoleBindingDialog.state.isOpen = true; + RoleBindingDialog.state.data = roleBinding; + } + + static close() { + RoleBindingDialog.state.isOpen = false; + RoleBindingDialog.state.data = undefined; + } + + get roleBinding(): RoleBinding { + return RoleBindingDialog.state.data; + } + + @computed get isEditing() { + return !!this.roleBinding; + } + + @observable.ref selectedRoleRef: Role | ClusterRole | undefined = undefined; + @observable bindingName = ""; + @observable bindingNamespace = ""; + selectedAccounts = new ObservableHashSet([], sa => sa.metadata.uid); + selectedUsers = observable.set([]); + selectedGroups = observable.set([]); + + @computed get selectedBindings(): RoleBindingSubject[] { + const serviceAccounts = Array.from(this.selectedAccounts, sa => ({ + name: sa.getName(), + kind: "ServiceAccount" as const, + namespace: this.bindingNamespace, + })); + const users = Array.from(this.selectedUsers, user => ({ + name: user, + kind: "User" as const, + namespace: this.bindingNamespace, + })); + const groups = Array.from(this.selectedGroups, group => ({ + name: group, + kind: "Group" as const, + namespace: this.bindingNamespace, + })); + + return [ + ...serviceAccounts, + ...users, + ...groups, + ]; + } + + @computed get roleRefOptions(): SelectOption[] { + const roles = rolesStore.items + .filter(role => role.getNs() === this.bindingNamespace) + .map(getRoleRefSelectOption); + const clusterRoles = clusterRolesStore.items + .map(getRoleRefSelectOption); + + return [ + ...roles, + ...clusterRoles, + ]; + } + + @computed get serviceAccountOptions(): ServiceAccountOption[] { + return serviceAccountsStore.items.map(account => { + const name = account.getName(); + const namespace = account.getNs(); + + return { + value: `${account.getName()}%${account.getNs()}`, + account, + label: <> {name} ({namespace}) + }; + }); + } + + @computed get selectedServiceAccountOptions(): ServiceAccountOption[] { + return this.serviceAccountOptions.filter(({ account }) => this.selectedAccounts.has(account)); + } + + @action + onOpen = () => { + const binding = this.roleBinding; + + if (!binding) { + return this.reset(); + } + + this.selectedRoleRef = (binding.roleRef.kind === roleApi.kind ? rolesStore : clusterRolesStore) + .items + .find(item => item.getName() === binding.roleRef.name); + + this.bindingName = binding.getName(); + this.bindingNamespace = binding.getNs(); + + const [saSubjects, uSubjects, gSubjects] = nFircate(binding.getSubjects(), "kind", ["ServiceAccount", "User", "Group"]); + const accountNames = new Set(saSubjects.map(acc => acc.name)); + + this.selectedAccounts.replace( + serviceAccountsStore.items + .filter(sa => accountNames.has(sa.getName())) + ); + this.selectedUsers.replace(uSubjects.map(user => user.name)); + this.selectedGroups.replace(gSubjects.map(group => group.name)); + }; + + @action + reset = () => { + this.selectedRoleRef = undefined; + this.bindingName = ""; + this.bindingNamespace = ""; + this.selectedAccounts.clear(); + this.selectedUsers.clear(); + this.selectedGroups.clear(); + }; + + createBindings = async () => { + const { selectedRoleRef, bindingNamespace: namespace, selectedBindings } = this; + + try { + const roleBinding = this.isEditing + ? await roleBindingsStore.updateSubjects(this.roleBinding, selectedBindings) + : await roleBindingsStore.create({ name: this.bindingName, namespace }, { + subjects: selectedBindings, + roleRef: { + name: selectedRoleRef.getName(), + kind: selectedRoleRef.kind, + } + }); + + showDetails(roleBinding.selfLink); + RoleBindingDialog.close(); + } catch (err) { + Notifications.error(err); + } + }; + + renderContents() { + return ( + <> + + this.bindingNamespace = value} + /> + + + this.bindingName = value} + /> + + + + Users + this.selectedUsers.add(newUser)} + items={Array.from(this.selectedUsers)} + remove={({ oldItem }) => this.selectedUsers.delete(oldItem)} + /> + + Groups + this.selectedGroups.add(newGroup)} + items={Array.from(this.selectedGroups)} + remove={({ oldItem }) => this.selectedGroups.delete(oldItem)} + /> + + Service Accounts + { } diff --git a/src/renderer/components/+user-management/+service-accounts/index.ts b/src/renderer/components/+user-management/+service-accounts/index.ts new file mode 100644 index 0000000000..b02c71be28 --- /dev/null +++ b/src/renderer/components/+user-management/+service-accounts/index.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +export * from "./view"; +export * from "./details"; +export * from "./create-dialog"; diff --git a/src/renderer/components/+user-management-service-accounts/service-accounts-secret.scss b/src/renderer/components/+user-management/+service-accounts/secret.scss similarity index 100% rename from src/renderer/components/+user-management-service-accounts/service-accounts-secret.scss rename to src/renderer/components/+user-management/+service-accounts/secret.scss diff --git a/src/renderer/components/+user-management-service-accounts/service-accounts-secret.tsx b/src/renderer/components/+user-management/+service-accounts/secret.tsx similarity index 94% rename from src/renderer/components/+user-management-service-accounts/service-accounts-secret.tsx rename to src/renderer/components/+user-management/+service-accounts/secret.tsx index d57bb8cc05..4eff2b30dd 100644 --- a/src/renderer/components/+user-management-service-accounts/service-accounts-secret.tsx +++ b/src/renderer/components/+user-management/+service-accounts/secret.tsx @@ -19,13 +19,14 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import "./service-accounts-secret.scss"; +import "./secret.scss"; -import React from "react"; import moment from "moment"; -import { Icon } from "../icon"; -import type { Secret } from "../../api/endpoints/secret.api"; -import { prevDefault } from "../../utils"; +import React from "react"; + +import type { Secret } from "../../../api/endpoints/secret.api"; +import { prevDefault } from "../../../utils"; +import { Icon } from "../../icon"; interface Props { secret: Secret; diff --git a/src/renderer/components/+user-management-service-accounts/service-accounts.store.ts b/src/renderer/components/+user-management/+service-accounts/store.ts similarity index 87% rename from src/renderer/components/+user-management-service-accounts/service-accounts.store.ts rename to src/renderer/components/+user-management/+service-accounts/store.ts index c0917dab12..51bbabca34 100644 --- a/src/renderer/components/+user-management-service-accounts/service-accounts.store.ts +++ b/src/renderer/components/+user-management/+service-accounts/store.ts @@ -19,10 +19,10 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { autoBind } from "../../utils"; -import { ServiceAccount, serviceAccountsApi } from "../../api/endpoints"; -import { KubeObjectStore } from "../../kube-object.store"; -import { apiManager } from "../../api/api-manager"; +import { apiManager } from "../../../api/api-manager"; +import { ServiceAccount, serviceAccountsApi } from "../../../api/endpoints"; +import { KubeObjectStore } from "../../../kube-object.store"; +import { autoBind } from "../../../utils"; export class ServiceAccountsStore extends KubeObjectStore { api = serviceAccountsApi; diff --git a/src/renderer/components/+user-management-service-accounts/service-accounts.scss b/src/renderer/components/+user-management/+service-accounts/view.scss similarity index 100% rename from src/renderer/components/+user-management-service-accounts/service-accounts.scss rename to src/renderer/components/+user-management/+service-accounts/view.scss diff --git a/src/renderer/components/+user-management-service-accounts/service-accounts.tsx b/src/renderer/components/+user-management/+service-accounts/view.tsx similarity index 80% rename from src/renderer/components/+user-management-service-accounts/service-accounts.tsx rename to src/renderer/components/+user-management/+service-accounts/view.tsx index dff4d1fe58..442fbe3d77 100644 --- a/src/renderer/components/+user-management-service-accounts/service-accounts.tsx +++ b/src/renderer/components/+user-management/+service-accounts/view.tsx @@ -19,22 +19,22 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import "./service-accounts.scss"; +import "./view.scss"; -import React from "react"; import { observer } from "mobx-react"; -import type { ServiceAccount } from "../../api/endpoints/service-accounts.api"; +import React from "react"; import type { RouteComponentProps } from "react-router"; -import type { KubeObjectMenuProps } from "../kube-object/kube-object-menu"; -import { MenuItem } from "../menu"; -import { openServiceAccountKubeConfig } from "../kubeconfig-dialog"; -import { Icon } from "../icon"; -import { KubeObjectListLayout } from "../kube-object"; -import type { IServiceAccountsRouteParams } from "../+user-management"; -import { serviceAccountsStore } from "./service-accounts.store"; -import { CreateServiceAccountDialog } from "./create-service-account-dialog"; -import { kubeObjectMenuRegistry } from "../../../extensions/registries/kube-object-menu-registry"; -import { KubeObjectStatusIcon } from "../kube-object-status-icon"; +import type { ServiceAccountsRouteParams } from "../user-management.route"; +import { kubeObjectMenuRegistry } from "../../../../extensions/registries/kube-object-menu-registry"; +import type { ServiceAccount } from "../../../api/endpoints/service-accounts.api"; +import { Icon } from "../../icon"; +import { KubeObjectListLayout } from "../../kube-object"; +import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; +import type { KubeObjectMenuProps } from "../../kube-object/kube-object-menu"; +import { openServiceAccountKubeConfig } from "../../kubeconfig-dialog"; +import { MenuItem } from "../../menu"; +import { CreateServiceAccountDialog } from "./create-dialog"; +import { serviceAccountsStore } from "./store"; enum columnId { name = "name", @@ -42,7 +42,7 @@ enum columnId { age = "age", } -interface Props extends RouteComponentProps { +interface Props extends RouteComponentProps { } @observer diff --git a/src/renderer/components/+user-management/select-options.tsx b/src/renderer/components/+user-management/select-options.tsx new file mode 100644 index 0000000000..65bfeeec9e --- /dev/null +++ b/src/renderer/components/+user-management/select-options.tsx @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import React from "react"; +import type { ServiceAccount } from "../../api/endpoints"; +import type { KubeObject } from "../../api/kube-object"; +import { Icon } from "../icon"; +import type { SelectOption } from "../select"; +import { TooltipPosition } from "../tooltip"; + +export type ServiceAccountOption = SelectOption & { account: ServiceAccount }; + +export function getRoleRefSelectOption(item: T): SelectOption { + return { + value: item, + label: ( + <> + + {" "} + {item.getName()} + + ), + }; +} diff --git a/src/renderer/components/+user-management/user-management.route.ts b/src/renderer/components/+user-management/user-management.route.ts index f6e4def1e8..290e7fa2d4 100644 --- a/src/renderer/components/+user-management/user-management.route.ts +++ b/src/renderer/components/+user-management/user-management.route.ts @@ -19,45 +19,62 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import type { RouteProps } from "react-router"; import { buildURL, IURLParams } from "../../../common/utils/buildUrl"; +import type { RouteProps } from "react-router"; + // Routes export const serviceAccountsRoute: RouteProps = { path: "/service-accounts" }; +export const podSecurityPoliciesRoute: RouteProps = { + path: "/pod-security-policies" +}; export const rolesRoute: RouteProps = { path: "/roles" }; +export const clusterRolesRoute: RouteProps = { + path: "/cluster-roles" +}; export const roleBindingsRoute: RouteProps = { path: "/role-bindings" }; -export const podSecurityPoliciesRoute: RouteProps = { - path: "/pod-security-policies" +export const clusterRoleBindingsRoute: RouteProps = { + path: "/cluster-role-bindings" }; export const usersManagementRoute: RouteProps = { path: [ serviceAccountsRoute, + podSecurityPoliciesRoute, roleBindingsRoute, + clusterRoleBindingsRoute, rolesRoute, - podSecurityPoliciesRoute + clusterRolesRoute, ].map(route => route.path.toString()) }; // Route params -export interface IServiceAccountsRouteParams { +export interface ServiceAccountsRouteParams { } -export interface IRoleBindingsRouteParams { +export interface RoleBindingsRouteParams { } -export interface IRolesRouteParams { +export interface ClusterRoleBindingsRouteParams { +} + +export interface RolesRouteParams { +} + +export interface ClusterRolesRouteParams { } // URL-builders export const usersManagementURL = (params?: IURLParams) => serviceAccountsURL(params); -export const serviceAccountsURL = buildURL(serviceAccountsRoute.path); -export const roleBindingsURL = buildURL(roleBindingsRoute.path); -export const rolesURL = buildURL(rolesRoute.path); +export const serviceAccountsURL = buildURL(serviceAccountsRoute.path); export const podSecurityPoliciesURL = buildURL(podSecurityPoliciesRoute.path); +export const roleBindingsURL = buildURL(roleBindingsRoute.path); +export const clusterRoleBindingsURL = buildURL(clusterRoleBindingsRoute.path); +export const rolesURL = buildURL(rolesRoute.path); +export const clusterRolesURL = buildURL(clusterRolesRoute.path); diff --git a/src/renderer/components/+user-management/user-management.tsx b/src/renderer/components/+user-management/user-management.tsx index cb5c5837f3..c714f34525 100644 --- a/src/renderer/components/+user-management/user-management.tsx +++ b/src/renderer/components/+user-management/user-management.tsx @@ -20,15 +20,32 @@ */ import "./user-management.scss"; -import React from "react"; + import { observer } from "mobx-react"; -import { TabLayout, TabLayoutRoute } from "../layout/tab-layout"; -import { Roles } from "../+user-management-roles"; -import { RoleBindings } from "../+user-management-roles-bindings"; -import { ServiceAccounts } from "../+user-management-service-accounts"; -import { podSecurityPoliciesRoute, podSecurityPoliciesURL, roleBindingsRoute, roleBindingsURL, rolesRoute, rolesURL, serviceAccountsRoute, serviceAccountsURL } from "./user-management.route"; +import React from "react"; + import { PodSecurityPolicies } from "../+pod-security-policies"; import { isAllowedResource } from "../../../common/rbac"; +import { TabLayout, TabLayoutRoute } from "../layout/tab-layout"; +import { ClusterRoles } from "./+cluster-roles"; +import { ClusterRoleBindings } from "./+cluster-role-bindings"; +import { Roles } from "./+roles"; +import { RoleBindings } from "./+role-bindings"; +import { ServiceAccounts } from "./+service-accounts"; +import { + clusterRoleBindingsRoute, + clusterRoleBindingsURL, + clusterRolesRoute, + clusterRolesURL, + podSecurityPoliciesRoute, + podSecurityPoliciesURL, + roleBindingsRoute, + roleBindingsURL, + rolesRoute, + rolesURL, + serviceAccountsRoute, + serviceAccountsURL, +} from "./user-management.route"; @observer export class UserManagement extends React.Component { @@ -44,18 +61,16 @@ export class UserManagement extends React.Component { }); } - if (isAllowedResource("rolebindings") || isAllowedResource("clusterrolebindings")) { - // TODO: seperate out these two pages + if (isAllowedResource("clusterroles")) { tabRoutes.push({ - title: "Role Bindings", - component: RoleBindings, - url: roleBindingsURL(), - routePath: roleBindingsRoute.path.toString(), + title: "Cluster Roles", + component: ClusterRoles, + url: clusterRolesURL(), + routePath: clusterRolesRoute.path.toString(), }); } - if (isAllowedResource("roles") || isAllowedResource("clusterroles")) { - // TODO: seperate out these two pages + if (isAllowedResource("roles")) { tabRoutes.push({ title: "Roles", component: Roles, @@ -64,6 +79,24 @@ export class UserManagement extends React.Component { }); } + if (isAllowedResource("clusterrolebindings")) { + tabRoutes.push({ + title: "Cluster Role Bindings", + component: ClusterRoleBindings, + url: clusterRoleBindingsURL(), + routePath: clusterRoleBindingsRoute.path.toString(), + }); + } + + if (isAllowedResource("rolebindings")) { + tabRoutes.push({ + title: "Role Bindings", + component: RoleBindings, + url: roleBindingsURL(), + routePath: roleBindingsRoute.path.toString(), + }); + } + if (isAllowedResource("podsecuritypolicies")) { tabRoutes.push({ title: "Pod Security Policies", diff --git a/src/renderer/components/app.tsx b/src/renderer/components/app.tsx index 948fec03ce..cde061b1aa 100755 --- a/src/renderer/components/app.tsx +++ b/src/renderer/components/app.tsx @@ -41,7 +41,6 @@ import { Events } from "./+events/events"; import { eventRoute } from "./+events"; import { Apps, appsRoute } from "./+apps"; import { KubeObjectDetails } from "./kube-object/kube-object-details"; -import { AddRoleBindingDialog } from "./+user-management-roles-bindings"; import { DeploymentScaleDialog } from "./+workloads-deployments/deployment-scale-dialog"; import { CronJobTriggerDialog } from "./+workloads-cronjobs/cronjob-trigger-dialog"; import { CustomResources } from "./+custom-resources/custom-resources"; @@ -201,7 +200,6 @@ export class App extends React.Component { - diff --git a/src/renderer/components/editable-list/editable-list.scss b/src/renderer/components/editable-list/editable-list.scss index 9bcd266da1..b30fdf7f57 100644 --- a/src/renderer/components/editable-list/editable-list.scss +++ b/src/renderer/components/editable-list/editable-list.scss @@ -23,7 +23,7 @@ .el-contents { display: flex; flex-direction: column; - margin-top: $padding * 2; + margin: $padding 0px; .el-value-remove { .Icon { @@ -35,7 +35,9 @@ display: grid; grid-template-columns: 1fr auto; padding: $padding $padding * 2; - margin-bottom: 1px; + margin-bottom: $padding / 4; + backdrop-filter: brightness(0.75); + border-radius: var(--border-radius); :last-child { margin-bottom: unset; @@ -46,4 +48,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/renderer/components/editable-list/editable-list.tsx b/src/renderer/components/editable-list/editable-list.tsx index 47bfaa8fe5..ec31a2586e 100644 --- a/src/renderer/components/editable-list/editable-list.tsx +++ b/src/renderer/components/editable-list/editable-list.tsx @@ -21,11 +21,11 @@ import "./editable-list.scss"; +import { observer } from "mobx-react"; import React from "react"; + import { Icon } from "../icon"; import { Input } from "../input"; -import { observable, makeObservable } from "mobx"; -import { observer } from "mobx-react"; import { boundMethod } from "../../utils"; export interface Props { @@ -47,20 +47,14 @@ const defaultProps: Partial> = { @observer export class EditableList extends React.Component> { static defaultProps = defaultProps as Props; - @observable currentNewItem = ""; - - constructor(props: Props) { - super(props); - makeObservable(this); - } @boundMethod - onSubmit(val: string) { + onSubmit(val: string, evt: React.KeyboardEvent) { const { add } = this.props; if (val) { + evt.preventDefault(); add(val); - this.currentNewItem = ""; } } @@ -71,17 +65,15 @@ export class EditableList extends React.Component> {
this.currentNewItem = val} />
{ items.map((item, index) => ( -
+
{renderItem(item, index)}
remove(({ index, oldItem: item }))} /> diff --git a/src/renderer/components/input/input.scss b/src/renderer/components/input/input.scss index 224fec829c..97ad5c058a 100644 --- a/src/renderer/components/input/input.scss +++ b/src/renderer/components/input/input.scss @@ -48,7 +48,7 @@ --flex-gap: #{$padding / 1.5}; position: relative; - padding: $padding /4 * 3 0; + padding: $padding / 4 * 3 0; border-bottom: 1px solid $halfGray; line-height: 1; @@ -110,23 +110,17 @@ //- Themes &.theme { - &.round-black { + &.round { &.invalid.dirty { label { border-color: $colorSoftError; } } - label { - background: var(--inputControlBackground); - border: 1px solid var(--inputControlBorder); - border-radius: 5px; - padding: $padding; - color: var(--textColorTertiary); - - &:hover { - border-color: var(--inputControlHoverBorder); - } + border-radius: $radius; + border: 1px solid $halfGray; + color: inherit; + padding: $padding / 4 * 3 $padding / 4 * 3; &:focus-within { border-color: $colorInfo; @@ -136,6 +130,18 @@ display: none; } } + &.black { + label { + background: var(--inputControlBackground); + border-color: var(--inputControlBorder); + color: var(--textColorTertiary); + padding: $padding; + + &:hover { + border-color: var(--inputControlHoverBorder); + } + } + } } } } diff --git a/src/renderer/components/input/input.tsx b/src/renderer/components/input/input.tsx index 1d0700b846..5de07cb856 100644 --- a/src/renderer/components/input/input.tsx +++ b/src/renderer/components/input/input.tsx @@ -41,7 +41,7 @@ type InputElement = HTMLInputElement | HTMLTextAreaElement; type InputElementProps = InputHTMLAttributes & TextareaHTMLAttributes & DOMAttributes; export type InputProps = Omit & { - theme?: "round-black"; + theme?: "round-black" | "round"; className?: string; value?: T; autoSelectOnFocus?: boolean @@ -55,7 +55,7 @@ export type InputProps = Omit): void; - onSubmit?(value: T): void; + onSubmit?(value: T, evt: React.KeyboardEvent): void; }; interface State { @@ -90,7 +90,7 @@ export class Input extends React.Component { return this.state.valid; } - setValue(value: string) { + setValue(value = "") { if (value !== this.getValue()) { const nativeInputValueSetter = Object.getOwnPropertyDescriptor(this.input.constructor.prototype, "value").set; @@ -236,16 +236,15 @@ export class Input extends React.Component { } @boundMethod - onChange(evt: React.ChangeEvent) { - if (this.props.onChange) { - this.props.onChange(evt.currentTarget.value, evt); - } - + onChange(evt: React.ChangeEvent) { + this.props.onChange?.(evt.currentTarget.value, evt); this.validate(); this.autoFitHeight(); // mark input as dirty for the first time only onBlur() to avoid immediate error-state show when start typing - if (!this.state.dirty) this.setState({ dirtyOnBlur: true }); + if (!this.state.dirty) { + this.setState({ dirtyOnBlur: true }); + } // re-render component when used as uncontrolled input // when used @defaultValue instead of @value changing real input.value doesn't call render() @@ -255,17 +254,19 @@ export class Input extends React.Component { } @boundMethod - onKeyDown(evt: React.KeyboardEvent) { + onKeyDown(evt: React.KeyboardEvent) { const modified = evt.shiftKey || evt.metaKey || evt.altKey || evt.ctrlKey; - if (this.props.onKeyDown) { - this.props.onKeyDown(evt); - } + this.props.onKeyDown?.(evt); switch (evt.key) { case "Enter": if (this.props.onSubmit && !modified && !evt.repeat && this.isValid) { - this.props.onSubmit(this.getValue()); + this.props.onSubmit(this.getValue(), evt); + + if (this.isUncontrolled) { + this.setValue(); + } } break; } @@ -303,6 +304,20 @@ export class Input extends React.Component { } } + get themeSelection(): Record { + const { theme } = this.props; + + if (!theme) { + return {}; + } + + return { + theme: true, + round: true, + black: theme === "round-black", + }; + } + @boundMethod bindRef(elem: InputElement) { this.input = elem; @@ -318,7 +333,7 @@ export class Input extends React.Component { const { focused, dirty, valid, validating, errors } = this.state; const className = cssNames("Input", this.props.className, { - [`theme ${theme}`]: theme, + ...this.themeSelection, focused, disabled, invalid: !valid, diff --git a/src/renderer/components/select/select.tsx b/src/renderer/components/select/select.tsx index 9dd5a61798..af13fecaae 100644 --- a/src/renderer/components/select/select.tsx +++ b/src/renderer/components/select/select.tsx @@ -26,10 +26,11 @@ import "./select.scss"; import React, { ReactNode } from "react"; import { computed, makeObservable } from "mobx"; import { observer } from "mobx-react"; -import { boundMethod, cssNames } from "../../utils"; import ReactSelect, { ActionMeta, components, OptionTypeBase, Props as ReactSelectProps, Styles } from "react-select"; import Creatable, { CreatableProps } from "react-select/creatable"; + import { ThemeStore } from "../../theme.store"; +import { boundMethod, cssNames } from "../../utils"; const { Menu } = components; @@ -65,8 +66,10 @@ export class Select extends React.Component { makeObservable(this); } - @computed get theme() { - return this.props.themeName || ThemeStore.getInstance().activeTheme.type; + @computed get themeClass() { + const themeName = this.props.themeName || ThemeStore.getInstance().activeTheme.type; + + return `theme-${themeName}`; } private styles: Styles = { @@ -128,7 +131,6 @@ export class Select extends React.Component { className, menuClass, isCreatable, autoConvertOptions, value, options, components = {}, ...props } = this.props; - const themeClass = `theme-${this.theme}`; const WrappedMenu = components.Menu ?? Menu; const selectProps: Partial = { @@ -138,14 +140,14 @@ export class Select extends React.Component { options: autoConvertOptions ? this.options : options, onChange: this.onChange, onKeyDown: this.onKeyDown, - className: cssNames("Select", themeClass, className), + className: cssNames("Select", this.themeClass, className), classNamePrefix: "Select", components: { ...components, Menu: props => ( ), } diff --git a/src/renderer/kube-object.store.ts b/src/renderer/kube-object.store.ts index f1b9cfba96..f869d131d4 100644 --- a/src/renderer/kube-object.store.ts +++ b/src/renderer/kube-object.store.ts @@ -22,7 +22,7 @@ import type { ClusterContext } from "./components/context"; import { action, computed, makeObservable, observable, reaction, when } from "mobx"; -import { autoBind, bifurcateArray, noop, rejectPromiseBy } from "./utils"; +import { autoBind, noop, rejectPromiseBy } from "./utils"; import { KubeObject, KubeStatus } from "./api/kube-object"; import type { IKubeWatchEvent } from "./api/kube-watch-api"; import { ItemStore } from "./item.store"; @@ -309,51 +309,31 @@ export abstract class KubeObjectStore extends ItemSt }); } - getSubscribeApis(): KubeApi[] { - return [this.api]; - } - - subscribe(apis = this.getSubscribeApis()) { + subscribe() { const abortController = new AbortController(); - const [clusterScopedApis, namespaceScopedApis] = bifurcateArray(apis, api => api.isNamespaced); - for (const api of namespaceScopedApis) { - const store = apiManager.getStore(api); - - // This waits for the context and namespaces to be ready or fails fast if the disposer is called - Promise.race([rejectPromiseBy(abortController.signal), Promise.all([store.contextReady, store.namespacesReady])]) + if (this.api.isNamespaced) { + Promise.race([rejectPromiseBy(abortController.signal), Promise.all([this.contextReady, this.namespacesReady])]) .then(() => { - if ( - store.context.cluster.isGlobalWatchEnabled - && store.loadedNamespaces.length === 0 - ) { - return store.watchNamespace(api, "", abortController); + if (this.context.cluster.isGlobalWatchEnabled && this.loadedNamespaces.length === 0) { + return this.watchNamespace("", abortController); } for (const namespace of this.loadedNamespaces) { - store.watchNamespace(api, namespace, abortController); + this.watchNamespace(namespace, abortController); } }) .catch(noop); // ignore DOMExceptions + } else { + this.watchNamespace("", abortController); } - for (const api of clusterScopedApis) { - /** - * if the api is cluster scoped then we will never assign to `loadedNamespaces` - * and thus `store.namespacesReady` will never resolve. Futhermore, we don't care - * about watching namespaces. - */ - apiManager.getStore(api).watchNamespace(api, "", abortController); - } - - return () => { - abortController.abort(); - }; + return () => abortController.abort(); } - private watchNamespace(api: KubeApi, namespace: string, abortController: AbortController) { + private watchNamespace(namespace: string, abortController: AbortController) { let timedRetry: NodeJS.Timeout; - const watch = () => api.watch({ + const watch = () => this.api.watch({ namespace, abortController, callback From 86d0a86ec92df4d263bf3a362e318b4083590cce Mon Sep 17 00:00:00 2001 From: Jim Ehrismann <40840436+jim-docker@users.noreply.github.com> Date: Wed, 9 Jun 2021 14:54:19 -0400 Subject: [PATCH 20/23] Fix shell-env timeout (#2938) --- src/main/shell-session/shell-session.ts | 2 +- src/main/shell-sync.ts | 14 +----- src/main/utils/shell-env.ts | 66 +++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 13 deletions(-) create mode 100644 src/main/utils/shell-env.ts diff --git a/src/main/shell-session/shell-session.ts b/src/main/shell-session/shell-session.ts index eaa95f0fd8..039caa44ce 100644 --- a/src/main/shell-session/shell-session.ts +++ b/src/main/shell-session/shell-session.ts @@ -22,7 +22,7 @@ import type { Cluster } from "../cluster"; import { Kubectl } from "../kubectl"; import type * as WebSocket from "ws"; -import shellEnv from "shell-env"; +import { shellEnv } from "../utils/shell-env"; import { app } from "electron"; import { clearKubeconfigEnvVars } from "../utils/clear-kube-env-vars"; import path from "path"; diff --git a/src/main/shell-sync.ts b/src/main/shell-sync.ts index ff38ede3bb..d699969224 100644 --- a/src/main/shell-sync.ts +++ b/src/main/shell-sync.ts @@ -19,10 +19,9 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import shellEnv from "shell-env"; +import { shellEnv } from "./utils/shell-env"; import os from "os"; import { app } from "electron"; -import logger from "./logger"; interface Env { [key: string]: string; @@ -37,16 +36,7 @@ export async function shellSync() { const { shell } = os.userInfo(); let envVars = {}; - try { - envVars = await Promise.race([ - shellEnv(shell), - new Promise((_resolve, reject) => setTimeout(() => { - reject(new Error("Resolving shell environment is taking very long. Please review your shell configuration.")); - }, 5_000)) - ]); - } catch (error) { - logger.error(`shellEnv: ${error}`); - } + envVars = await shellEnv(shell); const env: Env = JSON.parse(JSON.stringify(envVars)); diff --git a/src/main/utils/shell-env.ts b/src/main/utils/shell-env.ts new file mode 100644 index 0000000000..b544603075 --- /dev/null +++ b/src/main/utils/shell-env.ts @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import shellEnvironment from "shell-env"; +import logger from "../logger"; + +export interface EnvironmentVariables { + readonly [key: string]: string; +} + +let shellSyncFailed = false; + +/** + * Attempts to get the shell environment per the user's existing startup scripts. + * If the environment can't be retrieved after 5 seconds an error message is logged. + * Subsequent calls after such a timeout simply log an error message without trying + * to get the environment, unless forceRetry is true. + * @param shell the shell to get the environment from + * @param forceRetry if true will always try to get the environment, otherwise if + * a previous call to this function failed then this call will fail too. + * @returns object containing the shell's environment variables. An empty object is + * returned if the call fails. + */ +export async function shellEnv(shell?: string, forceRetry = false) : Promise { + let envVars = {}; + + if (forceRetry) { + shellSyncFailed = false; + } + + if (!shellSyncFailed) { + try { + envVars = await Promise.race([ + shellEnvironment(shell), + new Promise((_resolve, reject) => setTimeout(() => { + reject(new Error("Resolving shell environment is taking very long. Please review your shell configuration.")); + }, 5_000)) + ]); + } catch (error) { + logger.error(`shellEnv: ${error}`); + shellSyncFailed = true; + } + } else { + logger.error("shellSync(): Resolving shell environment took too long. Please review your shell configuration."); + } + + return envVars; +} From 4a4f5e6d2604f109e682a8810ac1cbb50ff61ba9 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 9 Jun 2021 15:35:46 -0400 Subject: [PATCH 21/23] CatalogEnitityDrawerMenu should have actions running left to right (#3015) Signed-off-by: Sebastian Malton --- .../components/+catalog/catalog-entity-drawer-menu.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/renderer/components/+catalog/catalog-entity-drawer-menu.tsx b/src/renderer/components/+catalog/catalog-entity-drawer-menu.tsx index f7aacba970..d4f75d7434 100644 --- a/src/renderer/components/+catalog/catalog-entity-drawer-menu.tsx +++ b/src/renderer/components/+catalog/catalog-entity-drawer-menu.tsx @@ -97,14 +97,12 @@ export class CatalogEntityDrawerMenu extends React.Comp ); } - items.unshift( + items.push( this.addToHotbar(entity) }> ); - items.reverse(); - return items; } From 8e244db1a3bcf2d0a53cf2ec3fffebfbd422bfbf Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 9 Jun 2021 19:57:04 -0400 Subject: [PATCH 22/23] Set build version before building extensions (#3001) --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fba4e9b98e..f2c9d0eb13 100644 --- a/Makefile +++ b/Makefile @@ -68,8 +68,9 @@ integration-win: binaries/client build-extension-types build-extensions yarn integration .PHONY: build -build: node_modules binaries/client build-extensions +build: node_modules binaries/client yarn run npm:fix-build-version + $(MAKE) build-extensions yarn run compile ifeq "$(DETECTED_OS)" "Windows" yarn run electron-builder --publish onTag --x64 --ia32 From 540c6dac37c277a7fc93a383454f0fd527f8e0c9 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Thu, 10 Jun 2021 08:19:16 +0300 Subject: [PATCH 23/23] fix catalog layout bugs (#3018) Signed-off-by: Jari Kolehmainen --- src/renderer/components/+catalog/catalog.module.css | 6 +++++- src/renderer/components/+catalog/catalog.tsx | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/renderer/components/+catalog/catalog.module.css b/src/renderer/components/+catalog/catalog.module.css index 937195aa8f..cc1e7ce411 100644 --- a/src/renderer/components/+catalog/catalog.module.css +++ b/src/renderer/components/+catalog/catalog.module.css @@ -23,6 +23,10 @@ max-width: 40px; display: flex; align-items: center; + + .MuiAvatar-root { + font-size: var(--unit); + } } .nameCell { @@ -88,4 +92,4 @@ .activeTab, .activeTab:hover { background-color: var(--blue); --color-active: white; -} \ No newline at end of file +} diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index b01ad3010a..fc42277c99 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -255,6 +255,7 @@ export class Catalog extends React.Component { renderTableHeader={[ { title: "", className: styles.iconCell }, { title: "Name", className: styles.nameCell, sortBy: sortBy.name }, + { title: "Kind", className: styles.kindCell, sortBy: sortBy.kind }, { title: "Source", className: styles.sourceCell, sortBy: sortBy.source }, { title: "Labels", className: styles.labelsCell }, { title: "Status", className: styles.statusCell, sortBy: sortBy.status },