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

tracker / user-store refactoring, part 1

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2020-07-05 17:16:50 +03:00
parent c9fb015bcf
commit 4270ec4240
23 changed files with 247 additions and 225 deletions

View File

@ -170,9 +170,9 @@
"@types/node": "^12.12.45",
"@types/proper-lockfile": "^4.1.1",
"@types/tar": "^4.0.3",
"conf": "^7.0.1",
"crypto-js": "^4.0.0",
"electron-promise-ipc": "^2.1.0",
"electron-store": "^5.2.0",
"electron-updater": "^4.3.1",
"electron-window-state": "^5.0.3",
"filenamify": "^4.1.0",
@ -193,6 +193,7 @@
"request": "^2.88.2",
"request-promise-native": "^1.0.8",
"semver": "^7.3.2",
"serializr": "^2.0.3",
"shell-env": "^3.0.0",
"tar": "^6.0.2",
"tcp-port-used": "^1.0.1",

View File

@ -1,32 +1,32 @@
import ElectronStore from "electron-store"
import { Singleton } from "./utils/singleton";
import Config from "conf"
import Singleton from "./utils/singleton";
import migrations from "../migrations/cluster-store"
import { Cluster, ClusterBaseInfo } from "../main/cluster";
export class ClusterStore extends Singleton {
private store = new ElectronStore({
name: "lens-cluster-store",
private storeConfig = new Config({
configName: "lens-cluster-store",
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
migrations: migrations,
})
public getAllClusterObjects(): Cluster[] {
return this.store.get("clusters", []).map((clusterInfo: ClusterBaseInfo) => {
return this.storeConfig.get("clusters", []).map((clusterInfo: ClusterBaseInfo) => {
return new Cluster(clusterInfo)
})
}
public getAllClusters(): ClusterBaseInfo[] {
return this.store.get("clusters", [])
return this.storeConfig.get("clusters", [])
}
public removeCluster(id: string): void {
this.store.delete(id);
this.storeConfig.delete(id);
const clusterBaseInfos = this.getAllClusters()
const index = clusterBaseInfos.findIndex((cbi) => cbi.id === id)
if (index !== -1) {
clusterBaseInfos.splice(index, 1)
this.store.set("clusters", clusterBaseInfos)
this.storeConfig.set("clusters", clusterBaseInfos)
}
}
@ -62,7 +62,7 @@ export class ClusterStore extends Singleton {
} else {
clusters[index] = storable
}
this.store.set("clusters", clusters)
this.storeConfig.set("clusters", clusters)
}
public storeClusters(clusters: ClusterBaseInfo[]) {

View File

@ -1,12 +1,11 @@
import request from "request"
import { userStore } from "./user-store"
export function globalRequestOpts(requestOpts: request.Options ) {
const userPrefs = userStore.getPreferences()
if (userPrefs.httpsProxy) {
requestOpts.proxy = userPrefs.httpsProxy
export function globalRequestOpts(requestOpts: request.Options) {
const { httpsProxy, allowUntrustedCAs } = userStore.preferences
if (httpsProxy) {
requestOpts.proxy = httpsProxy
}
requestOpts.rejectUnauthorized = !userPrefs.allowUntrustedCAs;
requestOpts.rejectUnauthorized = !allowUntrustedCAs;
return requestOpts
}

View File

@ -1,10 +1,12 @@
import { app, App, remote } from "electron"
import ua from "universal-analytics"
import { machineIdSync } from "node-machine-id"
import Singleton from "./utils/singleton";
import { userStore } from "./user-store"
const GA_ID = "UA-159377374-1"
export class Tracker extends Singleton {
static readonly GA_ID = "UA-159377374-1"
export class Tracker {
protected visitor: ua.Visitor
protected machineId: string = null;
protected ip: string = null;
@ -12,31 +14,35 @@ export class Tracker {
protected locale: string;
protected electronUA: string;
constructor(app: Electron.App) {
private constructor(app: App) {
super();
try {
this.visitor = ua(GA_ID, machineIdSync(), {strictCidFormat: false})
this.visitor = ua(Tracker.GA_ID, machineIdSync(), { strictCidFormat: false })
} catch (error) {
this.visitor = ua(GA_ID)
this.visitor = ua(Tracker.GA_ID)
}
this.visitor.set("dl", "https://lensapptelemetry.lakendlabs.com")
}
public async event(eventCategory: string, eventAction: string) {
return new Promise(async (resolve, reject) => {
if (!this.telemetryAllowed()) {
resolve()
return
protected async isTelemetryAllowed(): Promise<boolean> {
return userStore.preferences.allowTelemetry;
}
async event(eventCategory: string, eventAction: string, otherParams = {}) {
try {
const allowed = await this.isTelemetryAllowed();
if (!allowed) {
return;
}
this.visitor.event({
ec: eventCategory,
ea: eventAction
ea: eventAction,
...otherParams,
}).send()
resolve()
})
}
protected telemetryAllowed() {
const userPrefs = userStore.getPreferences()
return !!userPrefs.allowTelemetry
} catch (err) {
console.error(`Failed to track "${eventCategory}:${eventAction}"`, err)
}
}
}
export const tracker: Tracker = Tracker.getInstance(app || remote.app);

View File

@ -1,59 +1,105 @@
import ElectronStore from "electron-store"
import { app, remote } from "electron"
import { computed, observable, reaction, toJS } from "mobx";
import Config from "conf"
import semver from "semver"
import migrations from "../migrations/user-store"
import { Singleton } from "./utils/singleton";
import Singleton from "./utils/singleton";
import { getAppVersion } from "./utils/app-version";
import { tracker } from "./tracker";
export interface UserStoreModel {
lastSeenAppVersion: string;
seenContexts: string[];
preferences: UserPreferences;
}
export interface UserPreferences {
httpsProxy?: string;
colorTheme?: string;
colorTheme?: string | "dark";
allowUntrustedCAs?: boolean;
allowTelemetry?: boolean;
downloadMirror?: string;
downloadMirror?: string | "default";
}
export class UserStore extends Singleton {
protected store = new ElectronStore({
name: "lens-user-store",
migrations: migrations,
});
private storeConfig: Config<UserStoreModel>;
public lastSeenAppVersion() {
return this.store.get('lastSeenAppVersion', "0.0.0")
@observable isReady = false;
@observable lastSeenAppVersion = "0.0.0"
@observable seenContexts = observable.set();
@observable preferences: UserPreferences = {
allowTelemetry: true,
colorTheme: "dark",
downloadMirror: "default",
};
@computed get hasNewAppVersion() {
return semver.gt(getAppVersion(), this.lastSeenAppVersion);
}
public setLastSeenAppVersion(version: string) {
this.store.set('lastSeenAppVersion', version)
private constructor() {
super();
this.init();
}
public getSeenContexts(): Array<string> {
return this.store.get("seenContexts", [])
async init() {
/*await*/ this.load();
this.bindEvents();
this.isReady = true;
}
public storeSeenContext(newContexts: string[]) {
const seenContexts = this.getSeenContexts().concat(newContexts)
// store unique contexts by casting array to set first
const newContextSet = new Set(seenContexts)
const allContexts = [...newContextSet]
this.store.set("seenContexts", allContexts)
return allContexts
saveLastSeenAppVersion() {
this.lastSeenAppVersion = getAppVersion();
}
public setPreferences(preferences: UserPreferences) {
this.store.set('preferences', preferences)
// todo: use "conf" as pseudo-async for more future-proof usages
protected async load() {
this.storeConfig = new Config<UserStoreModel>({
configName: "lens-user-store",
migrations: migrations,
cwd: (app || remote.app).getPath("userData"),
});
this.fromStore(this.storeConfig.store);
}
public getPreferences(): UserPreferences {
const prefs = this.store.get("preferences", {})
if (!prefs.colorTheme) {
prefs.colorTheme = "dark"
protected bindEvents() {
// refresh from file-system updates
this.storeConfig.onDidAnyChange((data, oldValue) => {
this.fromStore(data);
});
// refresh config file from runtime
reaction(() => this.toJSON(), model => {
this.storeConfig.store = model;
});
// track telemetry availability
reaction(() => this.preferences.allowTelemetry, allowed => {
tracker.event("telemetry", allowed ? "enabled" : "disabled");
});
}
// todo: use "serializr" ?
protected fromStore(data: Partial<UserStoreModel> = {}) {
const { lastSeenAppVersion, seenContexts, preferences } = data
if (lastSeenAppVersion) {
this.lastSeenAppVersion = lastSeenAppVersion;
}
if (!prefs.downloadMirror) {
prefs.downloadMirror = "default"
if (seenContexts) {
this.seenContexts = observable.set(seenContexts)
}
if (prefs.allowTelemetry === undefined) {
prefs.allowTelemetry = true
if (preferences) {
Object.assign(this.preferences, preferences);
}
}
return prefs
protected toJSON(): UserStoreModel {
return toJS({
lastSeenAppVersion: this.lastSeenAppVersion,
seenContexts: Array.from(this.seenContexts),
preferences: this.preferences,
})
}
}

View File

@ -6,7 +6,8 @@
* const usersStore: UsersStore = UsersStore.getInstance();
*/
export class Singleton {
// todo: maybe convert to @decorator
class Singleton {
private static instances = new WeakMap<object, Singleton>();
// todo: figure out how to infer child class + arguments types
@ -21,3 +22,6 @@ export class Singleton {
Singleton.instances.delete(this);
}
}
export { Singleton }
export default Singleton;

View File

@ -1,30 +1,26 @@
import ElectronStore from "electron-store"
import { Singleton } from "./utils/singleton";
import Config from "conf"
import Singleton from "./utils/singleton";
import { clusterStore } from "./cluster-store"
import { getAppVersion } from "./utils/app-version";
export type WorkspaceId = string;
export interface WorkspaceData {
export interface WorkspaceStoreModel {
workspaces: Workspace[]
}
export interface Workspace {
id: WorkspaceId;
name: string;
description?: string;
}
export class Workspace implements WorkspaceData {
public id: string
public name: string
public description?: string
public constructor(data: WorkspaceData) {
Object.assign(this, data)
}
}
export class WorkspaceStore extends Singleton {
static defaultId = "default"
static readonly defaultId = "default"
private store = new ElectronStore({
name: "lens-workspace-store"
private storeConfig = new Config<WorkspaceStoreModel>({
configName: "lens-workspace-store",
projectVersion: getAppVersion(),
});
private constructor() {
@ -46,11 +42,10 @@ export class WorkspaceStore extends Singleton {
}
public getAllWorkspaces(): Workspace[] {
const workspacesData: WorkspaceData[] = this.store.get("workspaces", [])
return workspacesData.map((wsd) => new Workspace(wsd))
return this.storeConfig.get("workspaces", [])
}
public saveWorkspace(workspace: WorkspaceData) {
public saveWorkspace(workspace: Workspace) {
const workspaces = this.getAllWorkspaces()
const index = workspaces.findIndex((w) => w.id === workspace.id)
if (index !== -1) {
@ -58,7 +53,7 @@ export class WorkspaceStore extends Singleton {
} else {
workspaces.push(workspace)
}
this.store.set("workspaces", workspaces)
this.storeConfig.set("workspaces", workspaces)
}
public removeWorkspace(workspace: Workspace) {
@ -70,7 +65,7 @@ export class WorkspaceStore extends Singleton {
if (index !== -1) {
clusterStore.removeClustersByWorkspace(workspace.id)
workspaces.splice(index, 1)
this.store.set("workspaces", workspaces)
this.storeConfig.set("workspaces", workspaces)
}
}
}

View File

@ -18,7 +18,6 @@ import initMenu from "./menu"
import * as proxy from "./proxy"
import { WindowManager } from "./window-manager";
import { clusterStore } from "../common/cluster-store"
import { tracker } from "./tracker"
import { ClusterManager } from "./cluster-manager";
import AppUpdater from "./app-updater"
import { shellSync } from "./shell-sync"
@ -26,6 +25,7 @@ import { getFreePort } from "./port"
import { mangleProxyEnv } from "./proxy-env"
import { findMainWebContents } from "./webcontents"
import { registerStaticProtocol } from "../common/register-static";
import { tracker } from "../common/tracker";
mangleProxyEnv()
if (app.commandLine.getSwitchValue("proxy-server") !== "") {

View File

@ -285,7 +285,7 @@ export class Kubectl {
}
protected getDownloadMirror() {
const mirror = packageMirrors.get(userStore.getPreferences().downloadMirror)
const mirror = packageMirrors.get(userStore.preferences.downloadMirror)
if (mirror) {
return mirror
}

View File

@ -1,19 +1,7 @@
import packageInfo from "../../package.json"
import { bundledKubectl, Kubectl } from "../../src/main/kubectl";
import { UserStore } from "../common/user-store";
jest.mock("../common/user-store", () => {
const userStoreMock: Partial<UserStore> = {
getPreferences() {
return {
downloadMirror: "default"
}
}
}
return {
userStore: userStoreMock,
}
})
jest.mock("../common/user-store");
describe("kubectlVersion", () => {
it("returns bundled version if exactly same version used", async () => {

View File

@ -3,10 +3,10 @@ import * as pty from "node-pty"
import { ShellSession } from "./shell-session";
import { v4 as uuid } from "uuid"
import * as k8s from "@kubernetes/client-node"
import { KubeConfig } from "@kubernetes/client-node"
import { Cluster } from "./cluster"
import logger from "./logger";
import { KubeConfig, V1Pod } from "@kubernetes/client-node";
import { tracker } from "./tracker"
import { Cluster, ClusterPreferences } from "./cluster"
import { tracker } from "../common/tracker";
export class NodeShellSession extends ShellSession {
protected nodeName: string;
@ -107,7 +107,7 @@ export class NodeShellSession extends ShellSession {
const watch = new k8s.Watch(kc);
const req = await watch.watch(`/api/v1/namespaces/kube-system/pods`, {},
// callback is called for each received object.
// callback is called for each received object.
(_type, obj) => {
if (obj.metadata.name == podId && obj.status.phase === "Running") {
resolve(true)
@ -119,9 +119,13 @@ export class NodeShellSession extends ShellSession {
reject(false)
}
);
setTimeout(() => { req.abort(); reject(false); }, 120 * 1000);
setTimeout(() => {
req.abort();
reject(false);
}, 120 * 1000);
})
}
protected deleteNodeShellPod() {
const kc = this.getKubeConfig();
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
@ -130,12 +134,11 @@ export class NodeShellSession extends ShellSession {
}
export async function open(socket: WebSocket, pathToKubeconfig: string, cluster: Cluster, nodeName?: string): Promise<ShellSession> {
return new Promise(async(resolve, reject) => {
return new Promise(async (resolve, reject) => {
let shell = null
if (nodeName) {
shell = new NodeShellSession(socket, pathToKubeconfig, cluster, nodeName)
}
else {
} else {
shell = new ShellSession(socket, pathToKubeconfig, cluster)
}
shell.open()

View File

@ -5,7 +5,7 @@ import path from "path";
import * as tempy from "tempy";
import logger from "./logger"
import { Cluster } from "./cluster";
import { tracker } from "./tracker";
import { tracker } from "../common/tracker";
type KubeObject = {
status: {};

View File

@ -94,7 +94,7 @@ class ConfigRoute extends LensApi {
const data: IConfigRoutePayload = {
clusterName: cluster.contextName,
lensVersion: app.getVersion(),
lensTheme: `kontena-${userStore.getPreferences().colorTheme}`,
lensTheme: `kontena-${userStore.preferences.colorTheme}`,
kubeVersion: cluster.version,
chartsEnabled: true,
isClusterAdmin: cluster.isAdmin,

View File

@ -5,10 +5,10 @@ import path from "path"
import shellEnv from "shell-env"
import { app } from "electron"
import { Kubectl } from "./kubectl"
import { tracker } from "./tracker"
import { Cluster, ClusterPreferences } from "./cluster"
import { helmCli } from "./helm-cli"
import { isWindows } from "../common/vars";
import { tracker } from "../common/tracker";
export class ShellSession extends EventEmitter {
static shellEnvs: Map<string, any> = new Map()

View File

@ -1,4 +0,0 @@
import { Tracker } from "../common/tracker"
import { app, remote } from "electron"
export const tracker = new Tracker(app || remote.app);

View File

@ -1,8 +1,8 @@
import { BrowserWindow, shell } from "electron"
import { PromiseIpc } from "electron-promise-ipc"
import windowStateKeeper from "electron-window-state"
import { tracker } from "./tracker";
import { getStaticUrl } from "../common/register-static";
import { tracker } from "../common/tracker";
export class WindowManager {
public mainWindow: BrowserWindow = null;

View File

@ -1,10 +1,10 @@
import path from "path";
import ElectronStore from "electron-store";
import Config from "conf";
import { isTestEnv } from "../common/vars";
export interface MigrationOpts {
version: string;
run(store: ElectronStore, log: (...args: any[]) => void): void;
run(storeConfig: Config<any>, log: (...args: any[]) => void): void;
}
function infoLog(...args: any[]) {
@ -12,12 +12,12 @@ function infoLog(...args: any[]) {
console.log(...args);
}
export function migration({ version, run }: MigrationOpts) {
export function migration<S = any>({ version, run }: MigrationOpts) {
return {
[version]: (store: ElectronStore) => {
const storeName = path.dirname(store.path);
infoLog(`STORE MIGRATION (${storeName}): ${version}`, );
run(store, infoLog);
[version]: (storeConfig: Config<S>) => {
const storeName = path.dirname(storeConfig.path);
infoLog(`STORE MIGRATION (${storeName}): ${version}`,);
run(storeConfig, infoLog);
}
};
}

View File

@ -27,6 +27,8 @@
<script>
import CubeSpinner from "@/_vue/components/CubeSpinner";
import { tracker } from "../../../common/tracker"
export default {
name: "ClusterPage",
components: {
@ -112,7 +114,7 @@ export default {
this.lens.webview = webview;
}
this.$store.dispatch("attachWebview", this.lens);
this.$tracker.event("cluster", "open");
tracker.event("cluster", "open");
},
hideLens: function() {
this.$store.dispatch("hideWebviews");
@ -131,7 +133,7 @@ export default {
"accessible": function(newStatus, oldStatus) {
console.log("accessible watch, vals:", newStatus, oldStatus);
if(newStatus === false) { // accessble == false
this.$tracker.event("cluster", "open-failed");
tracker.event("cluster", "open-failed");
}
},
}

View File

@ -2,17 +2,14 @@ import "../../common/system-ca"
import "./assets/css/app.scss"
import "prismjs";
import "prismjs/components/prism-yaml"
import { remote } from "electron"
import { PromiseIpc } from 'electron-promise-ipc'
import Vue from 'vue'
import VueElectron from 'vue-electron'
import BootstrapVue from 'bootstrap-vue'
import { PromiseIpc } from 'electron-promise-ipc'
import { Tracker } from "../../common/tracker"
import App from './App'
import router from './router'
import store from './store'
const tracker = new Tracker(remote.app);
const promiseIpc = new PromiseIpc({maxTimeoutMs: 6000});
promiseIpc.on('navigate', async (view) => {
@ -26,7 +23,6 @@ Vue.use(BootstrapVue)
Vue.mixin({
created: function () {
this.$promiseIpc = promiseIpc;
this.$tracker = tracker;
}
})

View File

@ -7,18 +7,13 @@ import KubeContexts from './modules/kube-contexts'
import Clusters from './modules/clusters'
import HelmRepos from './modules/helm-repos'
import Workspaces from './modules/workspaces'
// promise ipc
import { tracker } from "../../../common/tracker"
import { PromiseIpc } from 'electron-promise-ipc'
const promiseIpc = new PromiseIpc( { maxTimeoutMs: 120000 } );
// tracker
import { Tracker } from "../../../common/tracker"
import { remote } from "electron"
const tracker = new Tracker(remote.app);
Vue.use(Vuex);
const promiseIpc = new PromiseIpc( { maxTimeoutMs: 120000 } );
export default new Vuex.Store({
modules: {
Clusters,
@ -31,29 +26,24 @@ export default new Vuex.Store({
hud: {
isMenuVisible: true,
},
seenContexts: userStore.getSeenContexts(),
lastSeenAppVersion: userStore.lastSeenAppVersion(),
seenContexts: userStore.seenContexts,
lastSeenAppVersion: userStore.lastSeenAppVersion,
},
mutations: {
storeSeenContexts(state, context) {
const seenContexts = userStore.storeSeenContext(context);
state.seenContexts = seenContexts
userStore.seenContexts.add(context);
state.seenContexts = Array.from(userStore.seenContexts);
},
updateLastSeenAppVersion(state, appVersion) {
state.lastSeenAppVersion = appVersion;
userStore.setLastSeenAppVersion(appVersion)
userStore.lastSeenAppVersion = appVersion
},
loadPreferences(state) {
this.commit("savePreferences", userStore.getPreferences());
this.commit("savePreferences", userStore.preferences);
},
savePreferences(state, prefs) {
if (prefs.allowTelemetry) {
tracker.event("telemetry", "enabled")
} else {
tracker.event("telemetry", "disabled")
}
state.preferences = prefs;
userStore.setPreferences(prefs);
userStore.preferences = prefs;
this.dispatch("destroyWebviews")
promiseIpc.send("preferencesSaved")
},
@ -67,9 +57,7 @@ export default new Vuex.Store({
actions: {
async init({commit, getters}) {
commit("loadPreferences");
await this.dispatch('refreshClusters', getters.currentWorkspace);
return true;
},
async addSeenContexts({commit}, data){

View File

@ -1,14 +1,12 @@
import Vue from "vue"
import { ClusterInfo } from "../../../../main/cluster"
import { MutationTree, ActionTree, GetterTree } from "vuex"
import { ActionTree, GetterTree, MutationTree } from "vuex"
import { PromiseIpc } from 'electron-promise-ipc'
import { Tracker } from "../../../../common/tracker"
import { remote } from "electron"
import { clusterStore } from "../../../../common/cluster-store"
import { Workspace } from "../../../../common/workspace-store"
import { tracker } from "../../../../common/tracker";
const promiseIpc = new PromiseIpc( { maxTimeoutMs: 120000 } );
const tracker = new Tracker(remote.app);
const promiseIpc = new PromiseIpc({ maxTimeoutMs: 120000 });
export interface LensWebview {
id: string;
@ -26,12 +24,12 @@ const state: ClusterState = {
clusters: []
}
const actions: ActionTree<ClusterState, any> = {
async refreshClusters({commit}, currentWorkspace: Workspace) {
const actions: ActionTree<ClusterState, any> = {
async refreshClusters({ commit }, currentWorkspace: Workspace) {
const clusters: ClusterInfo[] = await promiseIpc.send('getClusters', currentWorkspace.id).catch((error: Error) => {
return false;
})
if(!clusters) return false;
if (!clusters) return false;
commit('updateClusters', clusters);
clusters.forEach((cluster: ClusterInfo) => {
const lens: LensWebview = {
@ -43,29 +41,29 @@ const actions: ActionTree<ClusterState, any> = {
})
return true;
},
async getCluster({commit, getters}, id: string) {
async getCluster({ commit, getters }, id: string) {
const cluster: ClusterInfo = getters.clusters.find((c: ClusterInfo) => c.id === id)
if(!cluster) return null;
if (!cluster) return null;
const remoteCluster = await promiseIpc.send("getCluster", cluster.id)
if(!remoteCluster) return null;
if (!remoteCluster) return null;
Object.assign(cluster, remoteCluster)
commit('updateCluster', cluster);
return cluster;
},
async refineCluster({commit}, id: string) {
async refineCluster({ commit }, id: string) {
console.log("VUEX: ACTION: REFINE CLUSTER", id);
const remoteCluster = await promiseIpc.send("getCluster", id)
if(!remoteCluster) return null;
if (!remoteCluster) return null;
commit('updateCluster', remoteCluster);
return remoteCluster;
},
async stopCluster({dispatch, getters}, id: string) {
async stopCluster({ dispatch, getters }, id: string) {
const cluster: ClusterInfo = getters.clusters.find((c: ClusterInfo) => c.id === id)
if (!cluster) return;
@ -76,7 +74,7 @@ const actions: ActionTree<ClusterState, any> = {
tracker.event("cluster", "stop")
}
},
async removeCluster({getters, dispatch}, id: string) {
async removeCluster({ getters, dispatch }, id: string) {
const cluster: ClusterInfo = getters.clusters.find((c: ClusterInfo) => c.id === id)
if (!cluster) {
return
@ -92,16 +90,16 @@ const actions: ActionTree<ClusterState, any> = {
await dispatch("refreshClusters", getters.currentWorkspace)
return true;
},
async addCluster({commit, getters, dispatch}, data) {
async addCluster({ commit, getters, dispatch }, data) {
const res = await promiseIpc.send("addCluster", data)
if(!res) return null;
if (!res) return null;
tracker.event("cluster", "add");
commit('updateClusters', res.allClusters);
await dispatch("refreshClusters", getters.currentWorkspace);
return res.addedCluster;
},
async clearClusters({commit, getters, dispatch}){
async clearClusters({ commit, getters, dispatch }) {
// todo: clean from main process as well?
getters.lenses.forEach((lens: LensWebview) => {
if (lens.webview) {
@ -113,14 +111,14 @@ const actions: ActionTree<ClusterState, any> = {
return true;
},
async uploadClusterIcon({commit}, data) {
async uploadClusterIcon({ commit }, data) {
const res = await promiseIpc.send("saveClusterIcon", data)
tracker.event("cluster", "upload-icon")
if (res.cluster) commit("updateCluster", res.cluster)
return res
},
async resetClusterIcon({commit}, data) {
async resetClusterIcon({ commit }, data) {
const res = await promiseIpc.send("resetClusterIcon", data.clusterId)
tracker.event("cluster", "reset-icon")
if (res.cluster) commit("updateCluster", res.cluster)
@ -128,7 +126,7 @@ const actions: ActionTree<ClusterState, any> = {
},
// For data structure see: cluster-manager.ts / FeatureInstallRequest
async installClusterFeature({commit}, data) {
async installClusterFeature({ commit }, data) {
// Custom no timeout IPC as install can take very variable time
const ipc = new PromiseIpc();
const response = await ipc.send('installFeature', data)
@ -140,20 +138,19 @@ const actions: ActionTree<ClusterState, any> = {
return response
},
// For data structure see: cluster-manager.ts / FeatureInstallRequest
async upgradeClusterFeature({commit}, data) {
async upgradeClusterFeature({ commit }, data) {
// Custom no timeout IPC as install can take very variable time
const ipc = new PromiseIpc();
const response = await ipc.send('upgradeFeature', data)
console.log("upgrade result:", response);
const cluster = await ipc.send('refreshCluster', data.clusterId)
tracker.event("cluster", "upgrade-feature")
commit("updateCluster", cluster)
return response
},
// For data structure see: cluster-manager.ts / FeatureInstallRequest
async uninstallClusterFeature({commit}, data) {
async uninstallClusterFeature({ commit }, data) {
// Custom no timeout IPC as uninstall can take very variable time
const ipc = new PromiseIpc();
const response = await ipc.send('uninstallFeature', data)
@ -165,7 +162,7 @@ const actions: ActionTree<ClusterState, any> = {
return response
},
attachWebview({commit}, lens: LensWebview) {
attachWebview({ commit }, lens: LensWebview) {
const container: any = document.getElementById("lens-container");
if (!container || !lens.webview) {
return
@ -189,9 +186,11 @@ const actions: ActionTree<ClusterState, any> = {
})
promiseIpc.send("enableClusterSettingsMenuItem", lens.id)
},
detachWebview({commit}, lens: LensWebview) {
detachWebview({ commit }, lens: LensWebview) {
const container: any = document.getElementById("lens-container");
if (!container) { return }
if (!container) {
return
}
container.childNodes.forEach((child: any) => {
if (child === lens.webview) {
container.removeChild(lens.webview)
@ -202,21 +201,23 @@ const actions: ActionTree<ClusterState, any> = {
})
promiseIpc.send("disableClusterSettingsMenuItem")
},
hideWebviews({commit}) {
hideWebviews({ commit }) {
const container: any = document.getElementById("lens-container");
if (!container) { return }
if (!container) {
return
}
container.style = "display: none;"
container.childNodes.forEach((child: any) => {
child.style = "display: none;"
})
promiseIpc.send("disableClusterSettingsMenuItem")
},
destroyWebviews({commit}) {
destroyWebviews({ commit }) {
state.lenses.forEach((lens) => {
this.dispatch("detachWebview", lens)
})
},
storeCluster({commit}, cluster: ClusterInfo) {
storeCluster({ commit }, cluster: ClusterInfo) {
clusterStore.saveCluster(cluster);
commit("updateCluster", cluster)
promiseIpc.send("clusterStored", cluster.id)
@ -250,7 +251,7 @@ const mutations: MutationTree<ClusterState> = {
},
updateCluster(state, cluster) {
state.clusters.forEach((c, index) => {
if(c.id === cluster.id) {
if (c.id === cluster.id) {
Vue.set(state.clusters, index, cluster)
}
})

View File

@ -1,5 +1,5 @@
import { MutationTree, ActionTree, GetterTree } from "vuex"
import { workspaceStore, Workspace, WorkspaceData } from "../../../../common/workspace-store"
import { workspaceStore, Workspace } from "../../../../common/workspace-store"
export interface WorkspaceState {
workspaces: Array<Workspace>;
@ -26,11 +26,11 @@ const mutations: MutationTree<WorkspaceState> = {
setCurrentWorkspace(state, workspace: Workspace) {
state.currentWorkspace = workspace
},
addWorkspace(state, workspace: WorkspaceData) {
addWorkspace(state, workspace: Workspace) {
workspaceStore.saveWorkspace(workspace)
state.workspaces = workspaceStore.getAllWorkspaces()
},
updateWorkspace(state, workspace: WorkspaceData) {
updateWorkspace(state, workspace: Workspace) {
workspaceStore.saveWorkspace(workspace)
state.workspaces = workspaceStore.getAllWorkspaces()
},

View File

@ -3877,21 +3877,21 @@ concurrently@^5.2.0:
tree-kill "^1.2.2"
yargs "^13.3.0"
conf@^6.2.1:
version "6.2.4"
resolved "https://registry.yarnpkg.com/conf/-/conf-6.2.4.tgz#49d23c4e21ef2ac2860f7b5ed25b7b7e484f769f"
integrity sha512-GjgyPRLo1qK1LR9RWAdUagqo+DP18f5HWCFk4va7GS+wpxQTOzfuKTwKOvGW2c01/YXNicAyyoyuSddmdkBzZQ==
conf@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/conf/-/conf-7.0.1.tgz#a3ddd860db0357219b9ccdf92fd618e161fe6848"
integrity sha512-UFA9EPFKK+tedCjz4JKQSxAN0/0r0rjURdq9N805JXWOhFzmr7fTLmG1cnsHCvyYYd65wRjf0KB8zg+WPelkvA==
dependencies:
ajv "^6.10.2"
debounce-fn "^3.0.1"
dot-prop "^5.0.0"
ajv "^6.12.2"
debounce-fn "^4.0.0"
dot-prop "^5.2.0"
env-paths "^2.2.0"
json-schema-typed "^7.0.1"
make-dir "^3.0.0"
json-schema-typed "^7.0.3"
make-dir "^3.1.0"
onetime "^5.1.0"
pkg-up "^3.0.1"
semver "^6.2.0"
write-file-atomic "^3.0.0"
pkg-up "^3.1.0"
semver "^7.3.2"
write-file-atomic "^3.0.3"
config-chain@^1.1.11:
version "1.1.12"
@ -4233,12 +4233,12 @@ de-indent@^1.0.2:
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=
debounce-fn@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/debounce-fn/-/debounce-fn-3.0.1.tgz#034afe8b904d985d1ec1aa589cd15f388741d680"
integrity sha512-aBoJh5AhpqlRoHZjHmOzZlRx+wz2xVwGL9rjs+Kj0EWUrL4/h4K7OD176thl2Tdoqui/AaA4xhHrNArGLAaI3Q==
debounce-fn@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/debounce-fn/-/debounce-fn-4.0.0.tgz#ed76d206d8a50e60de0dd66d494d82835ffe61c7"
integrity sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==
dependencies:
mimic-fn "^2.1.0"
mimic-fn "^3.0.0"
debug@4.1.0:
version "4.1.0"
@ -4547,7 +4547,7 @@ dot-case@^3.0.3:
no-case "^3.0.3"
tslib "^1.10.0"
dot-prop@^5.0.0, dot-prop@^5.2.0:
dot-prop@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb"
integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==
@ -4674,14 +4674,6 @@ electron-publish@22.7.0:
lazy-val "^1.0.4"
mime "^2.4.5"
electron-store@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/electron-store/-/electron-store-5.2.0.tgz#a15718fc1fa21acfd07af55f9b94f9fa6a536665"
integrity sha512-iU3WDqEDAYNYR9XV7p0tJajq/zs9z7Nrn0sAoR5nDyn8h/9dr9kusKbTxD8NtVEBD1TB1pkGMqcbIt/y6knDwQ==
dependencies:
conf "^6.2.1"
type-fest "^0.7.1"
electron-to-chromium@^1.3.413:
version "1.3.464"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.464.tgz#fe13feaa08f6f865d3c89d5d72e54c194f463aa5"
@ -7260,7 +7252,7 @@ json-schema-traverse@^0.4.1:
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json-schema-typed@^7.0.1:
json-schema-typed@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-7.0.3.tgz#23ff481b8b4eebcd2ca123b4fa0409e66469a2d9"
integrity sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==
@ -7703,7 +7695,7 @@ make-dir@^2.0.0:
pify "^4.0.1"
semver "^5.6.0"
make-dir@^3.0.0, make-dir@^3.0.2:
make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
@ -7914,6 +7906,11 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
mimic-fn@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.0.0.tgz#76044cfa8818bbf6999c5c9acadf2d3649b14b4b"
integrity sha512-PiVO95TKvhiwgSwg1IdLYlCTdul38yZxZMIcnDSFIBUm4BNZha2qpQ4GpJ++15bHoKDtrW2D69lMfFwdFYtNZQ==
mimic-response@^1.0.0, mimic-response@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
@ -9071,7 +9068,7 @@ pkg-up@^2.0.0:
dependencies:
find-up "^2.1.0"
pkg-up@^3.0.1:
pkg-up@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
@ -10197,6 +10194,11 @@ serialize-javascript@^3.1.0:
dependencies:
randombytes "^2.1.0"
serializr@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/serializr/-/serializr-2.0.3.tgz#f163b2b39938e1313beaf8457133713df2a936bb"
integrity sha512-YHDTg+QE//5vESifXc7vvncxIAiUiX04FAy1RmSgACkwhvJ9DivljDEjqs5WJzg5KLokI3x3jh30KEfXq5nDJA==
set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
@ -11292,11 +11294,6 @@ type-fest@^0.6.0:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==
type-fest@^0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48"
integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==
type-fest@^0.8.0, type-fest@^0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
@ -11984,7 +11981,7 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
write-file-atomic@^3.0.0:
write-file-atomic@^3.0.0, write-file-atomic@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8"
integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==