mirror of
https://github.com/unraid/api.git
synced 2026-01-01 06:01:18 -06:00
feat: basic array controls (#1291)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Expanded API operations for array management, including new mutations for state changes and disk handling. - Introduced new enumeration and input types for managing array states in the GraphQL schema. - Added a new resolver for handling array mutations in the GraphQL API. - **Chores** - Upgraded configuration version to 4.4.1 and refined connectivity status reporting. - **Refactor** - Streamlined request processing with improved error handling to provide clearer feedback on connectivity issues. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
[api]
|
[api]
|
||||||
version="4.1.3"
|
version="4.4.1"
|
||||||
extraOrigins="https://google.com,https://test.com"
|
extraOrigins="https://google.com,https://test.com"
|
||||||
[local]
|
[local]
|
||||||
sandbox="yes"
|
sandbox="yes"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[api]
|
[api]
|
||||||
version="4.1.3"
|
version="4.4.1"
|
||||||
extraOrigins="https://google.com,https://test.com"
|
extraOrigins="https://google.com,https://test.com"
|
||||||
[local]
|
[local]
|
||||||
sandbox="yes"
|
sandbox="yes"
|
||||||
@@ -20,5 +20,5 @@ dynamicRemoteAccessType="DISABLED"
|
|||||||
ssoSubIds=""
|
ssoSubIds=""
|
||||||
allowedOrigins="/var/run/unraid-notifications.sock, /var/run/unraid-php.sock, /var/run/unraid-cli.sock, http://localhost:8080, https://localhost:4443, https://tower.local:4443, https://192.168.1.150:4443, https://tower:4443, https://192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net:4443, https://85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net:8443, https://10-252-0-1.hash.myunraid.net:4443, https://10-252-1-1.hash.myunraid.net:4443, https://10-253-3-1.hash.myunraid.net:4443, https://10-253-4-1.hash.myunraid.net:4443, https://10-253-5-1.hash.myunraid.net:4443, https://10-100-0-1.hash.myunraid.net:4443, https://10-100-0-2.hash.myunraid.net:4443, https://10-123-1-2.hash.myunraid.net:4443, https://221-123-121-112.hash.myunraid.net:4443, https://google.com, https://test.com, https://connect.myunraid.net, https://connect-staging.myunraid.net, https://dev-my.myunraid.net:4000, https://studio.apollographql.com"
|
allowedOrigins="/var/run/unraid-notifications.sock, /var/run/unraid-php.sock, /var/run/unraid-cli.sock, http://localhost:8080, https://localhost:4443, https://tower.local:4443, https://192.168.1.150:4443, https://tower:4443, https://192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net:4443, https://85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net:8443, https://10-252-0-1.hash.myunraid.net:4443, https://10-252-1-1.hash.myunraid.net:4443, https://10-253-3-1.hash.myunraid.net:4443, https://10-253-4-1.hash.myunraid.net:4443, https://10-253-5-1.hash.myunraid.net:4443, https://10-100-0-1.hash.myunraid.net:4443, https://10-100-0-2.hash.myunraid.net:4443, https://10-123-1-2.hash.myunraid.net:4443, https://221-123-121-112.hash.myunraid.net:4443, https://google.com, https://test.com, https://connect.myunraid.net, https://connect-staging.myunraid.net, https://dev-my.myunraid.net:4000, https://studio.apollographql.com"
|
||||||
[connectionStatus]
|
[connectionStatus]
|
||||||
minigraph="PRE_INIT"
|
minigraph="ERROR_RETRYING"
|
||||||
upnpStatus=""
|
upnpStatus=""
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { GraphQLError } from 'graphql';
|
|||||||
import { sum } from 'lodash-es';
|
import { sum } from 'lodash-es';
|
||||||
|
|
||||||
import type { ArrayCapacity, ArrayType } from '@app/graphql/generated/api/types.js';
|
import type { ArrayCapacity, ArrayType } from '@app/graphql/generated/api/types.js';
|
||||||
import { getServerIdentifier } from '@app/core/utils/server-identifier.js';
|
|
||||||
import { ArrayDiskType } from '@app/graphql/generated/api/types.js';
|
import { ArrayDiskType } from '@app/graphql/generated/api/types.js';
|
||||||
import { store } from '@app/store/index.js';
|
import { store } from '@app/store/index.js';
|
||||||
import { FileLoadStatus } from '@app/store/types.js';
|
import { FileLoadStatus } from '@app/store/types.js';
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { got } from 'got';
|
import { got } from 'got';
|
||||||
|
|
||||||
|
import { AppError } from '@app/core/errors/app-error.js';
|
||||||
import { logger } from '@app/core/log.js';
|
import { logger } from '@app/core/log.js';
|
||||||
import { type LooseObject } from '@app/core/types/index.js';
|
import { type LooseObject } from '@app/core/types/index.js';
|
||||||
import { catchHandlers } from '@app/core/utils/misc/catch-handlers.js';
|
|
||||||
import { DRY_RUN } from '@app/environment.js';
|
import { DRY_RUN } from '@app/environment.js';
|
||||||
import { getters } from '@app/store/index.js';
|
import { getters } from '@app/store/index.js';
|
||||||
|
|
||||||
@@ -27,10 +27,15 @@ export const emcmd = async (commands: LooseObject) => {
|
|||||||
// Ensure we only log on dry-run
|
// Ensure we only log on dry-run
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Untested, this code is unused right now so going to assume it's probably not working well anyway, swapped
|
|
||||||
// to got to remove this request-promise dependency
|
|
||||||
return got
|
return got
|
||||||
.get(url, { searchParams: { ...commands, csrf_token: csrfToken } })
|
.get(url, {
|
||||||
.catch(catchHandlers.emhttpd);
|
enableUnixSockets: true,
|
||||||
// return request.get(url, options).catch(catchHandlers.emhttpd);
|
searchParams: { ...commands, csrf_token: csrfToken },
|
||||||
|
})
|
||||||
|
.catch((error: NodeJS.ErrnoException) => {
|
||||||
|
if (error.code === 'ENOENT') {
|
||||||
|
throw new AppError('emhttpd socket unavailable.');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import * as Types from '@app/graphql/generated/api/types.js';
|
import * as Types from '@app/graphql/generated/api/types.js';
|
||||||
|
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { AccessUrl, AccessUrlInput, AddPermissionInput, AddRoleForApiKeyInput, AddRoleForUserInput, AllowedOriginInput, ApiKey, ApiKeyResponse, ApiKeyWithSecret, ApiSettingsInput, ArrayType, ArrayCapacity, ArrayDisk, ArrayDiskFsColor, ArrayDiskStatus, ArrayDiskType, ArrayPendingState, ArrayState, Baseboard, Capacity, Case, Cloud, CloudResponse, Config, ConfigErrorState, Connect, ConnectSettings, ConnectSettingsValues, ConnectSignInInput, ConnectUserInfoInput, ContainerHostConfig, ContainerMount, ContainerPort, ContainerPortType, ContainerState, CreateApiKeyInput, Devices, Disk, DiskFsType, DiskInterfaceType, DiskPartition, DiskSmartStatus, Display, Docker, DockerContainer, DockerNetwork, DynamicRemoteAccessStatus, DynamicRemoteAccessType, EnableDynamicRemoteAccessInput, Flash, Gpu, Importance, Info, InfoApps, InfoCpu, InfoMemory, KeyFile, LogFile, LogFileContent, Me, MemoryFormFactor, MemoryLayout, MemoryType, MinigraphStatus, MinigraphqlResponse, Mount, Network, Node, Notification, NotificationCounts, NotificationData, NotificationFilter, NotificationOverview, NotificationType, Notifications, NotificationslistArgs, Os, Owner, ParityCheck, Partition, Pci, Permission, ProfileModel, Registration, RegistrationState, RelayResponse, RemoteAccess, RemoveRoleFromApiKeyInput, Resource, Role, Server, ServerStatus, Service, SetupRemoteAccessInput, Share, System, Temperature, Theme, URL_TYPE, UnassignedDevice, Uptime, Usb, User, UserAccount, Vars, Versions, VmDomain, VmState, Vms, WAN_ACCESS_TYPE, WAN_FORWARD_TYPE, Welcome, addUserInput, arrayDiskInput, deleteUserInput, mdState, registrationType, usersInput } from '@app/graphql/generated/api/types.js'
|
import { AccessUrl, AccessUrlInput, AddPermissionInput, AddRoleForApiKeyInput, AddRoleForUserInput, AllowedOriginInput, ApiKey, ApiKeyResponse, ApiKeyWithSecret, ApiSettingsInput, ArrayType, ArrayCapacity, ArrayDisk, ArrayDiskFsColor, ArrayDiskInput, ArrayDiskStatus, ArrayDiskType, ArrayMutations, ArrayMutationsaddDiskToArrayArgs, ArrayMutationsclearArrayDiskStatisticsArgs, ArrayMutationsmountArrayDiskArgs, ArrayMutationsremoveDiskFromArrayArgs, ArrayMutationssetStateArgs, ArrayMutationsunmountArrayDiskArgs, ArrayPendingState, ArrayState, ArrayStateInput, ArrayStateInputState, Baseboard, Capacity, Case, Cloud, CloudResponse, Config, ConfigErrorState, Connect, ConnectSettings, ConnectSettingsValues, ConnectSignInInput, ConnectUserInfoInput, ContainerHostConfig, ContainerMount, ContainerPort, ContainerPortType, ContainerState, CreateApiKeyInput, Devices, Disk, DiskFsType, DiskInterfaceType, DiskPartition, DiskSmartStatus, Display, Docker, DockerContainer, DockerNetwork, DynamicRemoteAccessStatus, DynamicRemoteAccessType, EnableDynamicRemoteAccessInput, Flash, Gpu, Importance, Info, InfoApps, InfoCpu, InfoMemory, KeyFile, LogFile, LogFileContent, Me, MemoryFormFactor, MemoryLayout, MemoryType, MinigraphStatus, MinigraphqlResponse, Mount, Network, Node, Notification, NotificationCounts, NotificationData, NotificationFilter, NotificationOverview, NotificationType, Notifications, NotificationslistArgs, Os, Owner, ParityCheck, Partition, Pci, Permission, ProfileModel, Registration, RegistrationState, RelayResponse, RemoteAccess, RemoveRoleFromApiKeyInput, Resource, Role, Server, ServerStatus, Service, SetupRemoteAccessInput, Share, System, Temperature, Theme, URL_TYPE, UnassignedDevice, Uptime, Usb, User, UserAccount, Vars, Versions, VmDomain, VmState, Vms, WAN_ACCESS_TYPE, WAN_FORWARD_TYPE, Welcome, addUserInput, deleteUserInput, mdState, registrationType, usersInput } from '@app/graphql/generated/api/types.js'
|
||||||
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
||||||
|
|
||||||
type Properties<T> = Required<{
|
type Properties<T> = Required<{
|
||||||
@@ -25,6 +25,8 @@ export const ArrayPendingStateSchema = z.nativeEnum(ArrayPendingState);
|
|||||||
|
|
||||||
export const ArrayStateSchema = z.nativeEnum(ArrayState);
|
export const ArrayStateSchema = z.nativeEnum(ArrayState);
|
||||||
|
|
||||||
|
export const ArrayStateInputStateSchema = z.nativeEnum(ArrayStateInputState);
|
||||||
|
|
||||||
export const ConfigErrorStateSchema = z.nativeEnum(ConfigErrorState);
|
export const ConfigErrorStateSchema = z.nativeEnum(ConfigErrorState);
|
||||||
|
|
||||||
export const ContainerPortTypeSchema = z.nativeEnum(ContainerPortType);
|
export const ContainerPortTypeSchema = z.nativeEnum(ContainerPortType);
|
||||||
@@ -213,6 +215,67 @@ export function ArrayDiskSchema(): z.ZodObject<Properties<ArrayDisk>> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ArrayDiskInputSchema(): z.ZodObject<Properties<ArrayDiskInput>> {
|
||||||
|
return z.object({
|
||||||
|
id: z.string(),
|
||||||
|
slot: z.number().nullish()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ArrayMutationsSchema(): z.ZodObject<Properties<ArrayMutations>> {
|
||||||
|
return z.object({
|
||||||
|
__typename: z.literal('ArrayMutations').optional(),
|
||||||
|
addDiskToArray: ArrayTypeSchema().nullish(),
|
||||||
|
clearArrayDiskStatistics: z.record(z.string(), z.any()).nullish(),
|
||||||
|
mountArrayDisk: DiskSchema().nullish(),
|
||||||
|
removeDiskFromArray: ArrayTypeSchema().nullish(),
|
||||||
|
setState: ArrayTypeSchema().nullish(),
|
||||||
|
unmountArrayDisk: DiskSchema().nullish()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ArrayMutationsaddDiskToArrayArgsSchema(): z.ZodObject<Properties<ArrayMutationsaddDiskToArrayArgs>> {
|
||||||
|
return z.object({
|
||||||
|
input: z.lazy(() => ArrayDiskInputSchema().nullish())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ArrayMutationsclearArrayDiskStatisticsArgsSchema(): z.ZodObject<Properties<ArrayMutationsclearArrayDiskStatisticsArgs>> {
|
||||||
|
return z.object({
|
||||||
|
id: z.string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ArrayMutationsmountArrayDiskArgsSchema(): z.ZodObject<Properties<ArrayMutationsmountArrayDiskArgs>> {
|
||||||
|
return z.object({
|
||||||
|
id: z.string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ArrayMutationsremoveDiskFromArrayArgsSchema(): z.ZodObject<Properties<ArrayMutationsremoveDiskFromArrayArgs>> {
|
||||||
|
return z.object({
|
||||||
|
input: z.lazy(() => ArrayDiskInputSchema().nullish())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ArrayMutationssetStateArgsSchema(): z.ZodObject<Properties<ArrayMutationssetStateArgs>> {
|
||||||
|
return z.object({
|
||||||
|
input: z.lazy(() => ArrayStateInputSchema().nullish())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ArrayMutationsunmountArrayDiskArgsSchema(): z.ZodObject<Properties<ArrayMutationsunmountArrayDiskArgs>> {
|
||||||
|
return z.object({
|
||||||
|
id: z.string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ArrayStateInputSchema(): z.ZodObject<Properties<ArrayStateInput>> {
|
||||||
|
return z.object({
|
||||||
|
desiredState: z.lazy(() => ArrayStateInputStateSchema)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function BaseboardSchema(): z.ZodObject<Properties<Baseboard>> {
|
export function BaseboardSchema(): z.ZodObject<Properties<Baseboard>> {
|
||||||
return z.object({
|
return z.object({
|
||||||
__typename: z.literal('Baseboard').optional(),
|
__typename: z.literal('Baseboard').optional(),
|
||||||
@@ -1303,13 +1366,6 @@ export function addUserInputSchema(): z.ZodObject<Properties<addUserInput>> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function arrayDiskInputSchema(): z.ZodObject<Properties<arrayDiskInput>> {
|
|
||||||
return z.object({
|
|
||||||
id: z.string(),
|
|
||||||
slot: z.number().nullish()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteUserInputSchema(): z.ZodObject<Properties<deleteUserInput>> {
|
export function deleteUserInputSchema(): z.ZodObject<Properties<deleteUserInput>> {
|
||||||
return z.object({
|
return z.object({
|
||||||
name: z.string()
|
name: z.string()
|
||||||
|
|||||||
@@ -191,6 +191,13 @@ export enum ArrayDiskFsColor {
|
|||||||
YELLOW_ON = 'yellow_on'
|
YELLOW_ON = 'yellow_on'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ArrayDiskInput = {
|
||||||
|
/** Disk ID */
|
||||||
|
id: Scalars['ID']['input'];
|
||||||
|
/** The slot for the disk */
|
||||||
|
slot?: InputMaybe<Scalars['Int']['input']>;
|
||||||
|
};
|
||||||
|
|
||||||
export enum ArrayDiskStatus {
|
export enum ArrayDiskStatus {
|
||||||
/** disabled, old disk still present */
|
/** disabled, old disk still present */
|
||||||
DISK_DSBL = 'DISK_DSBL',
|
DISK_DSBL = 'DISK_DSBL',
|
||||||
@@ -223,6 +230,49 @@ export enum ArrayDiskType {
|
|||||||
PARITY = 'Parity'
|
PARITY = 'Parity'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ArrayMutations = {
|
||||||
|
__typename?: 'ArrayMutations';
|
||||||
|
/** Add new disk to array */
|
||||||
|
addDiskToArray?: Maybe<ArrayType>;
|
||||||
|
clearArrayDiskStatistics?: Maybe<Scalars['JSON']['output']>;
|
||||||
|
mountArrayDisk?: Maybe<Disk>;
|
||||||
|
/** Remove existing disk from array. NOTE: The array must be stopped before running this otherwise it'll throw an error. */
|
||||||
|
removeDiskFromArray?: Maybe<ArrayType>;
|
||||||
|
/** Set array state */
|
||||||
|
setState?: Maybe<ArrayType>;
|
||||||
|
unmountArrayDisk?: Maybe<Disk>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type ArrayMutationsaddDiskToArrayArgs = {
|
||||||
|
input?: InputMaybe<ArrayDiskInput>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type ArrayMutationsclearArrayDiskStatisticsArgs = {
|
||||||
|
id: Scalars['ID']['input'];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type ArrayMutationsmountArrayDiskArgs = {
|
||||||
|
id: Scalars['ID']['input'];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type ArrayMutationsremoveDiskFromArrayArgs = {
|
||||||
|
input?: InputMaybe<ArrayDiskInput>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type ArrayMutationssetStateArgs = {
|
||||||
|
input?: InputMaybe<ArrayStateInput>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type ArrayMutationsunmountArrayDiskArgs = {
|
||||||
|
id: Scalars['ID']['input'];
|
||||||
|
};
|
||||||
|
|
||||||
export enum ArrayPendingState {
|
export enum ArrayPendingState {
|
||||||
/** Array has no data disks */
|
/** Array has no data disks */
|
||||||
NO_DATA_DISKS = 'no_data_disks',
|
NO_DATA_DISKS = 'no_data_disks',
|
||||||
@@ -259,6 +309,18 @@ export enum ArrayState {
|
|||||||
TOO_MANY_MISSING_DISKS = 'TOO_MANY_MISSING_DISKS'
|
TOO_MANY_MISSING_DISKS = 'TOO_MANY_MISSING_DISKS'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ArrayStateInput = {
|
||||||
|
/** Array state */
|
||||||
|
desiredState: ArrayStateInputState;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum ArrayStateInputState {
|
||||||
|
/** Start array */
|
||||||
|
START = 'START',
|
||||||
|
/** Stop array */
|
||||||
|
STOP = 'STOP'
|
||||||
|
}
|
||||||
|
|
||||||
export type Baseboard = {
|
export type Baseboard = {
|
||||||
__typename?: 'Baseboard';
|
__typename?: 'Baseboard';
|
||||||
assetTag?: Maybe<Scalars['String']['output']>;
|
assetTag?: Maybe<Scalars['String']['output']>;
|
||||||
@@ -731,8 +793,6 @@ export type Mount = {
|
|||||||
|
|
||||||
export type Mutation = {
|
export type Mutation = {
|
||||||
__typename?: 'Mutation';
|
__typename?: 'Mutation';
|
||||||
/** Add new disk to array */
|
|
||||||
addDiskToArray?: Maybe<ArrayType>;
|
|
||||||
addPermission: Scalars['Boolean']['output'];
|
addPermission: Scalars['Boolean']['output'];
|
||||||
addRoleForApiKey: Scalars['Boolean']['output'];
|
addRoleForApiKey: Scalars['Boolean']['output'];
|
||||||
addRoleForUser: Scalars['Boolean']['output'];
|
addRoleForUser: Scalars['Boolean']['output'];
|
||||||
@@ -742,9 +802,9 @@ export type Mutation = {
|
|||||||
/** Marks a notification as archived. */
|
/** Marks a notification as archived. */
|
||||||
archiveNotification: Notification;
|
archiveNotification: Notification;
|
||||||
archiveNotifications: NotificationOverview;
|
archiveNotifications: NotificationOverview;
|
||||||
|
array?: Maybe<ArrayMutations>;
|
||||||
/** Cancel parity check */
|
/** Cancel parity check */
|
||||||
cancelParityCheck?: Maybe<Scalars['JSON']['output']>;
|
cancelParityCheck?: Maybe<Scalars['JSON']['output']>;
|
||||||
clearArrayDiskStatistics?: Maybe<Scalars['JSON']['output']>;
|
|
||||||
connectSignIn: Scalars['Boolean']['output'];
|
connectSignIn: Scalars['Boolean']['output'];
|
||||||
connectSignOut: Scalars['Boolean']['output'];
|
connectSignOut: Scalars['Boolean']['output'];
|
||||||
createApiKey: ApiKeyWithSecret;
|
createApiKey: ApiKeyWithSecret;
|
||||||
@@ -756,29 +816,21 @@ export type Mutation = {
|
|||||||
deleteUser?: Maybe<User>;
|
deleteUser?: Maybe<User>;
|
||||||
enableDynamicRemoteAccess: Scalars['Boolean']['output'];
|
enableDynamicRemoteAccess: Scalars['Boolean']['output'];
|
||||||
login?: Maybe<Scalars['String']['output']>;
|
login?: Maybe<Scalars['String']['output']>;
|
||||||
mountArrayDisk?: Maybe<Disk>;
|
|
||||||
/** Pause parity check */
|
/** Pause parity check */
|
||||||
pauseParityCheck?: Maybe<Scalars['JSON']['output']>;
|
pauseParityCheck?: Maybe<Scalars['JSON']['output']>;
|
||||||
reboot?: Maybe<Scalars['String']['output']>;
|
reboot?: Maybe<Scalars['String']['output']>;
|
||||||
/** Reads each notification to recompute & update the overview. */
|
/** Reads each notification to recompute & update the overview. */
|
||||||
recalculateOverview: NotificationOverview;
|
recalculateOverview: NotificationOverview;
|
||||||
/** Remove existing disk from array. NOTE: The array must be stopped before running this otherwise it'll throw an error. */
|
|
||||||
removeDiskFromArray?: Maybe<ArrayType>;
|
|
||||||
removeRoleFromApiKey: Scalars['Boolean']['output'];
|
removeRoleFromApiKey: Scalars['Boolean']['output'];
|
||||||
/** Resume parity check */
|
/** Resume parity check */
|
||||||
resumeParityCheck?: Maybe<Scalars['JSON']['output']>;
|
resumeParityCheck?: Maybe<Scalars['JSON']['output']>;
|
||||||
setAdditionalAllowedOrigins: Array<Scalars['String']['output']>;
|
setAdditionalAllowedOrigins: Array<Scalars['String']['output']>;
|
||||||
setupRemoteAccess: Scalars['Boolean']['output'];
|
setupRemoteAccess: Scalars['Boolean']['output'];
|
||||||
shutdown?: Maybe<Scalars['String']['output']>;
|
shutdown?: Maybe<Scalars['String']['output']>;
|
||||||
/** Start array */
|
|
||||||
startArray?: Maybe<ArrayType>;
|
|
||||||
/** Start parity check */
|
/** Start parity check */
|
||||||
startParityCheck?: Maybe<Scalars['JSON']['output']>;
|
startParityCheck?: Maybe<Scalars['JSON']['output']>;
|
||||||
/** Stop array */
|
|
||||||
stopArray?: Maybe<ArrayType>;
|
|
||||||
unarchiveAll: NotificationOverview;
|
unarchiveAll: NotificationOverview;
|
||||||
unarchiveNotifications: NotificationOverview;
|
unarchiveNotifications: NotificationOverview;
|
||||||
unmountArrayDisk?: Maybe<Disk>;
|
|
||||||
/** Marks a notification as unread. */
|
/** Marks a notification as unread. */
|
||||||
unreadNotification: Notification;
|
unreadNotification: Notification;
|
||||||
/**
|
/**
|
||||||
@@ -789,11 +841,6 @@ export type Mutation = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationaddDiskToArrayArgs = {
|
|
||||||
input?: InputMaybe<arrayDiskInput>;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationaddPermissionArgs = {
|
export type MutationaddPermissionArgs = {
|
||||||
input: AddPermissionInput;
|
input: AddPermissionInput;
|
||||||
};
|
};
|
||||||
@@ -829,11 +876,6 @@ export type MutationarchiveNotificationsArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationclearArrayDiskStatisticsArgs = {
|
|
||||||
id: Scalars['ID']['input'];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationconnectSignInArgs = {
|
export type MutationconnectSignInArgs = {
|
||||||
input: ConnectSignInInput;
|
input: ConnectSignInInput;
|
||||||
};
|
};
|
||||||
@@ -871,16 +913,6 @@ export type MutationloginArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationmountArrayDiskArgs = {
|
|
||||||
id: Scalars['ID']['input'];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationremoveDiskFromArrayArgs = {
|
|
||||||
input?: InputMaybe<arrayDiskInput>;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationremoveRoleFromApiKeyArgs = {
|
export type MutationremoveRoleFromApiKeyArgs = {
|
||||||
input: RemoveRoleFromApiKeyInput;
|
input: RemoveRoleFromApiKeyInput;
|
||||||
};
|
};
|
||||||
@@ -911,11 +943,6 @@ export type MutationunarchiveNotificationsArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationunmountArrayDiskArgs = {
|
|
||||||
id: Scalars['ID']['input'];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export type MutationunreadNotificationArgs = {
|
export type MutationunreadNotificationArgs = {
|
||||||
id: Scalars['String']['input'];
|
id: Scalars['String']['input'];
|
||||||
};
|
};
|
||||||
@@ -1823,13 +1850,6 @@ export type addUserInput = {
|
|||||||
password: Scalars['String']['input'];
|
password: Scalars['String']['input'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type arrayDiskInput = {
|
|
||||||
/** Disk ID */
|
|
||||||
id: Scalars['ID']['input'];
|
|
||||||
/** The slot for the disk */
|
|
||||||
slot?: InputMaybe<Scalars['Int']['input']>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type deleteUserInput = {
|
export type deleteUserInput = {
|
||||||
name: Scalars['String']['input'];
|
name: Scalars['String']['input'];
|
||||||
};
|
};
|
||||||
@@ -1945,10 +1965,14 @@ export type ResolversTypes = ResolversObject<{
|
|||||||
ArrayCapacity: ResolverTypeWrapper<ArrayCapacity>;
|
ArrayCapacity: ResolverTypeWrapper<ArrayCapacity>;
|
||||||
ArrayDisk: ResolverTypeWrapper<ArrayDisk>;
|
ArrayDisk: ResolverTypeWrapper<ArrayDisk>;
|
||||||
ArrayDiskFsColor: ArrayDiskFsColor;
|
ArrayDiskFsColor: ArrayDiskFsColor;
|
||||||
|
ArrayDiskInput: ArrayDiskInput;
|
||||||
ArrayDiskStatus: ArrayDiskStatus;
|
ArrayDiskStatus: ArrayDiskStatus;
|
||||||
ArrayDiskType: ArrayDiskType;
|
ArrayDiskType: ArrayDiskType;
|
||||||
|
ArrayMutations: ResolverTypeWrapper<ArrayMutations>;
|
||||||
ArrayPendingState: ArrayPendingState;
|
ArrayPendingState: ArrayPendingState;
|
||||||
ArrayState: ArrayState;
|
ArrayState: ArrayState;
|
||||||
|
ArrayStateInput: ArrayStateInput;
|
||||||
|
ArrayStateInputState: ArrayStateInputState;
|
||||||
Baseboard: ResolverTypeWrapper<Baseboard>;
|
Baseboard: ResolverTypeWrapper<Baseboard>;
|
||||||
Boolean: ResolverTypeWrapper<Scalars['Boolean']['output']>;
|
Boolean: ResolverTypeWrapper<Scalars['Boolean']['output']>;
|
||||||
Capacity: ResolverTypeWrapper<Capacity>;
|
Capacity: ResolverTypeWrapper<Capacity>;
|
||||||
@@ -2057,7 +2081,6 @@ export type ResolversTypes = ResolversObject<{
|
|||||||
WAN_FORWARD_TYPE: WAN_FORWARD_TYPE;
|
WAN_FORWARD_TYPE: WAN_FORWARD_TYPE;
|
||||||
Welcome: ResolverTypeWrapper<Welcome>;
|
Welcome: ResolverTypeWrapper<Welcome>;
|
||||||
addUserInput: addUserInput;
|
addUserInput: addUserInput;
|
||||||
arrayDiskInput: arrayDiskInput;
|
|
||||||
deleteUserInput: deleteUserInput;
|
deleteUserInput: deleteUserInput;
|
||||||
mdState: mdState;
|
mdState: mdState;
|
||||||
registrationType: registrationType;
|
registrationType: registrationType;
|
||||||
@@ -2079,6 +2102,9 @@ export type ResolversParentTypes = ResolversObject<{
|
|||||||
Array: ArrayType;
|
Array: ArrayType;
|
||||||
ArrayCapacity: ArrayCapacity;
|
ArrayCapacity: ArrayCapacity;
|
||||||
ArrayDisk: ArrayDisk;
|
ArrayDisk: ArrayDisk;
|
||||||
|
ArrayDiskInput: ArrayDiskInput;
|
||||||
|
ArrayMutations: ArrayMutations;
|
||||||
|
ArrayStateInput: ArrayStateInput;
|
||||||
Baseboard: Baseboard;
|
Baseboard: Baseboard;
|
||||||
Boolean: Scalars['Boolean']['output'];
|
Boolean: Scalars['Boolean']['output'];
|
||||||
Capacity: Capacity;
|
Capacity: Capacity;
|
||||||
@@ -2165,7 +2191,6 @@ export type ResolversParentTypes = ResolversObject<{
|
|||||||
Vms: Vms;
|
Vms: Vms;
|
||||||
Welcome: Welcome;
|
Welcome: Welcome;
|
||||||
addUserInput: addUserInput;
|
addUserInput: addUserInput;
|
||||||
arrayDiskInput: arrayDiskInput;
|
|
||||||
deleteUserInput: deleteUserInput;
|
deleteUserInput: deleteUserInput;
|
||||||
usersInput: usersInput;
|
usersInput: usersInput;
|
||||||
}>;
|
}>;
|
||||||
@@ -2250,6 +2275,16 @@ export type ArrayDiskResolvers<ContextType = Context, ParentType extends Resolve
|
|||||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
export type ArrayMutationsResolvers<ContextType = Context, ParentType extends ResolversParentTypes['ArrayMutations'] = ResolversParentTypes['ArrayMutations']> = ResolversObject<{
|
||||||
|
addDiskToArray?: Resolver<Maybe<ResolversTypes['Array']>, ParentType, ContextType, Partial<ArrayMutationsaddDiskToArrayArgs>>;
|
||||||
|
clearArrayDiskStatistics?: Resolver<Maybe<ResolversTypes['JSON']>, ParentType, ContextType, RequireFields<ArrayMutationsclearArrayDiskStatisticsArgs, 'id'>>;
|
||||||
|
mountArrayDisk?: Resolver<Maybe<ResolversTypes['Disk']>, ParentType, ContextType, RequireFields<ArrayMutationsmountArrayDiskArgs, 'id'>>;
|
||||||
|
removeDiskFromArray?: Resolver<Maybe<ResolversTypes['Array']>, ParentType, ContextType, Partial<ArrayMutationsremoveDiskFromArrayArgs>>;
|
||||||
|
setState?: Resolver<Maybe<ResolversTypes['Array']>, ParentType, ContextType, Partial<ArrayMutationssetStateArgs>>;
|
||||||
|
unmountArrayDisk?: Resolver<Maybe<ResolversTypes['Disk']>, ParentType, ContextType, RequireFields<ArrayMutationsunmountArrayDiskArgs, 'id'>>;
|
||||||
|
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||||
|
}>;
|
||||||
|
|
||||||
export type BaseboardResolvers<ContextType = Context, ParentType extends ResolversParentTypes['Baseboard'] = ResolversParentTypes['Baseboard']> = ResolversObject<{
|
export type BaseboardResolvers<ContextType = Context, ParentType extends ResolversParentTypes['Baseboard'] = ResolversParentTypes['Baseboard']> = ResolversObject<{
|
||||||
assetTag?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
assetTag?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||||
manufacturer?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
manufacturer?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||||
@@ -2612,7 +2647,6 @@ export type MountResolvers<ContextType = Context, ParentType extends ResolversPa
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type MutationResolvers<ContextType = Context, ParentType extends ResolversParentTypes['Mutation'] = ResolversParentTypes['Mutation']> = ResolversObject<{
|
export type MutationResolvers<ContextType = Context, ParentType extends ResolversParentTypes['Mutation'] = ResolversParentTypes['Mutation']> = ResolversObject<{
|
||||||
addDiskToArray?: Resolver<Maybe<ResolversTypes['Array']>, ParentType, ContextType, Partial<MutationaddDiskToArrayArgs>>;
|
|
||||||
addPermission?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationaddPermissionArgs, 'input'>>;
|
addPermission?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationaddPermissionArgs, 'input'>>;
|
||||||
addRoleForApiKey?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationaddRoleForApiKeyArgs, 'input'>>;
|
addRoleForApiKey?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationaddRoleForApiKeyArgs, 'input'>>;
|
||||||
addRoleForUser?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationaddRoleForUserArgs, 'input'>>;
|
addRoleForUser?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationaddRoleForUserArgs, 'input'>>;
|
||||||
@@ -2620,8 +2654,8 @@ export type MutationResolvers<ContextType = Context, ParentType extends Resolver
|
|||||||
archiveAll?: Resolver<ResolversTypes['NotificationOverview'], ParentType, ContextType, Partial<MutationarchiveAllArgs>>;
|
archiveAll?: Resolver<ResolversTypes['NotificationOverview'], ParentType, ContextType, Partial<MutationarchiveAllArgs>>;
|
||||||
archiveNotification?: Resolver<ResolversTypes['Notification'], ParentType, ContextType, RequireFields<MutationarchiveNotificationArgs, 'id'>>;
|
archiveNotification?: Resolver<ResolversTypes['Notification'], ParentType, ContextType, RequireFields<MutationarchiveNotificationArgs, 'id'>>;
|
||||||
archiveNotifications?: Resolver<ResolversTypes['NotificationOverview'], ParentType, ContextType, Partial<MutationarchiveNotificationsArgs>>;
|
archiveNotifications?: Resolver<ResolversTypes['NotificationOverview'], ParentType, ContextType, Partial<MutationarchiveNotificationsArgs>>;
|
||||||
|
array?: Resolver<Maybe<ResolversTypes['ArrayMutations']>, ParentType, ContextType>;
|
||||||
cancelParityCheck?: Resolver<Maybe<ResolversTypes['JSON']>, ParentType, ContextType>;
|
cancelParityCheck?: Resolver<Maybe<ResolversTypes['JSON']>, ParentType, ContextType>;
|
||||||
clearArrayDiskStatistics?: Resolver<Maybe<ResolversTypes['JSON']>, ParentType, ContextType, RequireFields<MutationclearArrayDiskStatisticsArgs, 'id'>>;
|
|
||||||
connectSignIn?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationconnectSignInArgs, 'input'>>;
|
connectSignIn?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationconnectSignInArgs, 'input'>>;
|
||||||
connectSignOut?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
|
connectSignOut?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
|
||||||
createApiKey?: Resolver<ResolversTypes['ApiKeyWithSecret'], ParentType, ContextType, RequireFields<MutationcreateApiKeyArgs, 'input'>>;
|
createApiKey?: Resolver<ResolversTypes['ApiKeyWithSecret'], ParentType, ContextType, RequireFields<MutationcreateApiKeyArgs, 'input'>>;
|
||||||
@@ -2631,22 +2665,17 @@ export type MutationResolvers<ContextType = Context, ParentType extends Resolver
|
|||||||
deleteUser?: Resolver<Maybe<ResolversTypes['User']>, ParentType, ContextType, RequireFields<MutationdeleteUserArgs, 'input'>>;
|
deleteUser?: Resolver<Maybe<ResolversTypes['User']>, ParentType, ContextType, RequireFields<MutationdeleteUserArgs, 'input'>>;
|
||||||
enableDynamicRemoteAccess?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationenableDynamicRemoteAccessArgs, 'input'>>;
|
enableDynamicRemoteAccess?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationenableDynamicRemoteAccessArgs, 'input'>>;
|
||||||
login?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType, RequireFields<MutationloginArgs, 'password' | 'username'>>;
|
login?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType, RequireFields<MutationloginArgs, 'password' | 'username'>>;
|
||||||
mountArrayDisk?: Resolver<Maybe<ResolversTypes['Disk']>, ParentType, ContextType, RequireFields<MutationmountArrayDiskArgs, 'id'>>;
|
|
||||||
pauseParityCheck?: Resolver<Maybe<ResolversTypes['JSON']>, ParentType, ContextType>;
|
pauseParityCheck?: Resolver<Maybe<ResolversTypes['JSON']>, ParentType, ContextType>;
|
||||||
reboot?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
reboot?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||||
recalculateOverview?: Resolver<ResolversTypes['NotificationOverview'], ParentType, ContextType>;
|
recalculateOverview?: Resolver<ResolversTypes['NotificationOverview'], ParentType, ContextType>;
|
||||||
removeDiskFromArray?: Resolver<Maybe<ResolversTypes['Array']>, ParentType, ContextType, Partial<MutationremoveDiskFromArrayArgs>>;
|
|
||||||
removeRoleFromApiKey?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationremoveRoleFromApiKeyArgs, 'input'>>;
|
removeRoleFromApiKey?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationremoveRoleFromApiKeyArgs, 'input'>>;
|
||||||
resumeParityCheck?: Resolver<Maybe<ResolversTypes['JSON']>, ParentType, ContextType>;
|
resumeParityCheck?: Resolver<Maybe<ResolversTypes['JSON']>, ParentType, ContextType>;
|
||||||
setAdditionalAllowedOrigins?: Resolver<Array<ResolversTypes['String']>, ParentType, ContextType, RequireFields<MutationsetAdditionalAllowedOriginsArgs, 'input'>>;
|
setAdditionalAllowedOrigins?: Resolver<Array<ResolversTypes['String']>, ParentType, ContextType, RequireFields<MutationsetAdditionalAllowedOriginsArgs, 'input'>>;
|
||||||
setupRemoteAccess?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationsetupRemoteAccessArgs, 'input'>>;
|
setupRemoteAccess?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationsetupRemoteAccessArgs, 'input'>>;
|
||||||
shutdown?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
shutdown?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||||
startArray?: Resolver<Maybe<ResolversTypes['Array']>, ParentType, ContextType>;
|
|
||||||
startParityCheck?: Resolver<Maybe<ResolversTypes['JSON']>, ParentType, ContextType, Partial<MutationstartParityCheckArgs>>;
|
startParityCheck?: Resolver<Maybe<ResolversTypes['JSON']>, ParentType, ContextType, Partial<MutationstartParityCheckArgs>>;
|
||||||
stopArray?: Resolver<Maybe<ResolversTypes['Array']>, ParentType, ContextType>;
|
|
||||||
unarchiveAll?: Resolver<ResolversTypes['NotificationOverview'], ParentType, ContextType, Partial<MutationunarchiveAllArgs>>;
|
unarchiveAll?: Resolver<ResolversTypes['NotificationOverview'], ParentType, ContextType, Partial<MutationunarchiveAllArgs>>;
|
||||||
unarchiveNotifications?: Resolver<ResolversTypes['NotificationOverview'], ParentType, ContextType, Partial<MutationunarchiveNotificationsArgs>>;
|
unarchiveNotifications?: Resolver<ResolversTypes['NotificationOverview'], ParentType, ContextType, Partial<MutationunarchiveNotificationsArgs>>;
|
||||||
unmountArrayDisk?: Resolver<Maybe<ResolversTypes['Disk']>, ParentType, ContextType, RequireFields<MutationunmountArrayDiskArgs, 'id'>>;
|
|
||||||
unreadNotification?: Resolver<ResolversTypes['Notification'], ParentType, ContextType, RequireFields<MutationunreadNotificationArgs, 'id'>>;
|
unreadNotification?: Resolver<ResolversTypes['Notification'], ParentType, ContextType, RequireFields<MutationunreadNotificationArgs, 'id'>>;
|
||||||
updateApiSettings?: Resolver<ResolversTypes['ConnectSettingsValues'], ParentType, ContextType, RequireFields<MutationupdateApiSettingsArgs, 'input'>>;
|
updateApiSettings?: Resolver<ResolversTypes['ConnectSettingsValues'], ParentType, ContextType, RequireFields<MutationupdateApiSettingsArgs, 'input'>>;
|
||||||
}>;
|
}>;
|
||||||
@@ -3273,6 +3302,7 @@ export type Resolvers<ContextType = Context> = ResolversObject<{
|
|||||||
Array?: ArrayResolvers<ContextType>;
|
Array?: ArrayResolvers<ContextType>;
|
||||||
ArrayCapacity?: ArrayCapacityResolvers<ContextType>;
|
ArrayCapacity?: ArrayCapacityResolvers<ContextType>;
|
||||||
ArrayDisk?: ArrayDiskResolvers<ContextType>;
|
ArrayDisk?: ArrayDiskResolvers<ContextType>;
|
||||||
|
ArrayMutations?: ArrayMutationsResolvers<ContextType>;
|
||||||
Baseboard?: BaseboardResolvers<ContextType>;
|
Baseboard?: BaseboardResolvers<ContextType>;
|
||||||
Capacity?: CapacityResolvers<ContextType>;
|
Capacity?: CapacityResolvers<ContextType>;
|
||||||
Case?: CaseResolvers<ContextType>;
|
Case?: CaseResolvers<ContextType>;
|
||||||
|
|||||||
@@ -3,16 +3,26 @@ type Query {
|
|||||||
array: Array!
|
array: Array!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
enum ArrayStateInputState {
|
||||||
"""Start array"""
|
"""Start array"""
|
||||||
startArray: Array
|
START
|
||||||
"""Stop array"""
|
"""Stop array"""
|
||||||
stopArray: Array
|
STOP
|
||||||
|
}
|
||||||
|
|
||||||
|
input ArrayStateInput {
|
||||||
|
"""Array state"""
|
||||||
|
desiredState: ArrayStateInputState!
|
||||||
|
}
|
||||||
|
|
||||||
|
type ArrayMutations {
|
||||||
|
"""Set array state"""
|
||||||
|
setState(input: ArrayStateInput): Array
|
||||||
|
|
||||||
"""Add new disk to array"""
|
"""Add new disk to array"""
|
||||||
addDiskToArray(input: arrayDiskInput): Array
|
addDiskToArray(input: ArrayDiskInput): Array
|
||||||
"""Remove existing disk from array. NOTE: The array must be stopped before running this otherwise it'll throw an error."""
|
"""Remove existing disk from array. NOTE: The array must be stopped before running this otherwise it'll throw an error."""
|
||||||
removeDiskFromArray(input: arrayDiskInput): Array
|
removeDiskFromArray(input: ArrayDiskInput): Array
|
||||||
|
|
||||||
mountArrayDisk(id: ID!): Disk
|
mountArrayDisk(id: ID!): Disk
|
||||||
unmountArrayDisk(id: ID!): Disk
|
unmountArrayDisk(id: ID!): Disk
|
||||||
@@ -20,11 +30,15 @@ type Mutation {
|
|||||||
clearArrayDiskStatistics(id: ID!): JSON
|
clearArrayDiskStatistics(id: ID!): JSON
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Mutation {
|
||||||
|
array: ArrayMutations
|
||||||
|
}
|
||||||
|
|
||||||
type Subscription {
|
type Subscription {
|
||||||
array: Array!
|
array: Array!
|
||||||
}
|
}
|
||||||
|
|
||||||
input arrayDiskInput {
|
input ArrayDiskInput {
|
||||||
"""Disk ID"""
|
"""Disk ID"""
|
||||||
id: ID!
|
id: ID!
|
||||||
"""The slot for the disk"""
|
"""The slot for the disk"""
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { Args, ResolveField, Resolver } from '@nestjs/graphql';
|
||||||
|
|
||||||
|
import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz';
|
||||||
|
|
||||||
|
import type { ArrayDiskInput, ArrayStateInput } from '@app/graphql/generated/api/types.js';
|
||||||
|
import { Resource } from '@app/graphql/generated/api/types.js';
|
||||||
|
import { ArrayService } from '@app/unraid-api/graph/resolvers/array/array.service.js';
|
||||||
|
|
||||||
|
@Resolver('ArrayMutations')
|
||||||
|
export class ArrayMutationsResolver {
|
||||||
|
constructor(private readonly arrayService: ArrayService) {}
|
||||||
|
|
||||||
|
@ResolveField('setState')
|
||||||
|
@UsePermissions({
|
||||||
|
action: AuthActionVerb.UPDATE,
|
||||||
|
resource: Resource.ARRAY,
|
||||||
|
possession: AuthPossession.ANY,
|
||||||
|
})
|
||||||
|
public async setState(@Args('input') input: ArrayStateInput) {
|
||||||
|
return this.arrayService.updateArrayState(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ResolveField('addDiskToArray')
|
||||||
|
@UsePermissions({
|
||||||
|
action: AuthActionVerb.UPDATE,
|
||||||
|
resource: Resource.ARRAY,
|
||||||
|
possession: AuthPossession.ANY,
|
||||||
|
})
|
||||||
|
public async addDiskToArray(@Args('input') input: ArrayDiskInput) {
|
||||||
|
return this.arrayService.addDiskToArray(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ResolveField('removeDiskFromArray')
|
||||||
|
@UsePermissions({
|
||||||
|
action: AuthActionVerb.UPDATE,
|
||||||
|
resource: Resource.ARRAY,
|
||||||
|
possession: AuthPossession.ANY,
|
||||||
|
})
|
||||||
|
public async removeDiskFromArray(@Args('input') input: ArrayDiskInput) {
|
||||||
|
return this.arrayService.removeDiskFromArray(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ResolveField('mountArrayDisk')
|
||||||
|
@UsePermissions({
|
||||||
|
action: AuthActionVerb.UPDATE,
|
||||||
|
resource: Resource.ARRAY,
|
||||||
|
possession: AuthPossession.ANY,
|
||||||
|
})
|
||||||
|
public async mountArrayDisk(@Args('id') id: string) {
|
||||||
|
return this.arrayService.mountArrayDisk(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ResolveField('unmountArrayDisk')
|
||||||
|
@UsePermissions({
|
||||||
|
action: AuthActionVerb.UPDATE,
|
||||||
|
resource: Resource.ARRAY,
|
||||||
|
possession: AuthPossession.ANY,
|
||||||
|
})
|
||||||
|
public async unmountArrayDisk(@Args('id') id: string) {
|
||||||
|
return this.arrayService.unmountArrayDisk(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ResolveField('clearArrayDiskStatistics')
|
||||||
|
@UsePermissions({
|
||||||
|
action: AuthActionVerb.UPDATE,
|
||||||
|
resource: Resource.ARRAY,
|
||||||
|
possession: AuthPossession.ANY,
|
||||||
|
})
|
||||||
|
public async clearArrayDiskStatistics(@Args('id') id: string) {
|
||||||
|
return this.arrayService.clearArrayDiskStatistics(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,35 @@
|
|||||||
import type { TestingModule } from '@nestjs/testing';
|
import type { TestingModule } from '@nestjs/testing';
|
||||||
import { Test } from '@nestjs/testing';
|
import { Test } from '@nestjs/testing';
|
||||||
|
|
||||||
import { beforeEach, describe, expect, it } from 'vitest';
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
import { ArrayResolver } from '@app/unraid-api/graph/resolvers/array/array.resolver.js';
|
import { ArrayResolver } from '@app/unraid-api/graph/resolvers/array/array.resolver.js';
|
||||||
|
import { ArrayService } from '@app/unraid-api/graph/resolvers/array/array.service.js';
|
||||||
|
|
||||||
describe('ArrayResolver', () => {
|
describe('ArrayResolver', () => {
|
||||||
let resolver: ArrayResolver;
|
let resolver: ArrayResolver;
|
||||||
|
let arrayService: ArrayService;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [ArrayResolver],
|
providers: [
|
||||||
|
ArrayResolver,
|
||||||
|
{
|
||||||
|
provide: ArrayService,
|
||||||
|
useValue: {
|
||||||
|
updateArrayState: vi.fn(),
|
||||||
|
addDiskToArray: vi.fn(),
|
||||||
|
removeDiskFromArray: vi.fn(),
|
||||||
|
mountArrayDisk: vi.fn(),
|
||||||
|
unmountArrayDisk: vi.fn(),
|
||||||
|
clearArrayDiskStatistics: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
resolver = module.get<ArrayResolver>(ArrayResolver);
|
resolver = module.get<ArrayResolver>(ArrayResolver);
|
||||||
|
arrayService = module.get<ArrayService>(ArrayService);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be defined', () => {
|
it('should be defined', () => {
|
||||||
|
|||||||
@@ -6,9 +6,12 @@ import { getArrayData } from '@app/core/modules/array/get-array-data.js';
|
|||||||
import { createSubscription, PUBSUB_CHANNEL } from '@app/core/pubsub.js';
|
import { createSubscription, PUBSUB_CHANNEL } from '@app/core/pubsub.js';
|
||||||
import { Resource } from '@app/graphql/generated/api/types.js';
|
import { Resource } from '@app/graphql/generated/api/types.js';
|
||||||
import { store } from '@app/store/index.js';
|
import { store } from '@app/store/index.js';
|
||||||
|
import { ArrayService } from '@app/unraid-api/graph/resolvers/array/array.service.js';
|
||||||
|
|
||||||
@Resolver('Array')
|
@Resolver('Array')
|
||||||
export class ArrayResolver {
|
export class ArrayResolver {
|
||||||
|
constructor(private readonly arrayService: ArrayService) {}
|
||||||
|
|
||||||
@Query()
|
@Query()
|
||||||
@UsePermissions({
|
@UsePermissions({
|
||||||
action: AuthActionVerb.READ,
|
action: AuthActionVerb.READ,
|
||||||
|
|||||||
210
api/src/unraid-api/graph/resolvers/array/array.service.spec.ts
Normal file
210
api/src/unraid-api/graph/resolvers/array/array.service.spec.ts
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
import type { TestingModule } from '@nestjs/testing';
|
||||||
|
import { Test } from '@nestjs/testing';
|
||||||
|
|
||||||
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
import type { ArrayDiskInput, ArrayStateInput } from '@app/graphql/generated/api/types.js';
|
||||||
|
import { getArrayData } from '@app/core/modules/array/get-array-data.js';
|
||||||
|
import { emcmd } from '@app/core/utils/clients/emcmd.js';
|
||||||
|
import { ArrayState, ArrayStateInputState } from '@app/graphql/generated/api/types.js';
|
||||||
|
import { getters } from '@app/store/index.js';
|
||||||
|
import { ArrayService } from '@app/unraid-api/graph/resolvers/array/array.service.js';
|
||||||
|
|
||||||
|
vi.mock('@app/core/utils/clients/emcmd.js', () => ({
|
||||||
|
emcmd: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@app/store/index.js', () => ({
|
||||||
|
getters: {
|
||||||
|
emhttp: vi.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@app/core/modules/array/get-array-data.js', () => ({
|
||||||
|
getArrayData: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('ArrayService', () => {
|
||||||
|
let service: ArrayService;
|
||||||
|
let mockArrayData: any;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [ArrayService],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
service = module.get<ArrayService>(ArrayService);
|
||||||
|
|
||||||
|
// Mock getters.emhttp()
|
||||||
|
vi.mocked(getters.emhttp).mockReturnValue({
|
||||||
|
var: {
|
||||||
|
mdState: ArrayState.STOPPED,
|
||||||
|
},
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
// Mock getArrayData
|
||||||
|
mockArrayData = {
|
||||||
|
id: 'array',
|
||||||
|
state: ArrayState.STOPPED,
|
||||||
|
capacity: {
|
||||||
|
kilobytes: {
|
||||||
|
free: '1000',
|
||||||
|
used: '1000',
|
||||||
|
total: '2000',
|
||||||
|
},
|
||||||
|
disks: {
|
||||||
|
free: '10',
|
||||||
|
used: '5',
|
||||||
|
total: '15',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
boot: null,
|
||||||
|
parities: [],
|
||||||
|
disks: [],
|
||||||
|
caches: [],
|
||||||
|
};
|
||||||
|
vi.mocked(getArrayData).mockReturnValue(mockArrayData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(service).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update array state', async () => {
|
||||||
|
const input: ArrayStateInput = {
|
||||||
|
desiredState: ArrayStateInputState.START,
|
||||||
|
};
|
||||||
|
const result = await service.updateArrayState(input);
|
||||||
|
expect(result).toEqual(mockArrayData);
|
||||||
|
expect(emcmd).toHaveBeenCalledWith({
|
||||||
|
cmdStart: 'Start',
|
||||||
|
startState: 'STOPPED',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add disk to array', async () => {
|
||||||
|
const input: ArrayDiskInput = {
|
||||||
|
id: 'test-disk',
|
||||||
|
slot: 1,
|
||||||
|
};
|
||||||
|
const result = await service.addDiskToArray(input);
|
||||||
|
expect(result).toEqual(mockArrayData);
|
||||||
|
expect(emcmd).toHaveBeenCalledWith({
|
||||||
|
changeDevice: 'apply',
|
||||||
|
'slotId.1': 'test-disk',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove disk from array', async () => {
|
||||||
|
const input: ArrayDiskInput = {
|
||||||
|
id: 'test-disk',
|
||||||
|
slot: 1,
|
||||||
|
};
|
||||||
|
const result = await service.removeDiskFromArray(input);
|
||||||
|
expect(result).toEqual(mockArrayData);
|
||||||
|
expect(emcmd).toHaveBeenCalledWith({
|
||||||
|
changeDevice: 'apply',
|
||||||
|
'slotId.1': '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should mount array disk', async () => {
|
||||||
|
// Mock array as running
|
||||||
|
vi.mocked(getters.emhttp).mockReturnValue({
|
||||||
|
var: {
|
||||||
|
mdState: ArrayState.STARTED,
|
||||||
|
},
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
const result = await service.mountArrayDisk('test-disk');
|
||||||
|
expect(result).toEqual(mockArrayData);
|
||||||
|
expect(emcmd).toHaveBeenCalledWith({
|
||||||
|
mount: 'apply',
|
||||||
|
'diskId.test-disk': '1',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should unmount array disk', async () => {
|
||||||
|
// Mock array as running
|
||||||
|
vi.mocked(getters.emhttp).mockReturnValue({
|
||||||
|
var: {
|
||||||
|
mdState: ArrayState.STARTED,
|
||||||
|
},
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
const result = await service.unmountArrayDisk('test-disk');
|
||||||
|
expect(result).toEqual(mockArrayData);
|
||||||
|
expect(emcmd).toHaveBeenCalledWith({
|
||||||
|
unmount: 'apply',
|
||||||
|
'diskId.test-disk': '1',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear array disk statistics', async () => {
|
||||||
|
// Mock array as running
|
||||||
|
vi.mocked(getters.emhttp).mockReturnValue({
|
||||||
|
var: {
|
||||||
|
mdState: ArrayState.STARTED,
|
||||||
|
},
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
const result = await service.clearArrayDiskStatistics('test-disk');
|
||||||
|
expect(result).toEqual(mockArrayData);
|
||||||
|
expect(emcmd).toHaveBeenCalledWith({
|
||||||
|
clearStats: 'apply',
|
||||||
|
'diskId.test-disk': '1',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error when array is running for add disk', async () => {
|
||||||
|
// Mock array as running
|
||||||
|
vi.mocked(getters.emhttp).mockReturnValue({
|
||||||
|
var: {
|
||||||
|
mdState: ArrayState.STARTED,
|
||||||
|
},
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
const input: ArrayDiskInput = {
|
||||||
|
id: 'test-disk',
|
||||||
|
slot: 1,
|
||||||
|
};
|
||||||
|
await expect(service.addDiskToArray(input)).rejects.toThrow(
|
||||||
|
'Array needs to be stopped before any changes can occur.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error when array is running for remove disk', async () => {
|
||||||
|
// Mock array as running
|
||||||
|
vi.mocked(getters.emhttp).mockReturnValue({
|
||||||
|
var: {
|
||||||
|
mdState: ArrayState.STARTED,
|
||||||
|
},
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
const input: ArrayDiskInput = {
|
||||||
|
id: 'test-disk',
|
||||||
|
slot: 1,
|
||||||
|
};
|
||||||
|
await expect(service.removeDiskFromArray(input)).rejects.toThrow(
|
||||||
|
'Array needs to be stopped before any changes can occur.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error when array is not running for mount disk', async () => {
|
||||||
|
await expect(service.mountArrayDisk('test-disk')).rejects.toThrow(
|
||||||
|
'Array must be running to mount disks'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error when array is not running for unmount disk', async () => {
|
||||||
|
await expect(service.unmountArrayDisk('test-disk')).rejects.toThrow(
|
||||||
|
'Array must be running to unmount disks'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error when array is not running for clear disk statistics', async () => {
|
||||||
|
await expect(service.clearArrayDiskStatistics('test-disk')).rejects.toThrow(
|
||||||
|
'Array must be running to clear disk statistics'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
145
api/src/unraid-api/graph/resolvers/array/array.service.ts
Normal file
145
api/src/unraid-api/graph/resolvers/array/array.service.ts
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { capitalCase, constantCase } from 'change-case';
|
||||||
|
|
||||||
|
import type { ArrayDiskInput, ArrayStateInput, ArrayType } from '@app/graphql/generated/api/types.js';
|
||||||
|
import { AppError } from '@app/core/errors/app-error.js';
|
||||||
|
import { ArrayRunningError } from '@app/core/errors/array-running-error.js';
|
||||||
|
import { getArrayData } from '@app/core/modules/array/get-array-data.js';
|
||||||
|
import { emcmd } from '@app/core/utils/clients/emcmd.js';
|
||||||
|
import { arrayIsRunning as arrayIsRunningUtil } from '@app/core/utils/index.js';
|
||||||
|
import {
|
||||||
|
ArrayPendingState,
|
||||||
|
ArrayState,
|
||||||
|
ArrayStateInputState,
|
||||||
|
} from '@app/graphql/generated/api/types.js';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ArrayService {
|
||||||
|
private pendingState: ArrayPendingState | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the array running?
|
||||||
|
* @todo Refactor this to include this util in the service directly
|
||||||
|
*/
|
||||||
|
private arrayIsRunning() {
|
||||||
|
return arrayIsRunningUtil();
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateArrayState({ desiredState }: ArrayStateInput): Promise<ArrayType> {
|
||||||
|
const startState = this.arrayIsRunning() ? ArrayState.STARTED : ArrayState.STOPPED;
|
||||||
|
const pendingState =
|
||||||
|
desiredState === ArrayStateInputState.STOP
|
||||||
|
? ArrayPendingState.STOPPING
|
||||||
|
: ArrayPendingState.STARTING;
|
||||||
|
|
||||||
|
// Prevent this running multiple times at once
|
||||||
|
if (this.pendingState) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
new AppError(`Array state is still being updated. Changing to ${pendingState}`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent starting/stopping array when it's already in the same state
|
||||||
|
if (
|
||||||
|
(this.arrayIsRunning() && desiredState === ArrayStateInputState.START) ||
|
||||||
|
(!this.arrayIsRunning() && desiredState === ArrayStateInputState.STOP)
|
||||||
|
) {
|
||||||
|
throw new BadRequestException(new AppError(`The array is already ${startState}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set lock then start/stop array
|
||||||
|
this.pendingState = pendingState;
|
||||||
|
const command = {
|
||||||
|
[`cmd${capitalCase(desiredState)}`]: capitalCase(desiredState),
|
||||||
|
startState: constantCase(startState),
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await emcmd(command);
|
||||||
|
} finally {
|
||||||
|
this.pendingState = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get new array JSON
|
||||||
|
const array = getArrayData();
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
async addDiskToArray(input: ArrayDiskInput): Promise<ArrayType> {
|
||||||
|
if (this.arrayIsRunning()) {
|
||||||
|
throw new ArrayRunningError();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id: diskId, slot: preferredSlot } = input;
|
||||||
|
const slot = preferredSlot?.toString() ?? '';
|
||||||
|
|
||||||
|
// Add disk
|
||||||
|
await emcmd({
|
||||||
|
changeDevice: 'apply',
|
||||||
|
[`slotId.${slot}`]: diskId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return getArrayData();
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeDiskFromArray(input: ArrayDiskInput): Promise<ArrayType> {
|
||||||
|
if (this.arrayIsRunning()) {
|
||||||
|
throw new ArrayRunningError();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { slot } = input;
|
||||||
|
const slotStr = slot?.toString() ?? '';
|
||||||
|
|
||||||
|
// Remove disk
|
||||||
|
await emcmd({
|
||||||
|
changeDevice: 'apply',
|
||||||
|
[`slotId.${slotStr}`]: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
return getArrayData();
|
||||||
|
}
|
||||||
|
|
||||||
|
async mountArrayDisk(id: string): Promise<ArrayType> {
|
||||||
|
if (!this.arrayIsRunning()) {
|
||||||
|
throw new BadRequestException('Array must be running to mount disks');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount disk
|
||||||
|
await emcmd({
|
||||||
|
mount: 'apply',
|
||||||
|
[`diskId.${id}`]: '1',
|
||||||
|
});
|
||||||
|
|
||||||
|
return getArrayData();
|
||||||
|
}
|
||||||
|
|
||||||
|
async unmountArrayDisk(id: string): Promise<ArrayType> {
|
||||||
|
if (!this.arrayIsRunning()) {
|
||||||
|
throw new BadRequestException('Array must be running to unmount disks');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmount disk
|
||||||
|
await emcmd({
|
||||||
|
unmount: 'apply',
|
||||||
|
[`diskId.${id}`]: '1',
|
||||||
|
});
|
||||||
|
|
||||||
|
return getArrayData();
|
||||||
|
}
|
||||||
|
|
||||||
|
async clearArrayDiskStatistics(id: string): Promise<ArrayType> {
|
||||||
|
if (!this.arrayIsRunning()) {
|
||||||
|
throw new BadRequestException('Array must be running to clear disk statistics');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear disk statistics
|
||||||
|
await emcmd({
|
||||||
|
clearStats: 'apply',
|
||||||
|
[`diskId.${id}`]: '1',
|
||||||
|
});
|
||||||
|
|
||||||
|
return getArrayData();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { ResolveField, Resolver } from '@nestjs/graphql';
|
||||||
|
|
||||||
|
@Resolver('Mutation')
|
||||||
|
export class MutationResolver {
|
||||||
|
@ResolveField()
|
||||||
|
public async array() {
|
||||||
|
return {
|
||||||
|
__typename: 'ArrayMutations',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,9 @@ import { Module } from '@nestjs/common';
|
|||||||
import { AuthModule } from '@app/unraid-api/auth/auth.module.js';
|
import { AuthModule } from '@app/unraid-api/auth/auth.module.js';
|
||||||
import { ConnectSettingsService } from '@app/unraid-api/graph/connect/connect-settings.service.js';
|
import { ConnectSettingsService } from '@app/unraid-api/graph/connect/connect-settings.service.js';
|
||||||
import { ApiKeyResolver } from '@app/unraid-api/graph/resolvers/api-key/api-key.resolver.js';
|
import { ApiKeyResolver } from '@app/unraid-api/graph/resolvers/api-key/api-key.resolver.js';
|
||||||
|
import { ArrayMutationsResolver } from '@app/unraid-api/graph/resolvers/array/array.mutations.resolver.js';
|
||||||
import { ArrayResolver } from '@app/unraid-api/graph/resolvers/array/array.resolver.js';
|
import { ArrayResolver } from '@app/unraid-api/graph/resolvers/array/array.resolver.js';
|
||||||
|
import { ArrayService } from '@app/unraid-api/graph/resolvers/array/array.service.js';
|
||||||
import { CloudResolver } from '@app/unraid-api/graph/resolvers/cloud/cloud.resolver.js';
|
import { CloudResolver } from '@app/unraid-api/graph/resolvers/cloud/cloud.resolver.js';
|
||||||
import { ConfigResolver } from '@app/unraid-api/graph/resolvers/config/config.resolver.js';
|
import { ConfigResolver } from '@app/unraid-api/graph/resolvers/config/config.resolver.js';
|
||||||
import { DisksResolver } from '@app/unraid-api/graph/resolvers/disks/disks.resolver.js';
|
import { DisksResolver } from '@app/unraid-api/graph/resolvers/disks/disks.resolver.js';
|
||||||
@@ -14,6 +16,7 @@ import { InfoResolver } from '@app/unraid-api/graph/resolvers/info/info.resolver
|
|||||||
import { LogsResolver } from '@app/unraid-api/graph/resolvers/logs/logs.resolver.js';
|
import { LogsResolver } from '@app/unraid-api/graph/resolvers/logs/logs.resolver.js';
|
||||||
import { LogsService } from '@app/unraid-api/graph/resolvers/logs/logs.service.js';
|
import { LogsService } from '@app/unraid-api/graph/resolvers/logs/logs.service.js';
|
||||||
import { MeResolver } from '@app/unraid-api/graph/resolvers/me/me.resolver.js';
|
import { MeResolver } from '@app/unraid-api/graph/resolvers/me/me.resolver.js';
|
||||||
|
import { MutationResolver } from '@app/unraid-api/graph/resolvers/mutation/mutation.resolver.js';
|
||||||
import { NotificationsResolver } from '@app/unraid-api/graph/resolvers/notifications/notifications.resolver.js';
|
import { NotificationsResolver } from '@app/unraid-api/graph/resolvers/notifications/notifications.resolver.js';
|
||||||
import { NotificationsService } from '@app/unraid-api/graph/resolvers/notifications/notifications.service.js';
|
import { NotificationsService } from '@app/unraid-api/graph/resolvers/notifications/notifications.service.js';
|
||||||
import { OnlineResolver } from '@app/unraid-api/graph/resolvers/online/online.resolver.js';
|
import { OnlineResolver } from '@app/unraid-api/graph/resolvers/online/online.resolver.js';
|
||||||
@@ -27,6 +30,8 @@ import { VmsResolver } from '@app/unraid-api/graph/resolvers/vms/vms.resolver.js
|
|||||||
imports: [AuthModule],
|
imports: [AuthModule],
|
||||||
providers: [
|
providers: [
|
||||||
ArrayResolver,
|
ArrayResolver,
|
||||||
|
ArrayMutationsResolver,
|
||||||
|
ArrayService,
|
||||||
ApiKeyResolver,
|
ApiKeyResolver,
|
||||||
CloudResolver,
|
CloudResolver,
|
||||||
ConfigResolver,
|
ConfigResolver,
|
||||||
@@ -34,6 +39,7 @@ import { VmsResolver } from '@app/unraid-api/graph/resolvers/vms/vms.resolver.js
|
|||||||
DisplayResolver,
|
DisplayResolver,
|
||||||
DockerResolver,
|
DockerResolver,
|
||||||
FlashResolver,
|
FlashResolver,
|
||||||
|
MutationResolver,
|
||||||
InfoResolver,
|
InfoResolver,
|
||||||
NotificationsResolver,
|
NotificationsResolver,
|
||||||
OnlineResolver,
|
OnlineResolver,
|
||||||
|
|||||||
Reference in New Issue
Block a user