Skip to content

Commit

Permalink
fix: use zod for backend type schemas
Browse files Browse the repository at this point in the history
  • Loading branch information
nytamin committed Dec 6, 2023
1 parent 11580d6 commit e81ce18
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 45 deletions.
Binary file modified .yarn/install-state.gz
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ export class PrompterSettingsStore {
})
}

create(part: PrompterSettings) {
this._updateIfChanged(part)
create(data: PrompterSettings) {
this._updateIfChanged(data)
}
update(part: PrompterSettings) {
this._updateIfChanged(part)
update(data: PrompterSettings) {
this._updateIfChanged(data)
}

private _updateIfChanged(prompterSettings: PrompterSettings) {
Expand Down
57 changes: 52 additions & 5 deletions packages/apps/backend/src/data-stores/ViewPortStore.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { action, makeAutoObservable, observable } from 'mobx'
import isEqual from 'lodash.isequal'
import { ViewPort } from 'packages/shared/model/dist'
import { ViewPort, ViewPortSchema } from '@sofie-prompter-editor/shared-model'

export class ViewPortStore {
public viewPort = observable<ViewPort>({
Expand All @@ -24,11 +24,11 @@ export class ViewPortStore {
})
}

create(part: ViewPort) {
this._updateIfChanged(part)
create(data: ViewPort) {
this._updateIfChanged(data)
}
update(part: ViewPort) {
this._updateIfChanged(part)
update(data: ViewPort) {
this._updateIfChanged(data)
}

registerInstance(instanceId: string): boolean {
Expand All @@ -40,9 +40,56 @@ export class ViewPortStore {
return this.viewPort.instanceId === instanceId
}

// private verifyData(viewPort: ViewPort) {

// // for (const key of objectKeys(viewPort)) {
// // if (key === '_id') ensureString(viewPort, key, viewPort[key], true)
// // else if (key === 'instanceId') ensureString(viewPort, key, viewPort[key], true)
// // else if (key === 'width') ensureNumber(viewPort, key, viewPort[key], true)
// // else if (key === 'position') {
// // const position = viewPort[key]
// // for (const key of objectKeys(position)) {
// // if (key === 'scrollOffset') ensureNumber(viewPort, key, position[key], true)
// // else if (key === 'scrollOffsetTarget') ensureStringOrNull(viewPort, key, position[key], true)
// // else assertNever(key)
// // }
// // } else assertNever(key)
// // }

// viewPort._id
// }
private _updateIfChanged(viewPort: ViewPort) {
if (!isEqual(this.viewPort, viewPort)) {
ViewPortSchema.parse(viewPort)
this.viewPort = viewPort
}
}
}
// function objectKeys<T extends object>(obj: T): (keyof T)[] {
// return Object.keys(obj) as (keyof T)[]
// }

// function ensureString(obj: any, key: string, value: string, strict?: boolean): string {
// if (typeof value === 'string') return value

// if (!strict) {
// if (typeof value === 'number') return String(value)
// }
// throw new Error(`${key}: expected string, got ${typeof value}`)
// }
// function ensureNumber(obj: any, key: string, value: number, strict?: boolean): number {
// if (typeof value === 'number') return value

// if (!strict) {
// if (typeof value === 'string') {
// const num = Number(value)
// if (isFinite(num)) return num
// }
// }
// throw new Error(`${key}: expected number, got ${typeof value}`)
// }
// function ensureStringOrNull(obj: any, key: string, value: string | null, strict?: boolean): string | null {
// if (value === null) return value

// return ensureString(obj, key, value, strict)
// }
4 changes: 2 additions & 2 deletions packages/apps/client/src/TestController.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import { APIConnection } from './api/ApiConnection.ts'
import { PrompterSettings } from '@sofie-prompter-editor/shared-model'
import { EditObject, EditValue, useApiConnection } from './TestUtil.tsx'
import { EditObject, useApiConnection } from './TestUtil.tsx'

export const TestController: React.FC<{ api: APIConnection }> = ({ api }) => {
const [ready, setReady] = React.useState(false)
Expand Down Expand Up @@ -53,7 +53,7 @@ export const TestController: React.FC<{ api: APIConnection }> = ({ api }) => {
<EditObject
obj={prompterSettings}
onChange={(newData) => {
api.prompterSettings.update('', newData)
api.prompterSettings.update('', newData).catch(console.error)
}}
/>
</div>
Expand Down
4 changes: 2 additions & 2 deletions packages/apps/client/src/TestUtil.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function useApiConnection(
api.off('connected', onConnected)
api.off('disconnected', onDisconnected)
}
}, [])
}, [api])

useEffect(() => {
effect(connected)
Expand Down Expand Up @@ -83,7 +83,7 @@ export const EditValue: React.FC<{ value: any; onChange: (value: any) => void }>
type="number"
value={value}
onChange={(e) => {
onChange(e.target.value)
onChange(Number.parseFloat(e.target.value))
}}
/>
) : valueType === 'boolean' ? (
Expand Down
4 changes: 2 additions & 2 deletions packages/apps/client/src/TestViewPort.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import { APIConnection } from './api/ApiConnection.ts'
import { ViewPort } from '@sofie-prompter-editor/shared-model'
import { EditObject, EditValue, useApiConnection } from './TestUtil.tsx'
import { EditObject, useApiConnection } from './TestUtil.tsx'

export const TestViewPort: React.FC<{ api: APIConnection }> = ({ api }) => {
const [ready, setReady] = React.useState(false)
Expand Down Expand Up @@ -53,7 +53,7 @@ export const TestViewPort: React.FC<{ api: APIConnection }> = ({ api }) => {
<EditObject
obj={viewPort}
onChange={(newViewPort) => {
api.viewPort.update('viewport', newViewPort)
api.viewPort.update('viewport', newViewPort).catch(console.error)
}}
/>
</div>
Expand Down
3 changes: 2 additions & 1 deletion packages/shared/model/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
},
"dependencies": {
"@sofie-automation/shared-lib": "^1.49.1",
"@sofie-prompter-editor/shared-lib": "0.0.0"
"@sofie-prompter-editor/shared-lib": "0.0.0",
"zod": "^3.22.4"
},
"lint-staged": {
"*.{js,css,json,md,scss}": [
Expand Down
22 changes: 13 additions & 9 deletions packages/shared/model/src/model/PrompterSettings.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { z } from 'zod'

/** Set by a user */
export interface PrompterSettings {
fontSize: number // in percentage of viewport height
export type PrompterSettings = z.infer<typeof PrompterSettingsSchema>

export const PrompterSettingsSchema = z.object({
fontSize: z.number().min(0).max(100),

mirrorHorizontally: boolean
mirrorVertically: boolean
mirrorHorizontally: z.boolean(),
mirrorVertically: z.boolean(),

focusPosition: 'start' | 'center' | 'end'
showFocusPosition: boolean
focusPosition: z.union([z.literal('start'), z.literal('center'), z.literal('end')]),
showFocusPosition: z.boolean(),

/** Adds padding between the edge of the screen and the text */
marginHorizontal: number
marginHorizontal: z.number().min(0).max(100),
/** In percentage of viewport height */
marginVertical: number
}
marginVertical: z.number().min(0).max(100),
})
44 changes: 25 additions & 19 deletions packages/shared/model/src/model/ViewPort.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,40 @@
import { z } from 'zod'
import { ProtectedString } from '../ProtectedString.js'
import { PartId } from './Part.js'
import { SegmentId } from './Segment.js'
import { ZodProtectedString } from './lib.js'

/** Represents a view of the prompter, is streamed from the viewPort. This is always the last connected viewport. */
export interface ViewPort {
_id: 'viewport'
export type ViewPort = z.infer<typeof ViewPortSchema>

/** Defines a position of the viewport */
export type ViewPortPosition = z.infer<typeof ViewPortPositionSchema>

export const ViewPortPositionSchema = z.object({
/** The position of the ViewPort */
scrollOffset: z.number(),
/**
* The Part which the current offset is calculated from.
* `null` means "top of page"
*/
scrollOffsetTarget: ZodProtectedString<SegmentId | PartId | TextMarkerId>().nullable(),
})

/** TBD, something used to mark places in ScriptContents */
export type TextMarkerId = ProtectedString<'TextMarkerId', string>

export const ViewPortSchema = z.object({
_id: z.literal('viewport'),
/**
* When a ViewPort starts up, it randomizes its instanceId and sends it to the Server.
* If the ViewPorts' instanceId is the "last one" it is in control.
* The ViewPort "in control" will stream its data to the server continuously.
* If a ViewPort is not "in control" it could listen to the ViewPort data and jump to the same position to stay in sync.
*/
instanceId: string
instanceId: z.string(),

/** The width of the viewport (as percentage of viewport height) */
width: number

width: z.number(),
/** Current position of the viewport */
position: ViewPortPosition
}
/** Defines a position of the viewport */
export interface ViewPortPosition {
/**
* The Part which the current offset is calculated from.
* `null` means "top of page"
*/
scrollOffsetTarget: SegmentId | PartId | TextMarkerId | null
/** The position of the ViewPort */
scrollOffset: number
}
/** TBD, something used to mark places in ScriptContents */
export type TextMarkerId = ProtectedString<'TextMarkerId', string>
position: ViewPortPositionSchema,
})
15 changes: 15 additions & 0 deletions packages/shared/model/src/model/lib.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
import { z } from 'zod'
import { AnyProtectedString } from '../ProtectedString.js'

export interface DataObject {
_id: AnyProtectedString
}
/**
* Convenience function, defines a zod string but infers a ProtectedString
* Usage: ZodProtectedString<MyProtectedStringType>()
*/
export function ZodProtectedString<T extends AnyProtectedString>(): Omit<
z.ZodString,
'_type' | '_output' | '_input'
> & {
_type: T
_output: T
_input: T
} {
return z.string() as any
}
3 changes: 2 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1994,6 +1994,7 @@ __metadata:
"@sofie-automation/shared-lib": "npm:^1.49.1"
"@sofie-prompter-editor/shared-lib": "npm:0.0.0"
eventemitter3: "npm:^5.0.1"
zod: "npm:^3.22.4"
languageName: unknown
linkType: soft

Expand Down Expand Up @@ -12574,7 +12575,7 @@ __metadata:
languageName: node
linkType: hard

"zod@npm:^3.19.1":
"zod@npm:^3.19.1, zod@npm:^3.22.4":
version: 3.22.4
resolution: "zod@npm:3.22.4"
checksum: 7578ab283dac0eee66a0ad0fc4a7f28c43e6745aadb3a529f59a4b851aa10872b3890398b3160f257f4b6817b4ce643debdda4fb21a2c040adda7862cab0a587
Expand Down

0 comments on commit e81ce18

Please sign in to comment.