Mobx-6 migration (#2718)

* mobx-6 migration -- part 1

Signed-off-by: Roman <ixrock@gmail.com>

* mobx-6 migration -- part 2 (npx mobx-undecorate --keepDecorators)

Signed-off-by: Roman <ixrock@gmail.com>

* mobx-6 migration -- part 3 (more fixes)

Signed-off-by: Roman <ixrock@gmail.com>

* unwrap possible observables from IPC-messaging

Signed-off-by: Roman <ixrock@gmail.com>

* mobx-6 migration -- remove @autobind as class-decorator

Signed-off-by: Roman <ixrock@gmail.com>

* mobx-6: replacing @autobind() as method-decorator to @boundMethod

Signed-off-by: Roman <ixrock@gmail.com>

* mobx-6: use toJS()-wrapper since monkey-patching require(mobx).toJS doesn't work

Signed-off-by: Roman <ixrock@gmail.com>

* removed `@observable static`

Signed-off-by: Roman <ixrock@gmail.com>

* use {useDefineForClassFields: true} in tsconfig.json

Signed-off-by: Roman <ixrock@gmail.com>

* remove ExtendedObservableMap

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* fix: removed makeObservable(this) from "terminal-tab.tsx"

Signed-off-by: Roman <ixrock@gmail.com>

* storage-helper refactoring

Signed-off-by: Roman <ixrock@gmail.com>

* normalize usages of #observable-value.toJSON() / attempt to catch the wind

Signed-off-by: Roman <ixrock@gmail.com>

* refactoring, more possible branch fixes + lint

Signed-off-by: Roman <ixrock@gmail.com>

* debugging cluster-view error -- part 1

Signed-off-by: Roman <ixrock@gmail.com>

* fix: refreshing cluster-view on ready

Signed-off-by: Roman <ixrock@gmail.com>

* fix: various app-crashes related to KubeObject.spec.* access from "undefined"
fix: config-map-details crash

Signed-off-by: Roman <ixrock@gmail.com>

* fix: namespace-store refactoring / saving selected-namespaces to external json-file

Signed-off-by: Roman <ixrock@gmail.com>

* fix: don't cache mobx.when(() => this.someObservable) cause might not work as expected due later call of makeObservable(this) in constructor

Signed-off-by: Roman <ixrock@gmail.com>

* fix: app-crash on editing k8s resource

Signed-off-by: Roman <ixrock@gmail.com>

* fix: restore "all namespaces" on page reload

Signed-off-by: Roman <ixrock@gmail.com>

* - fix: persist table-sort params and cluster-view's sidebar state to lens-local-storage
- new-feature: auto-open main-window's devtools in development-mode (yes/no/ugly?)

Signed-off-by: Roman <ixrock@gmail.com>

* fix: crd definition details -> crashing with <AceEditor mode="json"> (added missing mode-file in ace-editor.tsx)

Signed-off-by: Roman <ixrock@gmail.com>

* fix: crd definitions -> groups selector couldn't deselect last selected option

Signed-off-by: Roman <ixrock@gmail.com>

* refactoring: extensions-api exports clarification for "@k8slens/extensions"

Signed-off-by: Roman <ixrock@gmail.com>

* fix: various app-crashes related to kube-events (events page, some details page, overview, etc.)

Signed-off-by: Roman <ixrock@gmail.com>

* Reverted "use {useDefineForClassFields: true} in tsconfig.json" (various app-crash fixes)
This flag seems to be not possible to use with class-inheritance in some cases.

Example / demo:
`KubeObject` class has initial type definitions for the fields like: "metadata", "kind", etc.
and constructor() has Object.assign(this, data);
Meanwhile child class, e.g. KubeEvent inherited from KubeObject and has it's own extra type definitions for underlying resource, e.g. "involvedObject", "source", etc.

So calling super(data) doesn't work as expected for child class as it's own type definitions overwrites data from parent's constructor with `undefined` at later point.

Signed-off-by: Roman <ixrock@gmail.com>

* master-merge lint-fixes

Signed-off-by: Roman <ixrock@gmail.com>

* catalog.tsx / catalog-entities.store.ts refactoring & fixes

Signed-off-by: Roman <ixrock@gmail.com>

* fix: Catalog -> Browse all tab

Signed-off-by: Roman <ixrock@gmail.com>

* fix: CommandPalette doesn't appear from global menu by click/hotkey

Signed-off-by: Roman <ixrock@gmail.com>

* - Merging interfaces & classses to avoid overwriting fields from parent's super(data)-call with Object.assign(this, data). Otherwise use "declare" keyword at class field definition.

- Revamping {useDefineForClassFields: true} to avoid issues with non-observable class fields in some cases (from previous commit):

```
@observer
export class CommandContainer extends React.Component<CommandContainerProps> {
  // without some defined initial value "commandComponent" is non-observable for some reasons
  // when tsconfig.ts has {useDefineForClassFields:false}
  @observable.ref commandComponent: React.ReactNode = null;

  constructor(props: CommandContainerProps) {
    super(props);
    makeObservable(this);
  }
```

Signed-off-by: Roman <ixrock@gmail.com>

* update KubeObject class type definition

Signed-off-by: Roman <ixrock@gmail.com>

* clean up / responding to comments

Signed-off-by: Roman <ixrock@gmail.com>

* fix: app-crash when navigating to catalog from active cluster-view, refactoring `catalog-entity-store`

Signed-off-by: Roman <ixrock@gmail.com>

* catalog-pusher clean up, replaced .observe_() to external observe() helper from "mobx"

Signed-off-by: Roman <ixrock@gmail.com>

* fix: catalog's items stale/non-observable (after connection to the cluster status still "disconnected"), lint-fixes

Signed-off-by: Roman <ixrock@gmail.com>

* fix: Catalog is empty after closing main-window and re-opening app from Tray

Signed-off-by: Roman <ixrock@gmail.com>

* fix: HotBar's icon context menu items non-observable (no "disconnect cluster", etc.)

Signed-off-by: Roman <ixrock@gmail.com>

* lint-fix/license check

Signed-off-by: Roman <ixrock@gmail.com>

* fix: redirect to catalog when disconnecting active cluster

Signed-off-by: Roman <ixrock@gmail.com>

* fix: refresh visibility of active cluster-view on switching from hotbar/catalog

Signed-off-by: Roman <ixrock@gmail.com>

* updated package.json for built-in extensions to use "*" version for packages served from main app

Signed-off-by: Roman <ixrock@gmail.com>

* - added missing makeObservable(this) to metrics-settings.tsx
- updated package-lock.json for built-in extensions
- lint fixes

Signed-off-by: Roman <ixrock@gmail.com>

* master-merge clean up fix, updated package-lock.json for built-in extensions after `make clean-extensions && make build-extensions`

Signed-off-by: Roman <ixrock@gmail.com>

* fix unit-tests

Signed-off-by: Roman <ixrock@gmail.com>

* master-merge fixes

Signed-off-by: Roman <ixrock@gmail.com>

* make lint happy

Signed-off-by: Roman <ixrock@gmail.com>

* reverted some changes, removed auto-opening devtools in dev-mode

Signed-off-by: Roman <ixrock@gmail.com>

* merge fixes

Signed-off-by: Roman <ixrock@gmail.com>

* master-merge conflict fixes:
- proper handling and navigating into catalog's active category via URL-builder

Signed-off-by: Roman <ixrock@gmail.com>

* reverting splitted params for catalog's page route to "/catalog/:group?/:kind?"

Signed-off-by: Roman <ixrock@gmail.com>

* clean-up: remove app's injecting dependencies from `extensions/kube-object-event-status/package.json`

Signed-off-by: Roman <ixrock@gmail.com>

* master-merge fix: added missing makeObservable(this) for extensions.tsx

Signed-off-by: Roman <ixrock@gmail.com>

* fix: catalog entity context menu stale/unobservable

Signed-off-by: Roman <ixrock@gmail.com>

Co-authored-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
RomanandJari Kolehmainen authored and GitHub committed 2021-05-25 10:24:31 +03:00
1 parent 0c45dd807e
commit 2c3b510997
256 files changed
+2359 -1473

No files matched your search

+7 -3
View File
@@ -21,15 +21,19 @@
import type { KubeObjectStore } from "../kube-object.store";
import { action, observable } from "mobx";
import { autobind } from "../utils";
import { action, observable, makeObservable } from "mobx";
import { autoBind } from "../utils";
import { KubeApi, parseKubeApi } from "./kube-api";
@autobind()
export class ApiManager {
private apis = observable.map<string, KubeApi>();
private stores = observable.map<string, KubeObjectStore>();
constructor() {
makeObservable(this);
autoBind(this);
}
getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) {
if (typeof pathOrCallback === "string") {
return this.apis.get(pathOrCallback) || this.apis.get(parseKubeApi(pathOrCallback).apiBase);
+4 -2
View File
@@ -19,7 +19,7 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { computed, observable } from "mobx";
import { computed, observable, makeObservable } from "mobx";
import { subscribeToBroadcast } from "../../common/ipc";
import { CatalogCategory, CatalogEntity, CatalogEntityData, catalogCategoryRegistry, CatalogCategoryRegistry, CatalogEntityKindData } from "../../common/catalog";
import "../../common/catalog-entities";
@@ -29,7 +29,9 @@ export class CatalogEntityRegistry {
protected rawItems = observable.array<CatalogEntityData & CatalogEntityKindData>([], { deep: true });
@observable protected _activeEntity: CatalogEntity;
constructor(private categoryRegistry: CatalogCategoryRegistry) {}
constructor(private categoryRegistry: CatalogCategoryRegistry) {
makeObservable(this);
}
init() {
subscribeToBroadcast("catalog:items", (ev, items: (CatalogEntityData & CatalogEntityKindData)[]) => {
@@ -19,11 +19,9 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { autobind } from "../../utils";
import { Role } from "./role.api";
import { KubeApi } from "../kube-api";
@autobind()
export class ClusterRole extends Role {
static kind = "ClusterRole";
static namespaced = false;
+6 -4
View File
@@ -71,10 +71,7 @@ export interface IClusterMetrics<T = IMetrics> {
fsUsage: T;
}
export class Cluster extends KubeObject {
static kind = "Cluster";
static apiBase = "/apis/cluster.k8s.io/v1alpha1/clusters";
export interface Cluster {
spec: {
clusterNetwork?: {
serviceDomain?: string;
@@ -106,6 +103,11 @@ export class Cluster extends KubeObject {
errorMessage?: string;
errorReason?: string;
};
}
export class Cluster extends KubeObject {
static kind = "Cluster";
static apiBase = "/apis/cluster.k8s.io/v1alpha1/clusters";
getStatus() {
if (this.metadata.deletionTimestamp) return ClusterStatus.REMOVING;
@@ -28,13 +28,15 @@ export interface IComponentStatusCondition {
message: string;
}
export interface ComponentStatus {
conditions: IComponentStatusCondition[];
}
export class ComponentStatus extends KubeObject {
static kind = "ComponentStatus";
static namespaced = false;
static apiBase = "/api/v1/componentstatuses";
conditions: IComponentStatusCondition[];
getTruthyConditions() {
return this.conditions.filter(c => c.status === "True");
}
+10 -7
View File
@@ -21,10 +21,15 @@
import { KubeObject } from "../kube-object";
import type { KubeJsonApiData } from "../kube-json-api";
import { autobind } from "../../utils";
import { KubeApi } from "../kube-api";
import { autoBind } from "../../../common/utils";
export interface ConfigMap {
data: {
[param: string]: string;
};
}
@autobind()
export class ConfigMap extends KubeObject {
static kind = "ConfigMap";
static namespaced = true;
@@ -32,12 +37,10 @@ export class ConfigMap extends KubeObject {
constructor(data: KubeJsonApiData) {
super(data);
this.data = this.data || {};
}
autoBind(this);
data: {
[param: string]: string;
};
this.data ??= {};
}
getKeys(): string[] {
return Object.keys(this.data);
+7 -5
View File
@@ -38,11 +38,7 @@ type AdditionalPrinterColumnsV1Beta = AdditionalPrinterColumnsCommon & {
JSONPath: string;
};
export class CustomResourceDefinition extends KubeObject {
static kind = "CustomResourceDefinition";
static namespaced = false;
static apiBase = "/apis/apiextensions.k8s.io/v1/customresourcedefinitions";
export interface CustomResourceDefinition {
spec: {
group: string;
version?: string; // deprecated in v1 api
@@ -84,6 +80,12 @@ export class CustomResourceDefinition extends KubeObject {
};
storedVersions: string[];
};
}
export class CustomResourceDefinition extends KubeObject {
static kind = "CustomResourceDefinition";
static namespaced = false;
static apiBase = "/apis/apiextensions.k8s.io/v1/customresourcedefinitions";
getResourceUrl() {
return crdResourcesURL({
+14 -23
View File
@@ -23,8 +23,9 @@ import moment from "moment";
import { KubeObject } from "../kube-object";
import type { IPodContainer } from "./pods.api";
import { formatDuration } from "../../utils/formatDuration";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
export class CronJobApi extends KubeApi<CronJob> {
suspend(params: { namespace: string; name: string }) {
@@ -58,28 +59,7 @@ export class CronJobApi extends KubeApi<CronJob> {
}
}
@autobind()
export class CronJob extends KubeObject {
static kind = "CronJob";
static namespaced = true;
static apiBase = "/apis/batch/v1beta1/cronjobs";
kind: string;
apiVersion: string;
metadata: {
name: string;
namespace: string;
selfLink: string;
uid: string;
resourceVersion: string;
creationTimestamp: string;
labels: {
[key: string]: string;
};
annotations: {
[key: string]: string;
};
};
export interface CronJob {
spec: {
schedule: string;
concurrencyPolicy: string;
@@ -116,6 +96,17 @@ export class CronJob extends KubeObject {
status: {
lastScheduleTime?: string;
};
}
export class CronJob extends KubeObject {
static kind = "CronJob";
static namespaced = true;
static apiBase = "/apis/batch/v1beta1/cronjobs";
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
getSuspendFlag() {
return this.spec.suspend.toString();
+9 -4
View File
@@ -22,16 +22,21 @@
import get from "lodash/get";
import type { IPodContainer } from "./pods.api";
import { IAffinity, WorkloadKubeObject } from "../workload-kube-object";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
@autobind()
export class DaemonSet extends WorkloadKubeObject {
static kind = "DaemonSet";
static namespaced = true;
static apiBase = "/apis/apps/v1/daemonsets";
spec: {
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
declare spec: {
selector: {
matchLabels: {
[name: string]: string;
@@ -73,7 +78,7 @@ export class DaemonSet extends WorkloadKubeObject {
};
revisionHistoryLimit: number;
};
status: {
declare status: {
currentNumberScheduled: number;
numberMisscheduled: number;
desiredNumberScheduled: number;
+9 -4
View File
@@ -22,8 +22,9 @@
import moment from "moment";
import { IAffinity, WorkloadKubeObject } from "../workload-kube-object";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
export class DeploymentApi extends KubeApi<Deployment> {
protected getScaleApiUrl(params: { namespace: string; name: string }) {
@@ -87,13 +88,17 @@ interface IContainerProbe {
failureThreshold?: number;
}
@autobind()
export class Deployment extends WorkloadKubeObject {
static kind = "Deployment";
static namespaced = true;
static apiBase = "/apis/apps/v1/deployments";
spec: {
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
declare spec: {
replicas: number;
selector: { matchLabels: { [app: string]: string } };
template: {
@@ -172,7 +177,7 @@ export class Deployment extends WorkloadKubeObject {
};
};
};
status: {
declare status: {
observedGeneration: number;
replicas: number;
updatedReplicas: number;
+10 -3
View File
@@ -19,9 +19,10 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeObject } from "../kube-object";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
export interface IEndpointPort {
name?: string;
@@ -121,13 +122,19 @@ export class EndpointSubset implements IEndpointSubset {
}
}
@autobind()
export interface Endpoint {
subsets: IEndpointSubset[];
}
export class Endpoint extends KubeObject {
static kind = "Endpoints";
static namespaced = true;
static apiBase = "/api/v1/endpoints";
subsets: IEndpointSubset[];
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
getEndpointSubsets(): EndpointSubset[] {
const subsets = this.subsets || [];
+7 -7
View File
@@ -22,15 +22,9 @@
import moment from "moment";
import { KubeObject } from "../kube-object";
import { formatDuration } from "../../utils/formatDuration";
import { autobind } from "../../utils";
import { KubeApi } from "../kube-api";
@autobind()
export class KubeEvent extends KubeObject {
static kind = "Event";
static namespaced = true;
static apiBase = "/api/v1/events";
export interface KubeEvent {
involvedObject: {
kind: string;
namespace: string;
@@ -53,6 +47,12 @@ export class KubeEvent extends KubeObject {
eventTime: null;
reportingComponent: string;
reportingInstance: string;
}
export class KubeEvent extends KubeObject {
static kind = "Event";
static namespaced = true;
static apiBase = "/api/v1/events";
isWarning() {
return this.type === "Warning";
+13 -11
View File
@@ -22,7 +22,7 @@
import { compile } from "path-to-regexp";
import { apiBase } from "../index";
import { stringify } from "querystring";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
export type RepoHelmChartList = Record<string, HelmChart[]>;
export type HelmChartList = Record<string, RepoHelmChartList>;
@@ -83,16 +83,7 @@ export async function getChartValues(repo: string, name: string, version: string
return apiBase.get<string>(`/v2/charts/${repo}/${name}/values?${stringify({ version })}`);
}
@autobind()
export class HelmChart {
constructor(data: any) {
Object.assign(this, data);
}
static create(data: any) {
return new HelmChart(data);
}
export interface HelmChart {
apiVersion: string;
name: string;
version: string;
@@ -114,6 +105,17 @@ export class HelmChart {
appVersion?: string;
deprecated?: boolean;
tillerVersion?: string;
}
export class HelmChart {
constructor(data: HelmChart) {
Object.assign(this, data);
autoBind(this);
}
static create(data: any) {
return new HelmChart(data);
}
getId() {
return `${this.repo}:${this.apiVersion}/${this.name}@${this.getAppVersion()}+${this.digest}`;
+13 -11
View File
@@ -21,7 +21,7 @@
import jsYaml from "js-yaml";
import { compile } from "path-to-regexp";
import { autobind, formatDuration } from "../../utils";
import { autoBind, formatDuration } from "../../utils";
import capitalize from "lodash/capitalize";
import { apiBase } from "../index";
import { helmChartStore } from "../../components/+apps-helm-charts/helm-chart.store";
@@ -155,16 +155,7 @@ export async function rollbackRelease(name: string, namespace: string, revision:
});
}
@autobind()
export class HelmRelease implements ItemObject {
constructor(data: any) {
Object.assign(this, data);
}
static create(data: any) {
return new HelmRelease(data);
}
export interface HelmRelease {
appVersion: string;
name: string;
namespace: string;
@@ -172,6 +163,17 @@ export class HelmRelease implements ItemObject {
status: string;
updated: string;
revision: string;
}
export class HelmRelease implements ItemObject {
constructor(data: any) {
Object.assign(this, data);
autoBind(this);
}
static create(data: any) {
return new HelmRelease(data);
}
getId() {
return this.namespace + this.name;
+7 -5
View File
@@ -59,11 +59,7 @@ export interface IHpaMetric {
}>;
}
export class HorizontalPodAutoscaler extends KubeObject {
static kind = "HorizontalPodAutoscaler";
static namespaced = true;
static apiBase = "/apis/autoscaling/v2beta1/horizontalpodautoscalers";
export interface HorizontalPodAutoscaler {
spec: {
scaleTargetRef: {
kind: string;
@@ -86,6 +82,12 @@ export class HorizontalPodAutoscaler extends KubeObject {
type: string;
}[];
};
}
export class HorizontalPodAutoscaler extends KubeObject {
static kind = "HorizontalPodAutoscaler";
static namespaced = true;
static apiBase = "/apis/autoscaling/v2beta1/horizontalpodautoscalers";
getMaxPods() {
return this.spec.maxReplicas || 0;
+15 -8
View File
@@ -20,9 +20,10 @@
*/
import { KubeObject } from "../kube-object";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { IMetrics, metricsApi } from "./metrics.api";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
export class IngressApi extends KubeApi<Ingress> {
getMetrics(ingress: string, namespace: string): Promise<IIngressMetrics> {
@@ -82,12 +83,7 @@ export const getBackendServiceNamePort = (backend: IIngressBackend) => {
return { serviceName, servicePort };
};
@autobind()
export class Ingress extends KubeObject {
static kind = "Ingress";
static namespaced = true;
static apiBase = "/apis/networking.k8s.io/v1/ingresses";
export interface Ingress {
spec: {
tls: {
secretName: string;
@@ -117,6 +113,17 @@ export class Ingress extends KubeObject {
ingress: ILoadBalancerIngress[];
};
};
}
export class Ingress extends KubeObject {
static kind = "Ingress";
static namespaced = true;
static apiBase = "/apis/networking.k8s.io/v1/ingresses";
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
getRoutes() {
const { spec: { tls, rules } } = this;
@@ -188,7 +195,7 @@ export class Ingress extends KubeObject {
getLoadBalancers() {
const { status: { loadBalancer = { ingress: [] } } } = this;
return (loadBalancer.ingress ?? []).map(address => (
address.hostname || address.ip
));
+9 -4
View File
@@ -20,19 +20,24 @@
*/
import get from "lodash/get";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { IAffinity, WorkloadKubeObject } from "../workload-kube-object";
import type { IPodContainer } from "./pods.api";
import { KubeApi } from "../kube-api";
import type { JsonApiParams } from "../json-api";
import type { KubeJsonApiData } from "../kube-json-api";
@autobind()
export class Job extends WorkloadKubeObject {
static kind = "Job";
static namespaced = true;
static apiBase = "/apis/batch/v1/jobs";
spec: {
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
declare spec: {
parallelism?: number;
completions?: number;
backoffLimit?: number;
@@ -78,7 +83,7 @@ export class Job extends WorkloadKubeObject {
serviceAccount?: string;
schedulerName?: string;
};
status: {
declare status: {
conditions: {
type: string;
status: string;
+12 -5
View File
@@ -21,7 +21,8 @@
import { KubeObject } from "../kube-object";
import { KubeApi } from "../kube-api";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import type { KubeJsonApiData } from "../kube-json-api";
export enum LimitType {
CONTAINER = "Container",
@@ -50,15 +51,21 @@ export interface LimitRangeItem extends LimitRangeParts {
type: string
}
@autobind()
export interface LimitRange {
spec: {
limits: LimitRangeItem[];
};
}
export class LimitRange extends KubeObject {
static kind = "LimitRange";
static namespaced = true;
static apiBase = "/api/v1/limitranges";
spec: {
limits: LimitRangeItem[];
};
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
getContainerLimits() {
return this.spec.limits.filter(limit => limit.type === LimitType.CONTAINER);
+13 -6
View File
@@ -21,25 +21,32 @@
import { KubeApi } from "../kube-api";
import { KubeObject } from "../kube-object";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import type { KubeJsonApiData } from "../kube-json-api";
export enum NamespaceStatus {
ACTIVE = "Active",
TERMINATING = "Terminating",
}
@autobind()
export interface Namespace {
status?: {
phase: string;
};
}
export class Namespace extends KubeObject {
static kind = "Namespace";
static namespaced = false;
static apiBase = "/api/v1/namespaces";
status?: {
phase: string;
};
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
getStatus() {
return this.status ? this.status.phase : "-";
return this.status?.phase ?? "-";
}
}
@@ -20,8 +20,9 @@
*/
import { KubeObject } from "../kube-object";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
export interface IPolicyIpBlock {
cidr: string;
@@ -56,12 +57,7 @@ export interface IPolicyEgress {
}[];
}
@autobind()
export class NetworkPolicy extends KubeObject {
static kind = "NetworkPolicy";
static namespaced = true;
static apiBase = "/apis/networking.k8s.io/v1/networkpolicies";
export interface NetworkPolicy {
spec: {
podSelector: {
matchLabels: {
@@ -73,6 +69,17 @@ export class NetworkPolicy extends KubeObject {
ingress: IPolicyIngress[];
egress: IPolicyEgress[];
};
}
export class NetworkPolicy extends KubeObject {
static kind = "NetworkPolicy";
static namespaced = true;
static apiBase = "/apis/networking.k8s.io/v1/networkpolicies";
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
getMatchLabels(): string[] {
if (!this.spec.podSelector || !this.spec.podSelector.matchLabels) return [];
+15 -8
View File
@@ -20,13 +20,14 @@
*/
import { KubeObject } from "../kube-object";
import { autobind, cpuUnitsToNumber, unitsToBytes } from "../../utils";
import { autoBind, cpuUnitsToNumber, unitsToBytes } from "../../utils";
import { IMetrics, metricsApi } from "./metrics.api";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
export class NodesApi extends KubeApi<Node> {
getMetrics(): Promise<INodeMetrics> {
const opts = { category: "nodes"};
const opts = { category: "nodes" };
return metricsApi.getMetrics({
memoryUsage: opts,
@@ -49,12 +50,7 @@ export interface INodeMetrics<T = IMetrics> {
fsSize: T;
}
@autobind()
export class Node extends KubeObject {
static kind = "Node";
static namespaced = false;
static apiBase = "/api/v1/nodes";
export interface Node {
spec: {
podCIDR: string;
externalID: string;
@@ -105,6 +101,17 @@ export class Node extends KubeObject {
sizeBytes: number;
}[];
};
}
export class Node extends KubeObject {
static kind = "Node";
static namespaced = false;
static apiBase = "/api/v1/nodes";
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
getNodeConditionText() {
const { conditions } = this.status;
@@ -20,10 +20,11 @@
*/
import { KubeObject } from "../kube-object";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { IMetrics, metricsApi } from "./metrics.api";
import type { Pod } from "./pods.api";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
export class PersistentVolumeClaimsApi extends KubeApi<PersistentVolumeClaim> {
getMetrics(pvcName: string, namespace: string): Promise<IPvcMetrics> {
@@ -42,12 +43,7 @@ export interface IPvcMetrics<T = IMetrics> {
diskCapacity: T;
}
@autobind()
export class PersistentVolumeClaim extends KubeObject {
static kind = "PersistentVolumeClaim";
static namespaced = true;
static apiBase = "/api/v1/persistentvolumeclaims";
export interface PersistentVolumeClaim {
spec: {
accessModes: string[];
storageClassName: string;
@@ -70,6 +66,17 @@ export class PersistentVolumeClaim extends KubeObject {
status: {
phase: string; // Pending
};
}
export class PersistentVolumeClaim extends KubeObject {
static kind = "PersistentVolumeClaim";
static namespaced = true;
static apiBase = "/api/v1/persistentvolumeclaims";
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
getPods(allPods: Pod[]): Pod[] {
const pods = allPods.filter(pod => pod.getNs() === this.getNs());
@@ -21,15 +21,11 @@
import { KubeObject } from "../kube-object";
import { unitsToBytes } from "../../utils/convertMemory";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
@autobind()
export class PersistentVolume extends KubeObject {
static kind = "PersistentVolume";
static namespaced = false;
static apiBase = "/api/v1/persistentvolumes";
export interface PersistentVolume {
spec: {
capacity: {
storage: string; // 8Gi
@@ -65,6 +61,17 @@ export class PersistentVolume extends KubeObject {
phase: string;
reason?: string;
};
}
export class PersistentVolume extends KubeObject {
static kind = "PersistentVolume";
static namespaced = false;
static apiBase = "/api/v1/persistentvolumes";
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
getCapacity(inBytes = false) {
const capacity = this.spec.capacity;
@@ -22,11 +22,7 @@
import { KubeObject } from "../kube-object";
import { KubeApi } from "../kube-api";
export class PodMetrics extends KubeObject {
static kind = "PodMetrics";
static namespaced = true;
static apiBase = "/apis/metrics.k8s.io/v1beta1/pods";
export interface PodMetrics {
timestamp: string;
window: string;
containers: {
@@ -38,6 +34,12 @@ export class PodMetrics extends KubeObject {
}[];
}
export class PodMetrics extends KubeObject {
static kind = "PodMetrics";
static namespaced = true;
static apiBase = "/apis/metrics.k8s.io/v1beta1/pods";
}
export const podMetricsApi = new KubeApi({
objectConstructor: PodMetrics,
});
@@ -19,16 +19,12 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeObject } from "../kube-object";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
@autobind()
export class PodDisruptionBudget extends KubeObject {
static kind = "PodDisruptionBudget";
static namespaced = true;
static apiBase = "/apis/policy/v1beta1/poddisruptionbudgets";
export interface PodDisruptionBudget {
spec: {
minAvailable: string;
maxUnavailable: string;
@@ -40,6 +36,17 @@ export class PodDisruptionBudget extends KubeObject {
disruptionsAllowed: number
expectedPods: number
};
}
export class PodDisruptionBudget extends KubeObject {
static kind = "PodDisruptionBudget";
static namespaced = true;
static apiBase = "/apis/policy/v1beta1/poddisruptionbudgets";
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
getSelectors() {
const selector = this.spec.selector;
+9 -4
View File
@@ -20,9 +20,10 @@
*/
import { IAffinity, WorkloadKubeObject } from "../workload-kube-object";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { IMetrics, metricsApi } from "./metrics.api";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
export class PodsApi extends KubeApi<Pod> {
async getLogs(params: { namespace: string; name: string }, query?: IPodLogsQuery): Promise<string> {
@@ -202,13 +203,17 @@ export interface IPodContainerStatus {
started?: boolean;
}
@autobind()
export class Pod extends WorkloadKubeObject {
static kind = "Pod";
static namespaced = true;
static apiBase = "/api/v1/pods";
spec: {
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
declare spec: {
volumes?: {
name: string;
persistentVolumeClaim: {
@@ -265,7 +270,7 @@ export class Pod extends WorkloadKubeObject {
};
affinity?: IAffinity;
};
status?: {
declare status?: {
phase: string;
conditions: {
type: string;
@@ -19,16 +19,12 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeObject } from "../kube-object";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
@autobind()
export class PodSecurityPolicy extends KubeObject {
static kind = "PodSecurityPolicy";
static namespaced = false;
static apiBase = "/apis/policy/v1beta1/podsecuritypolicies";
export interface PodSecurityPolicy {
spec: {
allowPrivilegeEscalation?: boolean;
allowedCSIDrivers?: {
@@ -88,6 +84,17 @@ export class PodSecurityPolicy extends KubeObject {
};
volumes?: string[];
};
}
export class PodSecurityPolicy extends KubeObject {
static kind = "PodSecurityPolicy";
static namespaced = false;
static apiBase = "/apis/policy/v1beta1/podsecuritypolicies";
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
isPrivileged() {
return !!this.spec.privileged;
+10 -4
View File
@@ -20,10 +20,11 @@
*/
import get from "lodash/get";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { WorkloadKubeObject } from "../workload-kube-object";
import type { IPodContainer, Pod } from "./pods.api";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
export class ReplicaSetApi extends KubeApi<ReplicaSet> {
protected getScaleApiUrl(params: { namespace: string; name: string }) {
@@ -48,12 +49,17 @@ export class ReplicaSetApi extends KubeApi<ReplicaSet> {
}
}
@autobind()
export class ReplicaSet extends WorkloadKubeObject {
static kind = "ReplicaSet";
static namespaced = true;
static apiBase = "/apis/apps/v1/replicasets";
spec: {
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
declare spec: {
replicas?: number;
selector: { matchLabels: { [app: string]: string } };
template?: {
@@ -66,7 +72,7 @@ export class ReplicaSet extends WorkloadKubeObject {
};
minReadySeconds?: number;
};
status: {
declare status: {
replicas: number;
fullyLabeledReplicas?: number;
readyReplicas?: number;
@@ -21,7 +21,6 @@
import { KubeObject } from "../kube-object";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
export interface IResourceQuotaValues {
[quota: string]: string;
@@ -51,16 +50,7 @@ export interface IResourceQuotaValues {
"count/deployments.extensions"?: string;
}
export class ResourceQuota extends KubeObject {
static kind = "ResourceQuota";
static namespaced = true;
static apiBase = "/api/v1/resourcequotas";
constructor(data: KubeJsonApiData) {
super(data);
this.spec = this.spec || {} as any;
}
export interface ResourceQuota {
spec: {
hard: IResourceQuotaValues;
scopeSelector?: {
@@ -76,6 +66,12 @@ export class ResourceQuota extends KubeObject {
hard: IResourceQuotaValues;
used: IResourceQuotaValues;
};
}
export class ResourceQuota extends KubeObject {
static kind = "ResourceQuota";
static namespaced = true;
static apiBase = "/api/v1/resourcequotas";
getScopeSelector() {
const { matchExpressions = [] } = this.spec.scopeSelector || {};
+14 -7
View File
@@ -19,9 +19,10 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeObject } from "../kube-object";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
export interface IRoleBindingSubject {
kind: string;
@@ -30,18 +31,24 @@ export interface IRoleBindingSubject {
apiGroup?: string;
}
@autobind()
export class RoleBinding extends KubeObject {
static kind = "RoleBinding";
static namespaced = true;
static apiBase = "/apis/rbac.authorization.k8s.io/v1/rolebindings";
export interface RoleBinding {
subjects?: IRoleBindingSubject[];
roleRef: {
kind: string;
name: string;
apiGroup?: string;
};
}
export class RoleBinding extends KubeObject {
static kind = "RoleBinding";
static namespaced = true;
static apiBase = "/apis/rbac.authorization.k8s.io/v1/rolebindings";
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
getSubjects() {
return this.subjects || [];
+7 -5
View File
@@ -22,17 +22,19 @@
import { KubeObject } from "../kube-object";
import { KubeApi } from "../kube-api";
export class Role extends KubeObject {
static kind = "Role";
static namespaced = true;
static apiBase = "/apis/rbac.authorization.k8s.io/v1/roles";
export interface Role {
rules: {
verbs: string[];
apiGroups: string[];
resources: string[];
resourceNames?: string[];
}[];
}
export class Role extends KubeObject {
static kind = "Role";
static namespaced = true;
static apiBase = "/apis/rbac.authorization.k8s.io/v1/roles";
getRules() {
return this.rules || [];
+11 -8
View File
@@ -21,7 +21,7 @@
import { KubeObject } from "../kube-object";
import type { KubeJsonApiData } from "../kube-json-api";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeApi } from "../kube-api";
export enum SecretType {
@@ -40,21 +40,24 @@ export interface ISecretRef {
name: string;
}
@autobind()
export class Secret extends KubeObject {
static kind = "Secret";
static namespaced = true;
static apiBase = "/api/v1/secrets";
export interface Secret {
type: SecretType;
data: {
[prop: string]: string;
token?: string;
};
}
export class Secret extends KubeObject {
static kind = "Secret";
static namespaced = true;
static apiBase = "/api/v1/secrets";
constructor(data: KubeJsonApiData) {
super(data);
this.data = this.data || {};
autoBind(this);
this.data ??= {};
}
getKeys(): string[] {
@@ -28,8 +28,7 @@ export class SelfSubjectRulesReviewApi extends KubeApi<SelfSubjectRulesReview> {
spec: {
namespace
},
}
);
});
}
}
@@ -41,21 +40,21 @@ export interface ISelfSubjectReviewRule {
nonResourceURLs?: string[];
}
export class SelfSubjectRulesReview extends KubeObject {
static kind = "SelfSubjectRulesReview";
static namespaced = false;
static apiBase = "/apis/authorization.k8s.io/v1/selfsubjectrulesreviews";
export interface SelfSubjectRulesReview {
spec: {
// todo: add more types from api docs
namespace?: string;
};
status: {
resourceRules: ISelfSubjectReviewRule[];
nonResourceRules: ISelfSubjectReviewRule[];
incomplete: boolean;
};
}
export class SelfSubjectRulesReview extends KubeObject {
static kind = "SelfSubjectRulesReview";
static namespaced = false;
static apiBase = "/apis/authorization.k8s.io/v1/selfsubjectrulesreviews";
getResourceRules() {
const rules = this.status && this.status.resourceRules || [];
@@ -19,22 +19,29 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeObject } from "../kube-object";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
@autobind()
export class ServiceAccount extends KubeObject {
static kind = "ServiceAccount";
static namespaced = true;
static apiBase = "/api/v1/serviceaccounts";
export interface ServiceAccount {
secrets?: {
name: string;
}[];
imagePullSecrets?: {
name: string;
}[];
}
export class ServiceAccount extends KubeObject {
static kind = "ServiceAccount";
static namespaced = true;
static apiBase = "/api/v1/serviceaccounts";
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
getSecrets() {
return this.secrets || [];
+18 -16
View File
@@ -19,25 +19,21 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeObject } from "../kube-object";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
export interface IServicePort {
name?: string;
protocol: string;
port: number;
targetPort: number;
}
export class ServicePort implements IServicePort {
export interface ServicePort {
name?: string;
protocol: string;
port: number;
targetPort: number;
nodePort?: number;
}
constructor(data: IServicePort) {
export class ServicePort {
constructor(data: ServicePort) {
Object.assign(this, data);
}
@@ -50,12 +46,7 @@ export class ServicePort implements IServicePort {
}
}
@autobind()
export class Service extends KubeObject {
static kind = "Service";
static namespaced = true;
static apiBase = "/api/v1/services";
export interface Service {
spec: {
type: string;
clusterIP: string;
@@ -75,6 +66,17 @@ export class Service extends KubeObject {
}[];
};
};
}
export class Service extends KubeObject {
static kind = "Service";
static namespaced = true;
static apiBase = "/api/v1/services";
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
getClusterIp() {
return this.spec.clusterIP;
@@ -22,8 +22,9 @@
import get from "lodash/get";
import type { IPodContainer } from "./pods.api";
import { IAffinity, WorkloadKubeObject } from "../workload-kube-object";
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
export class StatefulSetApi extends KubeApi<StatefulSet> {
protected getScaleApiUrl(params: { namespace: string; name: string }) {
@@ -48,13 +49,17 @@ export class StatefulSetApi extends KubeApi<StatefulSet> {
}
}
@autobind()
export class StatefulSet extends WorkloadKubeObject {
static kind = "StatefulSet";
static namespaced = true;
static apiBase = "/apis/apps/v1/statefulsets";
spec: {
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
declare spec: {
serviceName: string;
replicas: number;
selector: {
@@ -107,7 +112,7 @@ export class StatefulSet extends WorkloadKubeObject {
};
}[];
};
status: {
declare status: {
observedGeneration: number;
replicas: number;
currentReplicas: number;
@@ -19,16 +19,12 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { autobind } from "../../utils";
import { autoBind } from "../../utils";
import { KubeObject } from "../kube-object";
import { KubeApi } from "../kube-api";
import type { KubeJsonApiData } from "../kube-json-api";
@autobind()
export class StorageClass extends KubeObject {
static kind = "StorageClass";
static namespaced = false;
static apiBase = "/apis/storage.k8s.io/v1/storageclasses";
export interface StorageClass {
provisioner: string; // e.g. "storage.k8s.io/v1"
mountOptions?: string[];
volumeBindingMode: string;
@@ -36,6 +32,17 @@ export class StorageClass extends KubeObject {
parameters: {
[param: string]: string; // every provisioner has own set of these parameters
};
}
export class StorageClass extends KubeObject {
static kind = "StorageClass";
static namespaced = false;
static apiBase = "/apis/storage.k8s.io/v1/storageclasses";
constructor(data: KubeJsonApiData) {
super(data);
autoBind(this);
}
isDefault() {
const annotations = this.metadata.annotations || {};
+10 -9
View File
@@ -23,7 +23,7 @@
import moment from "moment";
import type { KubeJsonApiData, KubeJsonApiDataList, KubeJsonApiListMetadata, KubeJsonApiMetadata } from "./kube-json-api";
import { autobind, formatDuration } from "../utils";
import { autoBind, formatDuration } from "../utils";
import type { ItemObject } from "../item.store";
import { apiKube } from "./index";
import type { JsonApiParams } from "./json-api";
@@ -87,11 +87,16 @@ export class KubeStatus {
export type IKubeMetaField = keyof IKubeObjectMetadata;
@autobind()
export class KubeObject implements ItemObject {
export class KubeObject<Metadata extends IKubeObjectMetadata = IKubeObjectMetadata, Status = any, Spec = any> implements ItemObject {
static readonly kind: string;
static readonly namespaced: boolean;
apiVersion: string;
kind: string;
metadata: Metadata;
status?: Status;
spec?: Spec;
static create(data: any) {
return new KubeObject(data);
}
@@ -176,13 +181,9 @@ export class KubeObject implements ItemObject {
constructor(data: KubeJsonApiData) {
Object.assign(this, data);
autoBind(this);
}
apiVersion: string;
kind: string;
metadata: IKubeObjectMetadata;
status?: any; // todo: type-safety support
get selfLink() {
return this.metadata.selfLink;
}
@@ -265,7 +266,7 @@ export class KubeObject implements ItemObject {
}
// use unified resource-applier api for updating all k8s objects
async update<T extends KubeObject>(data: Partial<T>) {
async update<T extends KubeObject>(data: Partial<T>): Promise<T> {
return resourceApplierApi.update<T>({
...this.toPlainObject(),
...data,
+6 -4
View File
@@ -26,8 +26,8 @@ import type { KubeObjectStore } from "../kube-object.store";
import type { ClusterContext } from "../components/context";
import plimit from "p-limit";
import { comparer, IReactionDisposer, observable, reaction, when } from "mobx";
import { autobind, noop } from "../utils";
import { comparer, IReactionDisposer, observable, reaction, makeObservable } from "mobx";
import { autoBind, noop } from "../utils";
import type { KubeApi } from "./kube-api";
import type { KubeJsonApiData } from "./kube-json-api";
import { isDebugging, isProduction } from "../../common/vars";
@@ -50,11 +50,13 @@ export interface IKubeWatchLog {
cssStyle?: string;
}
@autobind()
export class KubeWatchApi {
@observable context: ClusterContext = null;
contextReady = when(() => Boolean(this.context));
constructor() {
makeObservable(this);
autoBind(this);
}
isAllowedApi(api: KubeApi): boolean {
return Boolean(this.context?.cluster.isAllowedResource(api.kind));
+2 -2
View File
@@ -20,7 +20,7 @@
*/
import { stringify } from "querystring";
import { autobind, base64, EventEmitter } from "../utils";
import { boundMethod, base64, EventEmitter } from "../utils";
import { WebSocketApi } from "./websocket-api";
import isEqual from "lodash/isEqual";
import { isDevelopment } from "../../common/vars";
@@ -106,7 +106,7 @@ export class TerminalApi extends WebSocketApi {
this.onReady.removeAllListeners();
}
@autobind()
@boundMethod
protected _onReady(data: string) {
if (!data) return true;
this.isReady = true;
+2 -1
View File
@@ -19,7 +19,7 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { observable } from "mobx";
import { observable, makeObservable } from "mobx";
import { EventEmitter } from "../../common/event-emitter";
interface IParams {
@@ -66,6 +66,7 @@ export class WebSocketApi {
};
constructor(protected params: IParams) {
makeObservable(this);
this.params = Object.assign({}, WebSocketApi.defaultParams, params);
const { autoConnect, pingIntervalSeconds } = this.params;
-2
View File
@@ -68,8 +68,6 @@ export interface IAffinity {
}
export class WorkloadKubeObject extends KubeObject {
spec: any; // todo: add proper types
getSelectors(): string[] {
const selector = this.spec.selector;