Skip to content

Commit ff6690e

Browse files
authored
refactor: added some type guards (#1833)
Signed-off-by: Pedro Lamas <pedrolamas@gmail.com>
1 parent eab2bbf commit ff6690e

10 files changed

Lines changed: 115 additions & 44 deletions

File tree

src/components/widgets/filesystem/FileSystem.vue

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,12 +1232,18 @@ export default class FileSystem extends Mixins(StateMixin, FilesMixin, ServicesM
12321232
if (hasFileDataTransferTypeInDataTransfer(event.dataTransfer, 'files')) {
12331233
const files = getFileDataTransferDataFromDataTransfer(event.dataTransfer, 'files')
12341234
1235-
for (const file of files.items) {
1236-
const src = `${files.path}/${file}`
1237-
const dest = `${this.currentPath}/${file}`
1238-
SocketActions.serverFilesCopy(src, dest)
1235+
if (files) {
1236+
for (const file of files.items) {
1237+
const src = `${files.path}/${file}`
1238+
const dest = `${this.currentPath}/${file}`
1239+
SocketActions.serverFilesCopy(src, dest)
1240+
}
1241+
1242+
return
12391243
}
1240-
} else if (hasFilesInDataTransfer(event.dataTransfer)) {
1244+
}
1245+
1246+
if (hasFilesInDataTransfer(event.dataTransfer)) {
12411247
const files = await getFilesFromDataTransfer(event.dataTransfer)
12421248
12431249
if (files) {

src/components/widgets/gcode-preview/GcodePreviewCard.vue

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -442,12 +442,15 @@ export default class GcodePreviewCard extends Mixins(StateMixin, FilesMixin, Bro
442442
hasFileDataTransferTypeInDataTransfer(event.dataTransfer, 'jobs')
443443
) {
444444
const files = getFileDataTransferDataFromDataTransfer(event.dataTransfer, 'jobs')
445-
const path = files.path ? `gcodes/${files.path}` : 'gcodes'
446445
447-
const file: AppFile | undefined = this.$typedGetters['files/getFile'](path, files.items[0])
446+
if (files) {
447+
const path = files.path ? `gcodes/${files.path}` : 'gcodes'
448448
449-
if (file) {
450-
this.loadFile(file)
449+
const file: AppFile | undefined = this.$typedGetters['files/getFile'](path, files.items[0])
450+
451+
if (file) {
452+
this.loadFile(file)
453+
}
451454
}
452455
}
453456
}

src/components/widgets/job-queue/JobQueue.vue

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,11 +245,14 @@ export default class JobQueue extends Vue {
245245
hasFileDataTransferTypeInDataTransfer(event.dataTransfer, 'jobs')
246246
) {
247247
const files = getFileDataTransferDataFromDataTransfer(event.dataTransfer, 'jobs')
248-
const filePath = files.path ? `${files.path}/` : ''
249-
const filenames = files.items
250-
.map(file => `${filePath}${file}`)
251248
252-
SocketActions.serverJobQueuePostJob(filenames)
249+
if (files) {
250+
const filePath = files.path ? `${files.path}/` : ''
251+
const filenames = files.items
252+
.map(file => `${filePath}${file}`)
253+
254+
SocketActions.serverJobQueuePostJob(filenames)
255+
}
253256
}
254257
}
255258
}

src/plugins/socketClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ interface SocketApiResponse extends SocketResponseBase {
339339

340340
interface SocketApiErrorResponse extends SocketResponseBase {
341341
id: number;
342-
error: string | SocketError;
342+
error: SocketError;
343343
}
344344

345345
interface SocketNotificationResponse extends SocketResponseBase {

src/store/socket/actions.ts

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ActionTree } from 'vuex'
22
import { consola } from 'consola'
3-
import type { SocketState, SocketStatus } from './types'
3+
import type { SocketError, SocketState, SocketStatus } from './types'
44
import type { RootState } from '../types'
55
import { Globals } from '@/globals'
66
import { SocketActions } from '@/api/socketActions'
@@ -30,6 +30,27 @@ const VALID_TRANSITIONS: Record<SocketStatus, readonly SocketStatus[]> = {
3030
ready: ['disconnected', 'connecting', 'authenticating']
3131
}
3232

33+
const tryGetErrorMessageFromJson = (message: string): string | null => {
34+
try {
35+
// If our message contains JSON, we should try to parse it.
36+
// This was a problem in old versions of Moonraker, not sure if it's still the case, but it doesn't hurt to be defensive here.
37+
const messageAsObject = JSON.parse(message.replace(/'/g, '"'))
38+
39+
if (
40+
messageAsObject !== null &&
41+
typeof messageAsObject === 'object' &&
42+
'message' in messageAsObject &&
43+
typeof messageAsObject.message === 'string'
44+
) {
45+
return messageAsObject.message
46+
}
47+
} catch {
48+
// ignore and assume it's a plain string
49+
}
50+
51+
return null
52+
}
53+
3354
const getMoonrakerDatabase = async <T = Record<string, unknown>>(namespace: string) => {
3455
try {
3556
const response = await SocketActions.serverDatabaseGetItem<T>(undefined, namespace)
@@ -258,18 +279,16 @@ export const actions = {
258279
* Fired when the socket encounters an error. ws.onclose always follows and
259280
* drives the transition; here we just surface RPC error codes.
260281
*/
261-
async onSocketError ({ state, commit }, payload) {
262-
if (state.status === 'ready' && payload.code >= 400 && payload.code < 500) {
263-
// If our message contains json, we should try to parse it.
264-
// This is pretty bad, should get moonraker to fix this response.
265-
let message = ''
266-
try {
267-
const messageAsObject = JSON.parse(payload.message.replace(/'/g, '"')) as { message: string }
268-
269-
message = messageAsObject.message
270-
} catch {
271-
message = payload.message
272-
}
282+
async onSocketError ({ state, commit }, payload: SocketError) {
283+
if (
284+
state.status === 'ready' &&
285+
payload.code >= 400 &&
286+
payload.code < 500
287+
) {
288+
const message = (
289+
tryGetErrorMessageFromJson(payload.message) ||
290+
payload.message
291+
)
273292

274293
EventBus.$emit(message, { type: 'error' })
275294
} else if (payload.code === 503) {

src/store/socket/state.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ export const defaultState = (): SocketState => {
44
return {
55
status: 'initializing',
66
acceptingNotifications: false,
7-
error: null,
87
connectionId: null
98
}
109
}

src/store/socket/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
export interface SocketState {
22
status: SocketStatus;
33
acceptingNotifications: boolean;
4-
error: SocketError | null;
54
connectionId: number | null;
65
}
76

src/util/file-data-transfer.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
11
import { Globals } from '@/globals'
2+
import consola from 'consola'
3+
4+
const isFileDataTransferData = (dataAsObject: unknown): dataAsObject is FileDataTransferData => (
5+
dataAsObject !== null &&
6+
typeof dataAsObject === 'object' &&
7+
'path' in dataAsObject &&
8+
typeof dataAsObject.path === 'string' &&
9+
'items' in dataAsObject &&
10+
Array.isArray(dataAsObject.items) &&
11+
dataAsObject.items.every(item => typeof item === 'string')
12+
)
213

314
export type FileDataTransferData = {
415
path: string,
@@ -13,10 +24,20 @@ export const setFileDataTransferDataInDataTransfer = (dataTransfer: DataTransfer
1324
dataTransfer.setData(Globals.FILE_DATA_TRANSFER_TYPES[type], JSON.stringify(fileDataTransferData))
1425
}
1526

16-
export const getFileDataTransferDataFromDataTransfer = (dataTransfer: DataTransfer, type: keyof typeof Globals.FILE_DATA_TRANSFER_TYPES) => {
27+
export const getFileDataTransferDataFromDataTransfer = (dataTransfer: DataTransfer, type: keyof typeof Globals.FILE_DATA_TRANSFER_TYPES): FileDataTransferData | null => {
1728
const data = dataTransfer.getData(Globals.FILE_DATA_TRANSFER_TYPES[type])
1829

19-
const fileDataTransferData = JSON.parse(data) as FileDataTransferData
30+
try {
31+
const dataAsObject = JSON.parse(data)
32+
33+
if (!isFileDataTransferData(dataAsObject)) {
34+
throw new Error('Data in data transfer is not of type FileDataTransferData')
35+
}
36+
37+
return dataAsObject
38+
} catch (error) {
39+
consola.warn(`Failed to parse data transfer data for type ${Globals.FILE_DATA_TRANSFER_TYPES[type]}`, data, error)
40+
}
2041

21-
return fileDataTransferData
42+
return null
2243
}

src/util/string-formatters.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,16 @@ const stringFormatters = () => {
4747
getStringArray: (value: string, separator = ';') => {
4848
if (value.startsWith('["') && value.endsWith('"]')) {
4949
try {
50-
return JSON.parse(value) as string[]
50+
const valueAsObject = JSON.parse(value)
51+
52+
if (
53+
Array.isArray(valueAsObject) &&
54+
valueAsObject.every(item => typeof item === 'string')
55+
) {
56+
return valueAsObject
57+
}
5158
} catch {
59+
// ignore and fallback to splitting by separator
5260
}
5361
}
5462

src/workers/parseGcode.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable no-fallthrough */
2-
import type { ArcMove, BBox, Layer, LinearMove, Move, Part, Point, PositioningMode } from '@/store/gcodePreview/types'
2+
import type { ArcMove, BBox, Layer, LinearMove, Move, Part, PositioningMode } from '@/store/gcodePreview/types'
33
import isKeyOf from '@/util/is-key-of'
44
import { pick } from 'lodash-es'
55
import { split } from 'shlex'
@@ -63,6 +63,16 @@ const decimalRound = (a: number) => {
6363
return Math.round(a * 10000) / 10000
6464
}
6565

66+
const isPolygonData = (data: unknown): data is [number, number][] => (
67+
Array.isArray(data) &&
68+
data
69+
.every(x => (
70+
Array.isArray(x) &&
71+
x.length === 2 &&
72+
x.every(y => typeof y === 'number')
73+
))
74+
)
75+
6676
const parseGcode = (gcode: string, sendProgress: (filePosition: number) => void) => {
6777
const moves: Move[] = []
6878
const layers: Layer[] = []
@@ -115,17 +125,20 @@ const parseGcode = (gcode: string, sendProgress: (filePosition: number) => void)
115125
break
116126
case 'EXCLUDE_OBJECT_DEFINE':
117127
if ('polygon' in args && args.polygon) {
118-
const polygonData = JSON.parse(args.polygon) as [number, number][]
119-
120-
const part: Part = {
121-
polygon: polygonData
122-
.map(([x, y]): Point => ({
123-
x,
124-
y
125-
}))
128+
try {
129+
const data = JSON.parse(args.polygon)
130+
131+
if (isPolygonData(data)) {
132+
const part: Part = {
133+
polygon: data
134+
.map(([x, y]) => ({ x, y }))
135+
}
136+
137+
parts.push(part)
138+
}
139+
} catch {
140+
// ignore invalid JSON
126141
}
127-
128-
parts.push(part)
129142
}
130143
break
131144
case 'SET_RETRACTION':

0 commit comments

Comments
 (0)