1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Start handling errors when accessing helm repositories

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
Janne Savolainen 2022-06-07 12:41:01 +03:00
parent 13e3b16bf5
commit 88fd267988
No known key found for this signature in database
GPG Key ID: 8C6CFB2FFFE8F68A
26 changed files with 5646 additions and 3320 deletions

View File

@ -306,118 +306,122 @@ exports[`deactivate helm repository from list of active repositories in preferen
</h2>
<div>
<div
class="flex gaps"
data-testid="helm-controls"
>
<div
class="Select theme-lens box grow Select--is-disabled css-3iigni-container"
class="flex gaps"
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-selection-of-active-public-helm-repository-live-region"
/>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__control Select__control--is-disabled css-1insrsq-control"
class="Select theme-lens box grow Select--is-disabled css-3iigni-container"
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-selection-of-active-public-helm-repository-live-region"
/>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__value-container css-319lph-ValueContainer"
class="Select__control Select__control--is-disabled css-1insrsq-control"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-selection-of-active-public-helm-repository-placeholder"
class="Select__value-container css-319lph-ValueContainer"
>
Repositories
</div>
<div
class="Select__input-container css-jzldcf-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-describedby="react-select-selection-of-active-public-helm-repository-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
disabled=""
id="selection-of-active-public-helm-repository"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<div
aria-hidden="true"
class="Select__indicator Select__loading-indicator css-at12u2-loadingIndicator"
>
<span
class="css-1xtdfmb-LoadingDot"
/>
<span
class="css-zoievk-LoadingDot"
/>
<span
class="css-x748d8-LoadingDot"
/>
</div>
<span
class="Select__indicator-separator css-109onse-indicatorSeparator"
/>
<div
aria-hidden="true"
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-selection-of-active-public-helm-repository-placeholder"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
Repositories
</div>
<div
class="Select__input-container css-jzldcf-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-describedby="react-select-selection-of-active-public-helm-repository-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
disabled=""
id="selection-of-active-public-helm-repository"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</svg>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<div
aria-hidden="true"
class="Select__indicator Select__loading-indicator css-at12u2-loadingIndicator"
>
<span
class="css-1xtdfmb-LoadingDot"
/>
<span
class="css-zoievk-LoadingDot"
/>
<span
class="css-x748d8-LoadingDot"
/>
</div>
<span
class="Select__indicator-separator css-109onse-indicatorSeparator"
/>
<div
aria-hidden="true"
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
<button
class="Button primary"
data-testid="add-custom-helm-repo-button"
type="button"
>
Add Custom Helm Repo
</button>
</div>
<button
class="Button primary"
data-testid="add-custom-helm-repo-button"
type="button"
>
Add Custom Helm Repo
</button>
</div>
<div
class="repos"
>
<div
class="pt-5 relative"
class="repos"
>
<div
class="Spinner singleColor center"
data-testid="helm-repositories-are-loading"
/>
class="pt-5 relative"
>
<div
class="Spinner singleColor center"
data-testid="helm-repositories-are-loading"
/>
</div>
</div>
<div />
</div>
<div />
</div>
</section>
</section>
@ -771,123 +775,127 @@ exports[`deactivate helm repository from list of active repositories in preferen
</h2>
<div>
<div
class="flex gaps"
data-testid="helm-controls"
>
<div
class="Select theme-lens box grow css-b62m3t-container"
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-selection-of-active-public-helm-repository-live-region"
/>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-selection-of-active-public-helm-repository-placeholder"
>
Repositories
</div>
<div
class="Select__input-container css-6j8wv5-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-describedby="react-select-selection-of-active-public-helm-repository-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
id="selection-of-active-public-helm-repository"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<span
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
/>
<div
aria-hidden="true"
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
<button
class="Button primary"
data-testid="add-custom-helm-repo-button"
type="button"
>
Add Custom Helm Repo
</button>
</div>
<div
class="repos"
>
<div
class="item flex gaps align-center justify-space-between mt-3"
class="flex gaps"
>
<div
class="repoName"
data-testid="helm-repository-some-active-repository"
>
some-active-repository
</div>
<div
class="repoUrl"
>
some-url
</div>
<i
class="Icon material interactive focusable"
data-testid="deactivate-helm-repository-some-active-repository"
tabindex="0"
tooltip="Remove"
class="Select theme-lens box grow css-b62m3t-container"
>
<span
class="icon"
data-icon-name="delete"
class="css-1f43avz-a11yText-A11yText"
id="react-select-selection-of-active-public-helm-repository-live-region"
/>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__control css-1s2u09g-control"
>
delete
</span>
</i>
<div
class="Select__value-container css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-selection-of-active-public-helm-repository-placeholder"
>
Repositories
</div>
<div
class="Select__input-container css-6j8wv5-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-describedby="react-select-selection-of-active-public-helm-repository-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
id="selection-of-active-public-helm-repository"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<span
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
/>
<div
aria-hidden="true"
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
<button
class="Button primary"
data-testid="add-custom-helm-repo-button"
type="button"
>
Add Custom Helm Repo
</button>
</div>
<div
class="repos"
>
<div
class="item flex gaps align-center justify-space-between mt-3"
>
<div
class="repoName"
data-testid="helm-repository-some-active-repository"
>
some-active-repository
</div>
<div
class="repoUrl"
>
some-url
</div>
<i
class="Icon material interactive focusable"
data-testid="deactivate-helm-repository-some-active-repository"
tabindex="0"
tooltip="Remove"
>
<span
class="icon"
data-icon-name="delete"
>
delete
</span>
</i>
</div>
</div>
<div />
</div>
<div />
</div>
</section>
</section>
@ -1241,123 +1249,127 @@ exports[`deactivate helm repository from list of active repositories in preferen
</h2>
<div>
<div
class="flex gaps"
data-testid="helm-controls"
>
<div
class="Select theme-lens box grow css-b62m3t-container"
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-selection-of-active-public-helm-repository-live-region"
/>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-selection-of-active-public-helm-repository-placeholder"
>
Repositories
</div>
<div
class="Select__input-container css-6j8wv5-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-describedby="react-select-selection-of-active-public-helm-repository-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
id="selection-of-active-public-helm-repository"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<span
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
/>
<div
aria-hidden="true"
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
<button
class="Button primary"
data-testid="add-custom-helm-repo-button"
type="button"
>
Add Custom Helm Repo
</button>
</div>
<div
class="repos"
>
<div
class="item flex gaps align-center justify-space-between mt-3"
class="flex gaps"
>
<div
class="repoName"
data-testid="helm-repository-some-active-repository"
>
some-active-repository
</div>
<div
class="repoUrl"
>
some-url
</div>
<i
class="Icon material interactive focusable"
data-testid="deactivate-helm-repository-some-active-repository"
tabindex="0"
tooltip="Remove"
class="Select theme-lens box grow css-b62m3t-container"
>
<span
class="icon"
data-icon-name="delete"
class="css-1f43avz-a11yText-A11yText"
id="react-select-selection-of-active-public-helm-repository-live-region"
/>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__control css-1s2u09g-control"
>
delete
</span>
</i>
<div
class="Select__value-container css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-selection-of-active-public-helm-repository-placeholder"
>
Repositories
</div>
<div
class="Select__input-container css-6j8wv5-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-describedby="react-select-selection-of-active-public-helm-repository-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
id="selection-of-active-public-helm-repository"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<span
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
/>
<div
aria-hidden="true"
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
<button
class="Button primary"
data-testid="add-custom-helm-repo-button"
type="button"
>
Add Custom Helm Repo
</button>
</div>
<div
class="repos"
>
<div
class="item flex gaps align-center justify-space-between mt-3"
>
<div
class="repoName"
data-testid="helm-repository-some-active-repository"
>
some-active-repository
</div>
<div
class="repoUrl"
>
some-url
</div>
<i
class="Icon material interactive focusable"
data-testid="deactivate-helm-repository-some-active-repository"
tabindex="0"
tooltip="Remove"
>
<span
class="icon"
data-icon-name="delete"
>
delete
</span>
</i>
</div>
</div>
<div />
</div>
<div />
</div>
</section>
</section>
@ -1711,118 +1723,122 @@ exports[`deactivate helm repository from list of active repositories in preferen
</h2>
<div>
<div
class="flex gaps"
data-testid="helm-controls"
>
<div
class="Select theme-lens box grow Select--is-disabled css-3iigni-container"
class="flex gaps"
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-selection-of-active-public-helm-repository-live-region"
/>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__control Select__control--is-disabled css-1insrsq-control"
class="Select theme-lens box grow Select--is-disabled css-3iigni-container"
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-selection-of-active-public-helm-repository-live-region"
/>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__value-container css-319lph-ValueContainer"
class="Select__control Select__control--is-disabled css-1insrsq-control"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-selection-of-active-public-helm-repository-placeholder"
class="Select__value-container css-319lph-ValueContainer"
>
Repositories
</div>
<div
class="Select__input-container css-jzldcf-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-describedby="react-select-selection-of-active-public-helm-repository-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
disabled=""
id="selection-of-active-public-helm-repository"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<div
aria-hidden="true"
class="Select__indicator Select__loading-indicator css-at12u2-loadingIndicator"
>
<span
class="css-1xtdfmb-LoadingDot"
/>
<span
class="css-zoievk-LoadingDot"
/>
<span
class="css-x748d8-LoadingDot"
/>
</div>
<span
class="Select__indicator-separator css-109onse-indicatorSeparator"
/>
<div
aria-hidden="true"
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-selection-of-active-public-helm-repository-placeholder"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
Repositories
</div>
<div
class="Select__input-container css-jzldcf-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-describedby="react-select-selection-of-active-public-helm-repository-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
disabled=""
id="selection-of-active-public-helm-repository"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</svg>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<div
aria-hidden="true"
class="Select__indicator Select__loading-indicator css-at12u2-loadingIndicator"
>
<span
class="css-1xtdfmb-LoadingDot"
/>
<span
class="css-zoievk-LoadingDot"
/>
<span
class="css-x748d8-LoadingDot"
/>
</div>
<span
class="Select__indicator-separator css-109onse-indicatorSeparator"
/>
<div
aria-hidden="true"
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
<button
class="Button primary"
data-testid="add-custom-helm-repo-button"
type="button"
>
Add Custom Helm Repo
</button>
</div>
<button
class="Button primary"
data-testid="add-custom-helm-repo-button"
type="button"
>
Add Custom Helm Repo
</button>
</div>
<div
class="repos"
>
<div
class="pt-5 relative"
class="repos"
>
<div
class="Spinner singleColor center"
data-testid="helm-repositories-are-loading"
/>
class="pt-5 relative"
>
<div
class="Spinner singleColor center"
data-testid="helm-repositories-are-loading"
/>
</div>
</div>
<div />
</div>
<div />
</div>
</section>
</section>

View File

@ -14,8 +14,9 @@ import getActiveHelmRepositoriesInjectable from "../../main/helm/repositories/ge
import type { HelmRepo } from "../../common/helm-repo";
import callForPublicHelmRepositoriesInjectable from "../../renderer/components/+preferences/kubernetes/helm-charts/activation-of-public-helm-repository/public-helm-repositories/call-for-public-helm-repositories.injectable";
import isPathInjectable from "../../renderer/components/input/validators/is-path.injectable";
import showSuccessNotificationInjectable
from "../../renderer/components/notifications/show-success-notification.injectable";
import showSuccessNotificationInjectable from "../../renderer/components/notifications/show-success-notification.injectable";
import showErrorNotificationInjectable from "../../renderer/components/notifications/show-error-notification.injectable";
import type { AsyncResult } from "../../common/utils/async-result";
// TODO: Make tooltips free of side effects by making it deterministic
jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
@ -25,11 +26,12 @@ jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
describe("activate custom helm repository in preferences", () => {
let applicationBuilder: ApplicationBuilder;
let showSuccessNotificationMock: jest.Mock;
let showErrorNotificationMock: jest.Mock;
let rendered: RenderResult;
let execFileMock: AsyncFnMock<
ReturnType<typeof execFileInjectable["instantiate"]>
>;
let getActiveHelmRepositoriesMock: AsyncFnMock<() => Promise<HelmRepo[]>>;
let getActiveHelmRepositoriesMock: AsyncFnMock<() => AsyncResult<HelmRepo[]>>;
beforeEach(async () => {
jest.useFakeTimers("modern");
@ -46,6 +48,10 @@ describe("activate custom helm repository in preferences", () => {
rendererDi.override(showSuccessNotificationInjectable, () => showSuccessNotificationMock);
showErrorNotificationMock = jest.fn();
rendererDi.override(showErrorNotificationInjectable, () => showErrorNotificationMock);
// TODO: Figure out how to make async validators unit testable
rendererDi.override(isPathInjectable, () => ({ debounce: 0, validate: async () => {} }));
@ -74,9 +80,12 @@ describe("activate custom helm repository in preferences", () => {
describe("when active repositories resolve", () => {
beforeEach(async () => {
await Promise.all([
getActiveHelmRepositoriesMock.resolve([
{ name: "Some active repository", url: "some-url" },
]),
getActiveHelmRepositoriesMock.resolve({
callWasSuccessful: true,
response: [
{ name: "Some active repository", url: "some-url" },
],
}),
]);
});
@ -180,7 +189,37 @@ describe("activate custom helm repository in preferences", () => {
expect(showSuccessNotificationMock).not.toHaveBeenCalled();
});
describe("when activating resolves", () => {
describe("when activation rejects", () => {
beforeEach(async () => {
await execFileMock.reject(
"Some error",
);
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("shows error notification", () => {
expect(showErrorNotificationMock).toHaveBeenCalledWith(
"Some error",
);
});
it("does not show success notification", () => {
expect(showSuccessNotificationMock).not.toHaveBeenCalled();
});
it("does not show dialog anymore", () => {
expect(rendered.queryByTestId("activate-custom-helm-repository-dialog")).not.toBeInTheDocument();
});
it("does not reload active repositories", () => {
expect(getActiveHelmRepositoriesMock).not.toHaveBeenCalled();
});
});
describe("when activation resolves with success", () => {
beforeEach(async () => {
await execFileMock.resolveSpecific(
[
@ -204,7 +243,7 @@ describe("activate custom helm repository in preferences", () => {
expect(getActiveHelmRepositoriesMock).toHaveBeenCalled();
});
it("shows the notification", () => {
it("shows success notification", () => {
expect(showSuccessNotificationMock).toHaveBeenCalledWith(
"Helm repository some-custom-repository has been added.",
);

View File

@ -12,6 +12,11 @@ import helmBinaryPathInjectable from "../../main/helm/helm-binary-path.injectabl
import getActiveHelmRepositoriesInjectable from "../../main/helm/repositories/get-active-helm-repositories/get-active-helm-repositories.injectable";
import type { HelmRepo } from "../../common/helm-repo";
import callForPublicHelmRepositoriesInjectable from "../../renderer/components/+preferences/kubernetes/helm-charts/activation-of-public-helm-repository/public-helm-repositories/call-for-public-helm-repositories.injectable";
import showSuccessNotificationInjectable
from "../../renderer/components/notifications/show-success-notification.injectable";
import showErrorNotificationInjectable
from "../../renderer/components/notifications/show-error-notification.injectable";
import type { AsyncResult } from "../../common/utils/async-result";
// TODO: Make tooltips free of side effects by making it deterministic
jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
@ -21,11 +26,13 @@ jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
describe("activate helm repository from list in preferences", () => {
let applicationBuilder: ApplicationBuilder;
let showSuccessNotificationMock: jest.Mock;
let showErrorNotificationMock: jest.Mock;
let rendered: RenderResult;
let execFileMock: AsyncFnMock<
ReturnType<typeof execFileInjectable["instantiate"]>
>;
let getActiveHelmRepositoriesMock: AsyncFnMock<() => Promise<HelmRepo[]>>;
let getActiveHelmRepositoriesMock: AsyncFnMock<() => AsyncResult<HelmRepo[]>>;
let callForPublicHelmRepositoriesMock: AsyncFnMock<() => Promise<HelmRepo[]>>;
beforeEach(async () => {
@ -36,6 +43,14 @@ describe("activate helm repository from list in preferences", () => {
callForPublicHelmRepositoriesMock = asyncFn();
applicationBuilder.beforeApplicationStart(({ mainDi, rendererDi }) => {
showSuccessNotificationMock = jest.fn();
rendererDi.override(showSuccessNotificationInjectable, () => showSuccessNotificationMock);
showErrorNotificationMock = jest.fn();
rendererDi.override(showErrorNotificationInjectable, () => showErrorNotificationMock);
rendererDi.override(
callForPublicHelmRepositoriesInjectable,
() => callForPublicHelmRepositoriesMock,
@ -78,9 +93,12 @@ describe("activate helm repository from list in preferences", () => {
{ name: "Some to be activated repository", url: "some-other-url" },
]),
getActiveHelmRepositoriesMock.resolve([
{ name: "Some already active repository", url: "some-url" },
]),
getActiveHelmRepositoriesMock.resolve({
callWasSuccessful: true,
response: [
{ name: "Some already active repository", url: "some-url" },
],
}),
]);
});
@ -124,6 +142,36 @@ describe("activate helm repository from list in preferences", () => {
expect(getActiveHelmRepositoriesMock).not.toHaveBeenCalled();
});
describe("when activation rejects", () => {
beforeEach(async () => {
await execFileMock.reject(
"Some error",
);
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("shows error notification", () => {
expect(showErrorNotificationMock).toHaveBeenCalledWith(
"Some error",
);
});
it("does not show success notification", () => {
expect(showSuccessNotificationMock).not.toHaveBeenCalled();
});
it("does not show dialog anymore", () => {
expect(rendered.queryByTestId("activate-custom-helm-repository-dialog")).not.toBeInTheDocument();
});
it("does not reload active repositories", () => {
expect(getActiveHelmRepositoriesMock).not.toHaveBeenCalled();
});
});
describe("when activating resolves", () => {
beforeEach(async () => {
await execFileMock.resolveSpecific(
@ -144,12 +192,21 @@ describe("activate helm repository from list in preferences", () => {
expect(getActiveHelmRepositoriesMock).toHaveBeenCalled();
});
it("shows success notification", () => {
expect(showSuccessNotificationMock).toHaveBeenCalledWith(
"Helm repository Some to be activated repository has been added.",
);
});
describe("when active repositories resolve again", () => {
beforeEach(async () => {
await getActiveHelmRepositoriesMock.resolve([
{ name: "Some already active repository", url: "some-url" },
{ name: "Some to be activated repository", url: "some-other-url" },
]);
await getActiveHelmRepositoriesMock.resolve({
callWasSuccessful: true,
response: [
{ name: "Some already active repository", url: "some-url" },
{ name: "Some to be activated repository", url: "some-other-url" },
],
});
});
it("renders", () => {

View File

@ -13,6 +13,7 @@ import helmBinaryPathInjectable from "../../main/helm/helm-binary-path.injectabl
import getActiveHelmRepositoriesInjectable from "../../main/helm/repositories/get-active-helm-repositories/get-active-helm-repositories.injectable";
import type { HelmRepo } from "../../common/helm-repo";
import callForPublicHelmRepositoriesInjectable from "../../renderer/components/+preferences/kubernetes/helm-charts/activation-of-public-helm-repository/public-helm-repositories/call-for-public-helm-repositories.injectable";
import type { AsyncResult } from "../../common/utils/async-result";
// TODO: Make tooltips free of side effects by making it deterministic
jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
@ -22,7 +23,7 @@ jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
describe("deactivate helm repository from list of active repositories in preferences", () => {
let applicationBuilder: ApplicationBuilder;
let rendered: RenderResult;
let getActiveHelmRepositoriesMock: AsyncFnMock<() => Promise<HelmRepo[]>>;
let getActiveHelmRepositoriesMock: AsyncFnMock<() => AsyncResult<HelmRepo[]>>;
let execFileMock: AsyncFnMock<
ReturnType<typeof execFileInjectable["instantiate"]>
>;
@ -60,9 +61,12 @@ describe("deactivate helm repository from list of active repositories in prefere
describe("when active repositories resolve", () => {
beforeEach(async () => {
getActiveHelmRepositoriesMock.resolve([
{ name: "some-active-repository", url: "some-url" },
]);
getActiveHelmRepositoriesMock.resolve({
callWasSuccessful: true,
response: [
{ name: "some-active-repository", url: "some-url" },
],
});
});
it("renders", () => {

View File

@ -15,6 +15,8 @@ import helmBinaryPathInjectable from "../../main/helm/helm-binary-path.injectabl
import loggerInjectable from "../../common/logger.injectable";
import type { Logger } from "../../common/logger";
import callForPublicHelmRepositoriesInjectable from "../../renderer/components/+preferences/kubernetes/helm-charts/activation-of-public-helm-repository/public-helm-repositories/call-for-public-helm-repositories.injectable";
import showErrorNotificationInjectable
from "../../renderer/components/notifications/show-error-notification.injectable";
// TODO: Make tooltips free of side effects by making it deterministic
jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
@ -27,6 +29,7 @@ describe("listing active helm repositories in preferences", () => {
let readYamlFileMock: AsyncFnMock<ReadYamlFile>;
let execFileMock: AsyncFnMock<ReturnType<typeof execFileInjectable["instantiate"]>>;
let loggerStub: Logger;
let showErrorNotificationMock: jest.Mock;
beforeEach(async () => {
applicationBuilder = getApplicationBuilder();
@ -34,9 +37,12 @@ describe("listing active helm repositories in preferences", () => {
readYamlFileMock = asyncFn();
execFileMock = asyncFn();
loggerStub = { warn: jest.fn() } as unknown as Logger;
loggerStub = { error: jest.fn() } as unknown as Logger;
applicationBuilder.beforeApplicationStart(({ mainDi, rendererDi }) => {
showErrorNotificationMock = jest.fn();
rendererDi.override(showErrorNotificationInjectable, () => showErrorNotificationMock);
rendererDi.override(callForPublicHelmRepositoriesInjectable, () => async () => []);
mainDi.override(readYamlFileInjectable, () => readYamlFileMock);
mainDi.override(execFileInjectable, () => execFileMock);
@ -77,6 +83,34 @@ describe("listing active helm repositories in preferences", () => {
);
});
describe("when getting configuration rejects", () => {
beforeEach(async () => {
await execFileMock.reject("some-error");
});
it("shows error notification", () => {
expect(showErrorNotificationMock).toHaveBeenCalledWith(
"Error getting Helm configuration: some-error",
);
});
it("removes all helm controls", () => {
expect(
rendered.queryByTestId("helm-controls"),
).not.toBeInTheDocument();
});
it("does not show loader for repositories anymore", () => {
expect(
rendered.queryByTestId("helm-repositories-are-loading"),
).not.toBeInTheDocument();
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
});
describe("when configuration resolves without path to repository config file", () => {
beforeEach(async () => {
execFileMock.mockClear();
@ -87,20 +121,22 @@ describe("listing active helm repositories in preferences", () => {
);
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("logs error", () => {
expect(loggerStub.warn).toHaveBeenCalledWith(
"Tried to get Helm repositories, but HELM_REPOSITORY_CONFIG was not present in `$ helm env`. Behaving as if there were no repositories.",
expect(loggerStub.error).toHaveBeenCalledWith(
"Tried to get Helm repositories, but HELM_REPOSITORY_CONFIG was not present in `$ helm env`.",
);
});
it("shows message about no repositories found", () => {
it("shows error notification", () => {
expect(showErrorNotificationMock).toHaveBeenCalledWith(
"Error getting Helm configuration: Tried to get Helm repositories, but HELM_REPOSITORY_CONFIG was not present in `$ helm env`.",
);
});
it("removes all helm controls", () => {
expect(
rendered.getByTestId("no-helm-repositories"),
).toBeInTheDocument();
rendered.queryByTestId("helm-controls"),
).not.toBeInTheDocument();
});
it("does not show loader for repositories anymore", () => {
@ -109,11 +145,8 @@ describe("listing active helm repositories in preferences", () => {
).not.toBeInTheDocument();
});
it("does not call for updating of repositories", () => {
expect(execFileMock).not.toHaveBeenCalledWith(
"some-helm-binary-path",
["repo", "update"],
);
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
});
@ -127,20 +160,22 @@ describe("listing active helm repositories in preferences", () => {
);
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("logs error", () => {
expect(loggerStub.warn).toHaveBeenCalledWith(
"Tried to get Helm repositories, but HELM_REPOSITORY_CACHE was not present in `$ helm env`. Behaving as if there were no repositories.",
expect(loggerStub.error).toHaveBeenCalledWith(
"Tried to get Helm repositories, but HELM_REPOSITORY_CACHE was not present in `$ helm env`.",
);
});
it("shows message about no repositories found", () => {
it("shows error notification", () => {
expect(showErrorNotificationMock).toHaveBeenCalledWith(
"Error getting Helm configuration: Tried to get Helm repositories, but HELM_REPOSITORY_CACHE was not present in `$ helm env`.",
);
});
it("removes all helm controls", () => {
expect(
rendered.getByTestId("no-helm-repositories"),
).toBeInTheDocument();
rendered.queryByTestId("helm-controls"),
).not.toBeInTheDocument();
});
it("does not show loader for repositories anymore", () => {
@ -149,11 +184,8 @@ describe("listing active helm repositories in preferences", () => {
).not.toBeInTheDocument();
});
it("does not call for updating of repositories", () => {
expect(execFileMock).not.toHaveBeenCalledWith(
"some-helm-binary-path",
["repo", "update"],
);
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
});
@ -186,6 +218,34 @@ describe("listing active helm repositories in preferences", () => {
expect(readYamlFileMock).not.toHaveBeenCalled();
});
describe("when updating repositories reject", () => {
beforeEach(async () => {
await execFileMock.reject("Some error");
});
it("shows error notification", () => {
expect(showErrorNotificationMock).toHaveBeenCalledWith(
"Error updating Helm repositories: Some error",
);
});
it("removes all helm controls", () => {
expect(
rendered.queryByTestId("helm-controls"),
).not.toBeInTheDocument();
});
it("does not show loader for repositories anymore", () => {
expect(
rendered.queryByTestId("helm-repositories-are-loading"),
).not.toBeInTheDocument();
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
});
describe("when updating repositories resolve", () => {
beforeEach(async () => {
execFileMock.mockClear();
@ -196,6 +256,12 @@ describe("listing active helm repositories in preferences", () => {
);
});
it("loads repositories from file system", () => {
expect(readYamlFileMock).toHaveBeenCalledWith(
"some-helm-repository-config-file.yaml",
);
});
describe("when loading repositories resolves with existing repositories", () => {
beforeEach(async () => {
execFileMock.mockClear();
@ -252,12 +318,6 @@ describe("listing active helm repositories in preferences", () => {
).toBeInTheDocument();
});
it("does not show message about no repositories", () => {
expect(
rendered.queryByTestId("no-helm-repositories"),
).not.toBeInTheDocument();
});
it('adds "bitnami" as default repository', () => {
expect(execFileMock).toHaveBeenCalledWith(
"some-helm-binary-path",
@ -265,6 +325,34 @@ describe("listing active helm repositories in preferences", () => {
);
});
describe("when adding default repository reject", () => {
beforeEach(async () => {
await execFileMock.reject("Some error");
});
it("shows error notification", () => {
expect(showErrorNotificationMock).toHaveBeenCalledWith(
"Error when adding default Helm repository: Some error",
);
});
it("removes all helm controls", () => {
expect(
rendered.queryByTestId("helm-controls"),
).not.toBeInTheDocument();
});
it("does not show loader for repositories anymore", () => {
expect(
rendered.queryByTestId("helm-repositories-are-loading"),
).not.toBeInTheDocument();
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
});
describe("when adding of default repository resolves", () => {
beforeEach(async () => {
readYamlFileMock.mockClear();
@ -295,13 +383,6 @@ describe("listing active helm repositories in preferences", () => {
).toBeInTheDocument();
});
it("does not show message about no repositories", () => {
expect(
rendered.queryByTestId("no-helm-repositories"),
).not.toBeInTheDocument();
});
it("calls for repositories again", () => {
expect(readYamlFileMock).toHaveBeenCalledWith(
"some-helm-repository-config-file.yaml",
@ -342,12 +423,6 @@ describe("listing active helm repositories in preferences", () => {
expect(actual).toBeInTheDocument();
});
it("does not show message about no repositories", () => {
expect(
rendered.queryByTestId("no-helm-repositories"),
).not.toBeInTheDocument();
});
});
});
});

View File

@ -4,18 +4,20 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { execFile } from "child_process";
import { promisify } from "util";
export type ExecFile = (filePath: string, args: string[]) => Promise<string>;
const execFileInjectable = getInjectable({
id: "exec-file",
instantiate: (): ExecFile => async (filePath, args) =>
new Promise((resolve) => {
execFile(filePath, args, (error, stdout) => {
resolve(stdout);
});
}),
instantiate: (): ExecFile => async (filePath, args) => {
const asyncExecFile = promisify(execFile);
const result = await asyncExecFile(filePath, args);
return result.stdout;
},
causesSideEffects: true,
});

View File

@ -6,8 +6,9 @@ import { getInjectable } from "@ogre-tools/injectable";
import type { HelmRepo } from "../helm-repo";
import type { RequestChannel } from "../utils/channel/request-channel-injection-token";
import { requestChannelInjectionToken } from "../utils/channel/request-channel-injection-token";
import type { AsyncResult } from "../utils/async-result";
export type ActivateHelmRepositoryChannel = RequestChannel<HelmRepo>;
export type ActivateHelmRepositoryChannel = RequestChannel<HelmRepo, AsyncResult<string>>;
const activateHelmRepositoryChannelInjectable = getInjectable({
id: "activate-helm-repository-channel",

View File

@ -6,8 +6,9 @@ import { getInjectable } from "@ogre-tools/injectable";
import type { RequestChannel } from "../utils/channel/request-channel-injection-token";
import type { HelmRepo } from "../helm-repo";
import { requestChannelInjectionToken } from "../utils/channel/request-channel-injection-token";
import type { AsyncResult } from "../utils/async-result";
export type GetHelmRepositoriesChannel = RequestChannel<void, HelmRepo[]>;
export type GetHelmRepositoriesChannel = RequestChannel<void, AsyncResult<HelmRepo[]>>;
const getActiveHelmRepositoriesChannelInjectable = getInjectable({
id: "get-active-helm-repositories-channel",

View File

@ -0,0 +1,7 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export type AsyncResult<Response, Error = string> =
| { callWasSuccessful: true; response: Response }
| { callWasSuccessful: false; error: Error };

View File

@ -0,0 +1,15 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export const getErrorMessage = (error: unknown): string => {
if (typeof error === "string") {
return error;
}
if (error instanceof Error) {
return error.message;
}
return JSON.stringify(error);
};

View File

@ -5,6 +5,8 @@
import { getInjectable } from "@ogre-tools/injectable";
import execFileInjectable from "../../../common/fs/exec-file.injectable";
import helmBinaryPathInjectable from "../helm-binary-path.injectable";
import type { AsyncResult } from "../../../common/utils/async-result";
import { getErrorMessage } from "../../../common/utils/get-error-message";
const execHelmInjectable = getInjectable({
id: "exec-helm",
@ -13,8 +15,17 @@ const execHelmInjectable = getInjectable({
const execFile = di.inject(execFileInjectable);
const helmBinaryPath = di.inject(helmBinaryPathInjectable);
return (...args: string[]) => execFile(helmBinaryPath, args);
return async (...args: string[]): Promise<AsyncResult<string>> => {
try {
const response = await execFile(helmBinaryPath, args);
return { callWasSuccessful: true, response };
} catch (error) {
return { callWasSuccessful: false, error: getErrorMessage(error) };
}
};
},
});
export default execHelmInjectable;

View File

@ -4,6 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import execHelmInjectable from "../exec-helm/exec-helm.injectable";
import type { AsyncResult } from "../../../common/utils/async-result";
export type HelmEnv = Record<string, string> & {
HELM_REPOSITORY_CACHE?: string;
@ -16,10 +17,14 @@ const getHelmEnvInjectable = getInjectable({
instantiate: (di) => {
const execHelm = di.inject(execHelmInjectable);
return async () => {
const output = await execHelm("env");
return async (): Promise<AsyncResult<HelmEnv>> => {
const result = await execHelm("env");
const lines = output.split(/\r?\n/); // split by new line feed
if (!result.callWasSuccessful) {
return { callWasSuccessful: false, error: result.error };
}
const lines = result.response.split(/\r?\n/); // split by new line feed
const env: HelmEnv = {};
lines.forEach((line: string) => {
@ -30,7 +35,7 @@ const getHelmEnvInjectable = getInjectable({
}
});
return env;
return { callWasSuccessful: true, response: env };
};
},
});

View File

@ -3,6 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import { object } from "../../../common/utils";
import { HelmChartManager } from "../helm-chart-manager";
import getActiveHelmRepositoriesInjectable from "../repositories/get-active-helm-repositories/get-active-helm-repositories.injectable";
@ -14,7 +15,11 @@ const listHelmChartsInjectable = getInjectable({
const getActiveHelmRepositories = di.inject(getActiveHelmRepositoriesInjectable);
return async () => {
const repositories = await getActiveHelmRepositories();
const result = await getActiveHelmRepositories();
assert(result.callWasSuccessful);
const repositories = result.response;
return object.fromEntries(
await Promise.all(

View File

@ -54,7 +54,7 @@ const activateHelmRepositoryInjectable = getInjectable({
args.push("--cert-file", certFile);
}
await execHelm(...args);
return await execHelm(...args);
};
},
});

View File

@ -10,6 +10,7 @@ import getHelmEnvInjectable from "../../get-helm-env/get-helm-env.injectable";
import execHelmInjectable from "../../exec-helm/exec-helm.injectable";
import { isEmpty } from "lodash/fp";
import loggerInjectable from "../../../../common/logger.injectable";
import type { AsyncResult } from "../../../../common/utils/async-result";
interface HelmRepositoryFromYaml {
name: string;
@ -38,23 +39,41 @@ const getActiveHelmRepositoriesInjectable = getInjectable({
const getRepositoriesFor = getRepositoriesForFor(readYamlFile);
return async (): Promise<HelmRepo[]> => {
const { HELM_REPOSITORY_CONFIG: repositoryConfigFilePath, HELM_REPOSITORY_CACHE: helmRepositoryCacheDirPath } = await getHelmEnv();
return async (): Promise<AsyncResult<HelmRepo[]>> => {
const envResult = await getHelmEnv();
if (!envResult.callWasSuccessful) {
return {
callWasSuccessful: false,
error: `Error getting Helm configuration: ${envResult.error}`,
};
}
const {
HELM_REPOSITORY_CONFIG: repositoryConfigFilePath,
HELM_REPOSITORY_CACHE: helmRepositoryCacheDirPath,
} = envResult.response;
if (!repositoryConfigFilePath) {
logger.warn("Tried to get Helm repositories, but HELM_REPOSITORY_CONFIG was not present in `$ helm env`. Behaving as if there were no repositories.");
const errorMessage = "Tried to get Helm repositories, but HELM_REPOSITORY_CONFIG was not present in `$ helm env`.";
return [];
logger.error(errorMessage);
return {
callWasSuccessful: false,
error: `Error getting Helm configuration: ${errorMessage}`,
};
}
if (!helmRepositoryCacheDirPath) {
logger.warn("Tried to get Helm repositories, but HELM_REPOSITORY_CACHE was not present in `$ helm env`. Behaving as if there were no repositories.");
const errorMessage = "Tried to get Helm repositories, but HELM_REPOSITORY_CACHE was not present in `$ helm env`.";
return [];
}
logger.error(errorMessage);
if (!repositoryConfigFilePath || !helmRepositoryCacheDirPath) {
return [];
return {
callWasSuccessful: false,
error: `Error getting Helm configuration: ${errorMessage}`,
};
}
const getRepositories = getRepositoriesFor(
@ -62,17 +81,31 @@ const getActiveHelmRepositoriesInjectable = getInjectable({
helmRepositoryCacheDirPath,
);
await execHelm("repo", "update");
const updateResult = await execHelm("repo", "update");
if (!updateResult.callWasSuccessful) {
return {
callWasSuccessful: false,
error: `Error updating Helm repositories: ${updateResult.error}`,
};
}
const repositories = await getRepositories();
if (isEmpty(repositories)) {
await execHelm("repo", "add", "bitnami", "https://charts.bitnami.com/bitnami");
const resultOfAddingDefaultRepository = await execHelm("repo", "add", "bitnami", "https://charts.bitnami.com/bitnami");
return await getRepositories();
if (!resultOfAddingDefaultRepository.callWasSuccessful) {
return {
callWasSuccessful: false,
error: `Error when adding default Helm repository: ${resultOfAddingDefaultRepository.error}`,
};
}
return { callWasSuccessful: true, response: await getRepositories() };
}
return repositories;
return { callWasSuccessful: true, response: repositories };
};
},
});
@ -82,7 +115,7 @@ export default getActiveHelmRepositoriesInjectable;
const getRepositoriesForFor =
(readYamlFile: ReadYamlFile) =>
(repositoryConfigFilePath: string, helmRepositoryCacheDirPath: string) =>
async () => {
async (): Promise<HelmRepo[]> => {
const { repositories } = (await readYamlFile(
repositoryConfigFilePath,
)) as HelmRepositoriesFromYaml;

View File

@ -3,6 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import getActiveHelmRepositoriesInjectable from "./get-active-helm-repositories/get-active-helm-repositories.injectable";
const getActiveHelmRepositoryInjectable = getInjectable({
@ -14,7 +15,9 @@ const getActiveHelmRepositoryInjectable = getInjectable({
return async (name: string) => {
const activeHelmRepositories = await getActiveHelmRepositories();
return activeHelmRepositories.find(
assert(activeHelmRepositories.callWasSuccessful);
return activeHelmRepositories.response.find(
(repository) => repository.name === name,
);
};

View File

@ -6,7 +6,6 @@ import { getInjectable } from "@ogre-tools/injectable";
import type { HelmRepo } from "../../../../../../common/helm-repo";
import activateHelmRepositoryInjectable from "../activation-of-public-helm-repository/select-helm-repository/activate-helm-repository.injectable";
import hideDialogForActivatingCustomHelmRepositoryInjectable from "./dialog-visibility/hide-dialog-for-activating-custom-helm-repository.injectable";
import showSuccessNotificationInjectable from "../../../../notifications/show-success-notification.injectable";
const submitCustomHelmRepositoryInjectable = getInjectable({
id: "submit-custom-helm-repository",
@ -14,13 +13,10 @@ const submitCustomHelmRepositoryInjectable = getInjectable({
instantiate: (di) => {
const activateHelmRepository = di.inject(activateHelmRepositoryInjectable);
const hideDialog = di.inject(hideDialogForActivatingCustomHelmRepositoryInjectable);
const showSuccessNotification = di.inject(showSuccessNotificationInjectable);
return async (repository: HelmRepo) => {
await activateHelmRepository(repository);
showSuccessNotification(`Helm repository ${repository.name} has been added.`);
hideDialog();
};
},

View File

@ -7,6 +7,8 @@ import activateHelmRepositoryChannelInjectable from "../../../../../../../common
import type { HelmRepo } from "../../../../../../../common/helm-repo";
import { requestFromChannelInjectionToken } from "../../../../../../../common/utils/channel/request-from-channel-injection-token";
import activeHelmRepositoriesInjectable from "../../active-helm-repositories.injectable";
import showErrorNotificationInjectable from "../../../../../notifications/show-error-notification.injectable";
import showSuccessNotificationInjectable from "../../../../../notifications/show-success-notification.injectable";
const activateHelmRepositoryInjectable = getInjectable({
id: "activate-public-helm-repository",
@ -15,11 +17,24 @@ const activateHelmRepositoryInjectable = getInjectable({
const requestFromChannel = di.inject(requestFromChannelInjectionToken);
const activateHelmRepositoryChannel = di.inject(activateHelmRepositoryChannelInjectable);
const activeHelmRepositories = di.inject(activeHelmRepositoriesInjectable);
const showErrorNotification = di.inject(showErrorNotificationInjectable);
const showSuccessNotification = di.inject(showSuccessNotificationInjectable);
return async (repository: HelmRepo) => {
await requestFromChannel(activateHelmRepositoryChannel, repository);
const result = await requestFromChannel(
activateHelmRepositoryChannel,
repository,
);
activeHelmRepositories.invalidate();
if (result.callWasSuccessful) {
showSuccessNotification(
`Helm repository ${repository.name} has been added.`,
);
activeHelmRepositories.invalidate();
} else {
showErrorNotification(result.error);
}
};
},
});

View File

@ -4,20 +4,39 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { asyncComputed } from "@ogre-tools/injectable-react";
import requestFromChannelInjectable from "../../../../utils/channel/request-from-channel.injectable";
import getActiveHelmRepositoriesChannelInjectable from "../../../../../common/helm/get-active-helm-repositories-channel.injectable";
import { requestFromChannelInjectionToken } from "../../../../../common/utils/channel/request-from-channel-injection-token";
import showErrorNotificationInjectable from "../../../notifications/show-error-notification.injectable";
import helmRepositoriesErrorStateInjectable from "./helm-repositories-error-state.injectable";
import { runInAction } from "mobx";
const activeHelmRepositoriesInjectable = getInjectable({
id: "active-helm-repositories",
instantiate: (di) => {
const requestFromChannel = di.inject(requestFromChannelInjectable);
const requestFromChannel = di.inject(requestFromChannelInjectionToken);
const getHelmRepositoriesChannel = di.inject(getActiveHelmRepositoriesChannelInjectable);
const showErrorNotification = di.inject(showErrorNotificationInjectable);
const helmRepositoriesErrorState = di.inject(helmRepositoriesErrorStateInjectable);
return asyncComputed(
async () => await requestFromChannel(getHelmRepositoriesChannel),
[],
);
return asyncComputed(async () => {
const result = await requestFromChannel(getHelmRepositoriesChannel);
if (result.callWasSuccessful) {
return result.response;
} else {
showErrorNotification(result.error);
runInAction(() =>
helmRepositoriesErrorState.set({
controlsAreShown: false,
errorMessage: result.error,
}),
);
return [];
}
}, []);
},
});

View File

@ -3,25 +3,59 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import React from "react";
import { HelmRepositories } from "./helm-repositories";
import { ActivationOfPublicHelmRepository } from "./activation-of-public-helm-repository/activation-of-public-helm-repository";
import { ActivationOfCustomHelmRepositoryOpenButton } from "./activation-of-custom-helm-repository/activation-of-custom-helm-repository-open-button";
import { ActivationOfCustomHelmRepositoryDialog } from "./activation-of-custom-helm-repository/activation-of-custom-helm-repository-dialog";
import { withInjectables } from "@ogre-tools/injectable-react";
import type { HelmRepositoriesErrorState } from "./helm-repositories-error-state.injectable";
import helmRepositoriesErrorStateInjectable from "./helm-repositories-error-state.injectable";
import type { IObservableValue } from "mobx";
import { observer } from "mobx-react";
import { Notice } from "../../../+extensions/notice";
export const HelmCharts = () => (
<div>
<div className="flex gaps">
<ActivationOfPublicHelmRepository />
interface Dependencies {
helmRepositoriesErrorState: IObservableValue<HelmRepositoriesErrorState>;
}
<ActivationOfCustomHelmRepositoryOpenButton />
</div>
const NonInjectedHelmCharts = observer(
({ helmRepositoriesErrorState }: Dependencies) => {
const state = helmRepositoriesErrorState.get();
<HelmRepositories />
return (
<div>
{!state.controlsAreShown && (
<Notice>
<div className="flex-grow text-center">{state.errorMessage}</div>
</Notice>
)}
<ActivationOfCustomHelmRepositoryDialog />
</div>
{state.controlsAreShown && (
<div data-testid="helm-controls">
<div className="flex gaps">
<ActivationOfPublicHelmRepository />
<ActivationOfCustomHelmRepositoryOpenButton />
</div>
<HelmRepositories />
<ActivationOfCustomHelmRepositoryDialog />
</div>
)}
</div>
);
},
);
export const HelmCharts = withInjectables<Dependencies>(
NonInjectedHelmCharts,
{
getProps: (di) => ({
helmRepositoriesErrorState: di.inject(helmRepositoriesErrorStateInjectable),
}),
},
);

View File

@ -0,0 +1,19 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { observable } from "mobx";
export type HelmRepositoriesErrorState =
| { controlsAreShown: true }
| { controlsAreShown: false; errorMessage: string };
const helmRepositoriesErrorStateInjectable = getInjectable({
id: "helm-repositories-error-state",
instantiate: () =>
observable.box<HelmRepositoriesErrorState>({ controlsAreShown: true }),
});
export default helmRepositoriesErrorStateInjectable;

View File

@ -13,8 +13,6 @@ import type { IAsyncComputed } from "@ogre-tools/injectable-react";
import { withInjectables } from "@ogre-tools/injectable-react";
import { Spinner } from "../../../spinner";
import type { HelmRepo } from "../../../../../common/helm-repo";
import { Notice } from "../../../+extensions/notice";
import { isEmpty } from "lodash/fp";
import { RemovableItem } from "../../removable-item";
import deactivateHelmRepositoryInjectable from "./deactivate-helm-repository.injectable";
@ -36,18 +34,6 @@ const NonInjectedActiveHelmRepositories = observer(({ activeHelmRepositories, de
const repositories = activeHelmRepositories.value.get();
if (isEmpty(repositories)) {
return (
<div className={styles.repos}>
<Notice>
<div className="flex-grow text-center" data-testid="no-helm-repositories">
The repositories have not been added yet
</div>
</Notice>
</div>
);
}
return (
<div className={styles.repos}>
{repositories.map((repository) => (

View File

@ -0,0 +1,26 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { NotificationMessage, Notification } from "./notifications.store";
import { NotificationStatus } from "./notifications.store";
import notificationsStoreInjectable from "./notifications-store.injectable";
const showErrorNotificationInjectable = getInjectable({
id: "show-error-notification",
instantiate: (di) => {
const notificationsStore = di.inject(notificationsStoreInjectable);
return (message: NotificationMessage, customOpts: Partial<Omit<Notification, "message">> = {}) =>
notificationsStore.add({
status: NotificationStatus.ERROR,
timeout: 5000,
message,
...customOpts,
});
},
});
export default showErrorNotificationInjectable;