web/multinode: storage api, service and store module created
Change-Id: Ieb3dbcd9c967388315f5203598ff56a848477476
This commit is contained in:
parent
55754df110
commit
8686267e06
@ -11,7 +11,7 @@ export class BandwidthClient extends APIClient {
|
||||
private readonly ROOT_PATH: string = '/api/v0/bandwidth';
|
||||
|
||||
/**
|
||||
* Returns bandwidth information for selected node and satellite in any.
|
||||
* Returns bandwidth information for selected node and satellite if any.
|
||||
*
|
||||
* @throws {@link BadRequestError}
|
||||
* This exception is thrown if the input is not a valid.
|
||||
@ -26,7 +26,7 @@ export class BandwidthClient extends APIClient {
|
||||
let path = `${this.ROOT_PATH}`;
|
||||
|
||||
if (satelliteId) {
|
||||
path += `/satellite/${satelliteId}`;
|
||||
path += `/satellites/${satelliteId}`;
|
||||
}
|
||||
|
||||
if (nodeId) {
|
||||
|
85
web/multinode/src/api/storage.ts
Normal file
85
web/multinode/src/api/storage.ts
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import { APIClient } from '@/api/index';
|
||||
import { DiskSpace, Stamp } from '@/storage';
|
||||
|
||||
/**
|
||||
* Client for storage controller of MND api.
|
||||
*/
|
||||
export class StorageClient extends APIClient {
|
||||
private readonly ROOT_PATH: string = '/api/v0/storage';
|
||||
|
||||
/**
|
||||
* Returns storage usage information for selected node and satellite if any.
|
||||
*
|
||||
* @throws {@link BadRequestError}
|
||||
* This exception is thrown if the input is not a valid.
|
||||
*
|
||||
* @throws {@link UnauthorizedError}
|
||||
* Thrown if the auth cookie is missing or invalid.
|
||||
*
|
||||
* @throws {@link InternalError}
|
||||
* Thrown if something goes wrong on server side.
|
||||
*/
|
||||
public async usage(satelliteId: string | null, nodeId: string | null): Promise<Stamp[]> {
|
||||
let path = `${this.ROOT_PATH}/usage`;
|
||||
|
||||
if (satelliteId) {
|
||||
path += `/satellites/${satelliteId}`;
|
||||
}
|
||||
|
||||
if (nodeId) {
|
||||
path += `/${nodeId}`;
|
||||
}
|
||||
|
||||
const response = await this.http.get(path);
|
||||
|
||||
if (!response.ok) {
|
||||
await this.handleError(response);
|
||||
}
|
||||
|
||||
const usage = await response.json() || [];
|
||||
|
||||
return usage.map(stamp => {
|
||||
return new Stamp(stamp.atRestTotal, new Date(stamp.intervalStart));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns disk space information for selected node if selected.
|
||||
*
|
||||
* @throws {@link BadRequestError}
|
||||
* This exception is thrown if the input is not a valid.
|
||||
*
|
||||
* @throws {@link UnauthorizedError}
|
||||
* Thrown if the auth cookie is missing or invalid.
|
||||
*
|
||||
* @throws {@link InternalError}
|
||||
* Thrown if something goes wrong on server side.
|
||||
*/
|
||||
public async diskSpace(nodeId: string | null): Promise<DiskSpace> {
|
||||
let path = `${this.ROOT_PATH}/disk-space`;
|
||||
|
||||
if (nodeId) {
|
||||
path += `/${nodeId}`;
|
||||
}
|
||||
|
||||
const response = await this.http.get(path);
|
||||
|
||||
if (!response.ok) {
|
||||
await this.handleError(response);
|
||||
}
|
||||
|
||||
const diskSpace = await response.json();
|
||||
|
||||
return new DiskSpace(
|
||||
diskSpace.allocated,
|
||||
diskSpace.usedPieces,
|
||||
diskSpace.usedTrash,
|
||||
diskSpace.free,
|
||||
diskSpace.available,
|
||||
diskSpace.overused,
|
||||
);
|
||||
}
|
||||
}
|
@ -8,14 +8,17 @@ import { BandwidthClient } from '@/api/bandwidth';
|
||||
import { NodesClient } from '@/api/nodes';
|
||||
import { Operators as OperatorsClient } from '@/api/operators';
|
||||
import { PayoutsClient } from '@/api/payouts';
|
||||
import { StorageClient } from '@/api/storage';
|
||||
import { BandwidthModule, BandwidthState } from '@/app/store/bandwidth';
|
||||
import { NodesModule, NodesState } from '@/app/store/nodes';
|
||||
import { OperatorsModule, OperatorsState } from '@/app/store/operators';
|
||||
import { PayoutsModule, PayoutsState } from '@/app/store/payouts';
|
||||
import { StorageModule, StorageState } from '@/app/store/storage';
|
||||
import { Bandwidth } from '@/bandwidth/service';
|
||||
import { Nodes } from '@/nodes/service';
|
||||
import { Operators } from '@/operators';
|
||||
import { Payouts } from '@/payouts/service';
|
||||
import { StorageService } from '@/storage/service';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
@ -27,6 +30,7 @@ export class RootState {
|
||||
payouts: PayoutsState;
|
||||
operators: OperatorsState;
|
||||
bandwidth: BandwidthState;
|
||||
storage: StorageState;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,6 +46,7 @@ export class MultinodeStoreOptions implements StoreOptions<RootState> {
|
||||
payouts: PayoutsModule,
|
||||
operators: OperatorsModule,
|
||||
bandwidth: BandwidthModule,
|
||||
storage: StorageModule,
|
||||
) {
|
||||
this.strict = true;
|
||||
this.state = {
|
||||
@ -49,12 +54,14 @@ export class MultinodeStoreOptions implements StoreOptions<RootState> {
|
||||
payouts: payouts.state,
|
||||
bandwidth: bandwidth.state,
|
||||
operators: operators.state,
|
||||
storage: storage.state,
|
||||
};
|
||||
this.modules = {
|
||||
nodes,
|
||||
payouts,
|
||||
bandwidth,
|
||||
operators,
|
||||
storage,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -68,14 +75,17 @@ const bandwidthClient = new BandwidthClient();
|
||||
const bandwidthService = new Bandwidth(bandwidthClient);
|
||||
const operatorsClient: OperatorsClient = new OperatorsClient();
|
||||
const operatorsService: Operators = new Operators(operatorsClient);
|
||||
const storageClient: StorageClient = new StorageClient();
|
||||
const storageService: StorageService = new StorageService(storageClient);
|
||||
|
||||
// Modules
|
||||
const nodesModule: NodesModule = new NodesModule(nodesService);
|
||||
const payoutsModule: PayoutsModule = new PayoutsModule(payoutsService);
|
||||
const bandwidthModule: BandwidthModule = new BandwidthModule(bandwidthService);
|
||||
const operatorsModule: OperatorsModule = new OperatorsModule(operatorsService);
|
||||
const storageModule: StorageModule = new StorageModule(storageService);
|
||||
|
||||
// Store
|
||||
export const store: Store<RootState> = new Vuex.Store<RootState>(
|
||||
new MultinodeStoreOptions(nodesModule, payoutsModule, operatorsModule, bandwidthModule),
|
||||
new MultinodeStoreOptions(nodesModule, payoutsModule, operatorsModule, bandwidthModule, storageModule),
|
||||
);
|
||||
|
87
web/multinode/src/app/store/storage.ts
Normal file
87
web/multinode/src/app/store/storage.ts
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import { ActionContext, ActionTree, GetterTree, Module, MutationTree } from 'vuex';
|
||||
|
||||
import { RootState } from '@/app/store/index';
|
||||
import { DiskSpace, Stamp } from '@/storage';
|
||||
import { StorageService } from '@/storage/service';
|
||||
|
||||
/**
|
||||
* StorageState is a representation of by day and total storage usage.
|
||||
*/
|
||||
export class StorageState {
|
||||
public usage: Stamp[] = [];
|
||||
public diskSpace: DiskSpace = new DiskSpace();
|
||||
}
|
||||
|
||||
/**
|
||||
* StorageModule is a part of a global store that encapsulates all storage related logic.
|
||||
*/
|
||||
export class StorageModule implements Module<StorageState, RootState> {
|
||||
public readonly namespaced: boolean;
|
||||
public readonly state: StorageState;
|
||||
public readonly getters?: GetterTree<StorageState, RootState>;
|
||||
public readonly actions: ActionTree<StorageState, RootState>;
|
||||
public readonly mutations: MutationTree<StorageState>;
|
||||
|
||||
private readonly storage: StorageService;
|
||||
|
||||
public constructor(storage: StorageService) {
|
||||
this.storage = storage;
|
||||
|
||||
this.namespaced = true;
|
||||
this.state = new StorageState();
|
||||
this.mutations = {
|
||||
setUsage: this.setUsage,
|
||||
setDiskSpace: this.setDiskSpace,
|
||||
};
|
||||
this.actions = {
|
||||
usage: this.usage.bind(this),
|
||||
diskSpace: this.diskSpace.bind(this),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* setUsage mutation will set storage usage.
|
||||
* @param state - state of the module.
|
||||
* @param usage
|
||||
*/
|
||||
public setUsage(state: StorageState, usage: Stamp[]): void {
|
||||
state.usage = usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* setDiskSpace mutation will set storage totals.
|
||||
* @param state - state of the module.
|
||||
* @param diskSpace
|
||||
*/
|
||||
public setDiskSpace(state: StorageState, diskSpace: DiskSpace): void {
|
||||
state.diskSpace = diskSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* usage action loads storage usage information.
|
||||
* @param ctx - context of the Vuex action.
|
||||
*/
|
||||
public async usage(ctx: ActionContext<StorageState, RootState>): Promise<void> {
|
||||
const selectedSatelliteId = ctx.rootState.nodes.selectedSatellite ? ctx.rootState.nodes.selectedSatellite.id : null;
|
||||
const selectedNodeId = ctx.rootState.nodes.selectedNode ? ctx.rootState.nodes.selectedNode.id : null;
|
||||
|
||||
const usage = await this.storage.usage(selectedSatelliteId, selectedNodeId);
|
||||
|
||||
ctx.commit('setUsage', usage);
|
||||
}
|
||||
|
||||
/**
|
||||
* diskSpace action loads total storage usage information.
|
||||
* @param ctx - context of the Vuex action.
|
||||
*/
|
||||
public async diskSpace(ctx: ActionContext<StorageState, RootState>): Promise<void> {
|
||||
const selectedNodeId = ctx.rootState.nodes.selectedNode ? ctx.rootState.nodes.selectedNode.id : null;
|
||||
|
||||
const diskSpace = await this.storage.diskSpace(selectedNodeId);
|
||||
|
||||
ctx.commit('setDiskSpace', diskSpace);
|
||||
}
|
||||
}
|
@ -3,36 +3,10 @@
|
||||
|
||||
import { BandwidthRollup } from '@/bandwidth';
|
||||
import { SizeBreakpoints } from '@/private/memory/size';
|
||||
import { Stamp } from '@/storage';
|
||||
|
||||
const shortMonthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'];
|
||||
|
||||
// TODO: move to diskspace package
|
||||
/**
|
||||
* Stamp is storage usage stamp for satellite at some point in time
|
||||
*/
|
||||
export class Stamp {
|
||||
public atRestTotal: number;
|
||||
public intervalStart: Date;
|
||||
|
||||
public constructor(atRestTotal: number = 0, intervalStart: Date = new Date()) {
|
||||
this.atRestTotal = atRestTotal;
|
||||
this.intervalStart = intervalStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new empty instance of stamp with defined date
|
||||
* @param date - holds specific date of the month
|
||||
* @returns Stamp - new empty instance of stamp with defined date
|
||||
*/
|
||||
public static emptyWithDate(date: number): Stamp {
|
||||
const now = new Date();
|
||||
now.setUTCDate(date);
|
||||
now.setUTCHours(0, 0, 0, 0);
|
||||
|
||||
return new Stamp(0, now);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to display correct and convenient data on chart.
|
||||
*/
|
||||
|
42
web/multinode/src/storage/index.ts
Normal file
42
web/multinode/src/storage/index.ts
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
/**
|
||||
* Stamp is storage usage stamp for satellite at some point in time
|
||||
*/
|
||||
export class Stamp {
|
||||
public atRestTotal: number;
|
||||
public intervalStart: Date;
|
||||
|
||||
public constructor(atRestTotal: number = 0, intervalStart: Date = new Date()) {
|
||||
this.atRestTotal = atRestTotal;
|
||||
this.intervalStart = intervalStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new empty instance of stamp with defined date
|
||||
* @param date - holds specific date of the month
|
||||
* @returns Stamp - new empty instance of stamp with defined date
|
||||
*/
|
||||
public static emptyWithDate(date: number): Stamp {
|
||||
const now = new Date();
|
||||
now.setUTCDate(date);
|
||||
now.setUTCHours(0, 0, 0, 0);
|
||||
|
||||
return new Stamp(0, now);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DiskSpace is total storage usage for node if any selected
|
||||
*/
|
||||
export class DiskSpace {
|
||||
public constructor(
|
||||
public allocated: number = 0,
|
||||
public usedPieces: number = 0,
|
||||
public usedTrash: number = 0,
|
||||
public free: number = 0,
|
||||
public available: number = 0,
|
||||
public overused: number = 0,
|
||||
) {}
|
||||
}
|
48
web/multinode/src/storage/service.ts
Normal file
48
web/multinode/src/storage/service.ts
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import { StorageClient } from '@/api/storage';
|
||||
import { DiskSpace, Stamp } from '@/storage';
|
||||
|
||||
/**
|
||||
* Exposes all bandwidth related logic
|
||||
*/
|
||||
export class StorageService {
|
||||
private readonly storage: StorageClient;
|
||||
|
||||
public constructor(bandwidth: StorageClient) {
|
||||
this.storage = bandwidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns storage usage for selected satellite and node if any.
|
||||
*
|
||||
* @throws {@link BadRequestError}
|
||||
* This exception is thrown if the input is not a valid.
|
||||
*
|
||||
* @throws {@link UnauthorizedError}
|
||||
* Thrown if the auth cookie is missing or invalid.
|
||||
*
|
||||
* @throws {@link InternalError}
|
||||
* Thrown if something goes wrong on server side.
|
||||
*/
|
||||
public async usage(satelliteId: string | null, nodeId: string | null): Promise<Stamp[]> {
|
||||
return await this.storage.usage(satelliteId, nodeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns total storage usage for selected node if any.
|
||||
*
|
||||
* @throws {@link BadRequestError}
|
||||
* This exception is thrown if the input is not a valid.
|
||||
*
|
||||
* @throws {@link UnauthorizedError}
|
||||
* Thrown if the auth cookie is missing or invalid.
|
||||
*
|
||||
* @throws {@link InternalError}
|
||||
* Thrown if something goes wrong on server side.
|
||||
*/
|
||||
public async diskSpace(nodeId: string | null): Promise<DiskSpace> {
|
||||
return await this.storage.diskSpace(nodeId);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user