Начал рефакторинг запросов к апи.

Выношу в клиент
jwt
Rinsvent 2 years ago
parent 23ca544247
commit f1de8a69f9
  1. 4
      api/client.ts
  2. 63
      api/schema-client.ts
  3. 9
      api/sm/requests/get-processes.ts
  4. 4
      api/sm/requests/pagination.ts
  5. 28
      api/sm/responses/comamnds.ts
  6. 32
      api/sm/responses/processes.ts
  7. 4
      api/sm/responses/response.ts
  8. 10
      api/sm/schemas/commands.ts
  9. 10
      api/sm/schemas/processes.ts
  10. 10
      api/sm/schemas/run-commands.ts
  11. 46
      api/sm/sm-client.ts
  12. 23
      components/elements/commands/index.tsx
  13. 72
      components/elements/processes/index.tsx

@ -1,4 +0,0 @@
export class Client {
}

@ -0,0 +1,63 @@
export enum Method {
GET = 'GET',
POST = 'POST',
PUT = 'PUT',
PATCH = 'PATCH',
}
export let hasQuery = (method: Method) => {
return Method.GET === method
}
export interface ClientOptions {
baseUrl: string
}
export interface SchemaInterface {
url: string,
method: Method
contentType: string | null
}
export class SchemaClient {
baseUrl: string
constructor({baseUrl}: ClientOptions) {
this.baseUrl = baseUrl
}
async send(schema: SchemaInterface, data: any) {
let url = `${this.baseUrl}${schema.url}`
let {url: preparedUrl, data: preparedData} = this.processAttributes(url, data)
if (hasQuery(schema.method)) {
preparedUrl += '?' + new URLSearchParams(preparedData)
}
let response = await fetch(preparedUrl, {
method: schema.method.toString(),
headers: {
...(schema.contentType ? {'Content-Type': schema.contentType} : {}),
'X-Plugin-Token': 'passw0rd'
},
body: hasQuery(schema.method) ? null : JSON.stringify(preparedData)
});
let responseData = response.headers.get('Content-Type')?.toString().includes('application/json')
? await response.json()
: await response.text()
return {responseData: responseData, headers: response.headers}
}
private processAttributes(url: string, data: any)
{
const preparedData = data
for (const key in data) {
if (url.indexOf('{' + key + '}') > -1) {
url = url.replace('{' + key + '}', preparedData[key])
delete preparedData[key]
}
}
return {url, data: preparedData }
}
}

@ -0,0 +1,9 @@
export enum ProcessesType
{
RUNNING = 'running',
HISTORY = 'history',
}
export class GetProcessesRequest {
type?: ProcessesType
}

@ -0,0 +1,4 @@
export class PaginationRequest {
page?: number
limit?: number
}

@ -0,0 +1,28 @@
export interface OptionInterface {
name: string,
description: string | null,
default: any,
value: any,
shortcut: string | null,
isArray: boolean,
isNegatable: boolean,
isValueOptional: boolean,
isValueRequired: boolean
acceptValue: boolean
}
export interface ArgumentInterface {
name: string,
description: string|null,
default: any,
isArray: boolean,
isRequired: boolean,
}
export interface CommandResponseInterface {
class: string,
name: string,
description: string,
options: OptionInterface[],
arguments: ArgumentInterface[],
}

@ -0,0 +1,32 @@
export interface ProcessExceptionInterface {
message: string,
file: string,
line: number,
code: number,
trace: any[],
}
export interface ProcessProgressInterface {
total: number,
progress: number,
memory: any[],
}
export interface ProcessesResponseInterface {
id: string
lock: string | null
containerUuid: string | null
pid: bigint | null
name: string
options: Record<string, any>
arguments: Record<string, any>
exception: ProcessExceptionInterface | null
progress: ProcessProgressInterface | null
outputId: string | null
createdAt: string
updatedAt: string | null
startedAt: string | null
pausedAt: string | null
cancelledAt: string | null
completedAt: string | null
}

@ -0,0 +1,4 @@
export interface ResponseInterface {
data: any
headers: Headers
}

@ -0,0 +1,10 @@
import {Method, SchemaInterface} from "../../schema-client";
class CommandsSchema implements SchemaInterface {
method = Method.GET
url = '/system-monitoring/commands'
contentType = null;
}
export let commandsSchema = new CommandsSchema()
export default commandsSchema

@ -0,0 +1,10 @@
import {Method, SchemaInterface} from "../../schema-client";
class ProcessesSchema implements SchemaInterface {
method = Method.GET
url = '/system-monitoring/processes'
contentType = null;
}
export let processesSchema = new ProcessesSchema()
export default processesSchema

@ -0,0 +1,10 @@
import {Method, SchemaInterface} from "../../schema-client";
class RunCommandsSchema implements SchemaInterface {
method = Method.POST
url = '/system-monitoring/commands/{commandName}/run'
contentType = 'application/json;charset=utf-8';
}
export let runCommandsSchema = new RunCommandsSchema()
export default runCommandsSchema

@ -0,0 +1,46 @@
import {processesSchema} from "./schemas/processes";
import {ProcessesResponseInterface} from "./responses/processes";
import {SchemaClient} from "../schema-client";
import {GetProcessesRequest} from "./requests/get-processes";
import {PaginationRequest} from "./requests/pagination";
import {ResponseInterface} from "./responses/response";
import {CommandResponseInterface} from "./responses/comamnds";
import commandsSchema from "./schemas/commands";
import runCommandsSchema from "./schemas/run-commands";
export class SMClient {
schemaClient: SchemaClient
constructor() {
this.schemaClient = new SchemaClient({
baseUrl: 'http://fmw.sipachev.sv'
})
}
async getCommands(): Promise<ResponseInterface> {
let { responseData, headers } = await this.schemaClient.send(commandsSchema, {})
return {
data: responseData as CommandResponseInterface[],
headers: headers
}
}
async runCommand(data: any): Promise<ResponseInterface> {
let { headers } = await this.schemaClient.send(runCommandsSchema, data)
return {
data: null,
headers: headers
}
}
async getProcesses(data: GetProcessesRequest | PaginationRequest): Promise<ResponseInterface> {
let { responseData, headers } = await this.schemaClient.send(processesSchema, data)
return {
data: responseData as ProcessesResponseInterface[],
headers: headers
}
}
}
export let smClient = new SMClient
export default smClient

@ -6,6 +6,7 @@ import PlayCircleOutline from '@mui/icons-material/PlayCircleOutline';
import ConfirmDialog from "../confirm-dialog"; import ConfirmDialog from "../confirm-dialog";
import TabContext from "../../../context/tab"; import TabContext from "../../../context/tab";
import {TabEnum} from "../../../pages"; import {TabEnum} from "../../../pages";
import smClient from "../../../api/sm/sm-client";
export default function Commands() { export default function Commands() {
const {setTab} = useContext(TabContext) const {setTab} = useContext(TabContext)
@ -16,14 +17,7 @@ export default function Commands() {
const [open, setOpen] = useState<boolean>(false); const [open, setOpen] = useState<boolean>(false);
let refreshCommands = async () => { let refreshCommands = async () => {
let response = await fetch('http://fmw.sipachev.sv/system-monitoring/commands', { const { data: commands } = await smClient.getCommands()
method: 'GET',
headers: {
'Content-Type': 'application/json;charset=utf-8',
'X-Plugin-Token': 'passw0rd'
},
});
const commands: CommandInterface[] = await response.json()
setCommands(commands) setCommands(commands)
} }
@ -57,17 +51,12 @@ export default function Commands() {
return return
} }
lock = true lock = true
let url = `http://fmw.sipachev.sv/system-monitoring/commands/${selectedCommand.name}/run`
let data = command2data[selectedCommand.name] || {} let data = command2data[selectedCommand.name] || {}
data['requestId'] = dialogId; data['requestId'] = dialogId;
let response = await fetch(url, { await smClient.runCommand({
method: 'POST', commandName: selectedCommand.name,
headers: { ...data
'Content-Type': 'application/json;charset=utf-8', })
'X-Plugin-Token': 'passw0rd'
},
body: JSON.stringify(data)
});
lock = false lock = false
setTab(TabEnum.Processes) setTab(TabEnum.Processes)
} }

@ -12,6 +12,7 @@ import RunCircleOutlined from "@mui/icons-material/RunCircleOutlined"
import FactCheckOutlined from "@mui/icons-material/FactCheckOutlined" import FactCheckOutlined from "@mui/icons-material/FactCheckOutlined"
import {IconButton, LinearProgress, TableContainer, Table, TableBody, TableCell, TableHead, TableRow, TablePagination} from "@mui/material" import {IconButton, LinearProgress, TableContainer, Table, TableBody, TableCell, TableHead, TableRow, TablePagination} from "@mui/material"
import ConfirmDialog from "../confirm-dialog"; import ConfirmDialog from "../confirm-dialog";
import smClient from "../../../api/sm/sm-client";
export interface ProcessExceptionInterface { export interface ProcessExceptionInterface {
message: string, message: string,
@ -67,20 +68,12 @@ export default function Processes() {
return return
} }
refreshLock = true refreshLock = true
let response = await fetch('http://fmw.sipachev.sv/system-monitoring/processes?' + new URLSearchParams({ const { data: processes, headers } = await smClient.getProcesses({
page: (page + 1) + '', page: page + 1,
limit: 20 + '', limit: 20,
}), })
{
method: 'GET',
headers: {
'X-Plugin-Token': 'passw0rd',
'X-Pagination-Count': '0',
},
});
const processes: ProcessInterface[] = await response.json()
setProcesses(processes) setProcesses(processes)
setCount(Number(response.headers.get('X-Pagination-Count'))) setCount(Number(headers.get('X-Pagination-Count')))
refreshLock = false refreshLock = false
} }
let output = async (process: ProcessInterface) => { let output = async (process: ProcessInterface) => {
@ -164,6 +157,55 @@ export default function Processes() {
setOpen(false) setOpen(false)
} }
let formatSize = (length: any) => {
var i = 0, type = ['b','Kb','Mb','Gb','Tb','Pb'];
while((length / 1000 | 0) && i < type.length - 1) {
length /= 1024;
i++;
}
return length.toFixed(2) + ' ' + type[i];
}
let remaining = (process: ProcessInterface) =>
{
if (!process?.progress?.progress || !process.startedAt) {
return null
}
return Math.round((new Date().getTime() - new Date(process.startedAt).getTime()) / process?.progress.progress * (process?.progress.total - process?.progress.progress));
}
let formatTime = ($secs: number | null) => {
if ($secs === null) {
return ''
}
let $timeFormats = [
[0, '< 1 sec'],
[1, '1 sec'],
[2, 'secs', 1],
[60, '1 min'],
[120, 'mins', 60],
[3600, '1 hr'],
[7200, 'hrs', 3600],
[86400, '1 day'],
[172800, 'days', 86400],
];
for (let $index in $timeFormats) {
let $format = $timeFormats[$index]
if ($secs >= $format[0]) {
if ((typeof $timeFormats[$index + 1] !== 'undefined' && $secs < $timeFormats[$index + 1][0])
|| $index == $timeFormats.length - 1
) {
if (2 == $format.length) {
return $format[1];
}
return Math.floor($secs / $format[2]) + ' ' + $format[1];
}
}
}
}
return ( return (
<> <>
<TableContainer> <TableContainer>
@ -184,9 +226,9 @@ export default function Processes() {
<TableCell> <TableCell>
{!process.progress && !isFinished(process) && <LinearProgress/>} {!process.progress && !isFinished(process) && <LinearProgress/>}
{!process.progress && isFinished(process) && <LinearProgress variant="determinate" value={100}/>} {!process.progress && isFinished(process) && <LinearProgress variant="determinate" value={100}/>}
{process.progress && <LinearProgress value={progress(process.progress)}/>} {process.progress && <LinearProgress variant="determinate" value={progress(process.progress)}/>}
{process.progress && <span> {process.progress && <span>
{`${process.progress.progress}`} / {`${process.progress.total}`} - 50% [53Mb] / 20 sec {`${process.progress.progress}`} / {`${process.progress.total}`} - {progress(process.progress)}% [{formatSize(process.progress.memory)}] / {formatTime(remaining(process))}
</span>} </span>}
{canPlay(process) && <IconButton title={`Play`} aria-label="Play"> {canPlay(process) && <IconButton title={`Play`} aria-label="Play">
<PlayCircleOutline/> <PlayCircleOutline/>

Loading…
Cancel
Save