Alex Mikhalev
7 years ago
12 changed files with 259 additions and 297 deletions
@ -1,175 +1,108 @@ |
|||||||
|
/* tslint:disable:ordered-imports */ |
||||||
import { assign, pick } from "lodash"; |
import { assign, pick } from "lodash"; |
||||||
|
import { |
||||||
|
createSimpleSchema, createModelSchema, primitive, object, date, custom, |
||||||
|
ModelSchema, PropSchema, |
||||||
|
} from "serializr"; |
||||||
|
import list from "./list"; |
||||||
import * as s from ".."; |
import * as s from ".."; |
||||||
|
|
||||||
export interface ISectionJSON { |
export const durationSchema: PropSchema = { |
||||||
name: string; |
serializer: (duration: s.Duration | null) => |
||||||
state: boolean; |
duration != null ? duration.toSeconds() : null, |
||||||
} |
deserializer: (json: any) => |
||||||
const sectionProps = ["name", "state"]; |
typeof json === "number" ? s.Duration.fromSeconds(json) : null, |
||||||
|
|
||||||
export function sectionToJSON(sec: s.Section): ISectionJSON { |
|
||||||
return pick(sec, sectionProps); |
|
||||||
} |
|
||||||
|
|
||||||
export function sectionFromJSON(sec: s.Section, json: ISectionJSON) { |
|
||||||
assign(sec, pick(json, sectionProps)); |
|
||||||
} |
|
||||||
|
|
||||||
export interface ITimeOfDayJSON { |
|
||||||
hour: number; |
|
||||||
minute: number; |
|
||||||
second: number; |
|
||||||
millisecond: number; |
|
||||||
} |
|
||||||
const timeOfDayProps = ["hour", "minute", "second", "millisecond"]; |
|
||||||
|
|
||||||
export function timeOfDayToJSON(timeOfDay: s.TimeOfDay): ITimeOfDayJSON { |
|
||||||
return pick(timeOfDay, timeOfDayProps); |
|
||||||
} |
|
||||||
|
|
||||||
export function timeOfDayFromJSON(timeOfDay: s.TimeOfDay, json: ITimeOfDayJSON) { |
|
||||||
assign(timeOfDay, pick(json, timeOfDayProps)); |
|
||||||
} |
|
||||||
|
|
||||||
export interface IScheduleJSON { |
|
||||||
times: ITimeOfDayJSON[]; |
|
||||||
weekdays: number[]; |
|
||||||
from?: string; |
|
||||||
to?: string; |
|
||||||
} |
|
||||||
const scheduleProps = ["weekdays", "from", "to"]; |
|
||||||
|
|
||||||
export function scheduleToJSON(schedule: s.Schedule): IScheduleJSON { |
|
||||||
return { |
|
||||||
...pick(schedule, scheduleProps), |
|
||||||
times: schedule.times.map(timeOfDayToJSON), |
|
||||||
}; |
}; |
||||||
} |
|
||||||
|
|
||||||
export function scheduleFromJSON(schedule: s.Schedule, json: IScheduleJSON) { |
|
||||||
assign(schedule, pick(json, scheduleProps)); |
|
||||||
schedule.times.length = json.times.length; |
|
||||||
schedule.times.forEach((timeOfDay, i) => |
|
||||||
timeOfDayFromJSON(timeOfDay, json.times[i])); |
|
||||||
} |
|
||||||
|
|
||||||
export interface IProgramItemJSON { |
|
||||||
section: number; |
|
||||||
duration: number; |
|
||||||
} |
|
||||||
const programItemProps = ["section"]; |
|
||||||
|
|
||||||
export function programItemToJSON(programItem: s.ProgramItem): IProgramItemJSON { |
export const dateSchema: PropSchema = { |
||||||
return { |
serializer: (jsDate: Date | null) => jsDate != null ? |
||||||
...pick(programItem, programItemProps), |
jsDate.toISOString() : null, |
||||||
duration: programItem.duration.toSeconds(), |
deserializer: (json: any) => typeof json === "string" ? |
||||||
|
new Date(json) : null, |
||||||
}; |
}; |
||||||
} |
|
||||||
|
|
||||||
export function programItemFromJSON(json: IProgramItemJSON): s.ProgramItem { |
export const dateOfYearSchema: ModelSchema<s.DateOfYear> = { |
||||||
const duration = s.Duration.fromSeconds(json.duration); |
factory: () => new s.DateOfYear(), |
||||||
return new s.ProgramItem(json.section, duration); |
props: { |
||||||
} |
year: primitive(), |
||||||
|
month: primitive(), // this only works if it is represented as a # from 0-12
|
||||||
export interface IProgramJSON { |
day: primitive(), |
||||||
name: string; |
}, |
||||||
enabled: boolean; |
|
||||||
sequence: IProgramItemJSON[]; |
|
||||||
schedule: IScheduleJSON; |
|
||||||
running: boolean; |
|
||||||
} |
|
||||||
const programProps = ["name", "enabled", "running"]; |
|
||||||
|
|
||||||
export function programToJSON(program: s.Program): IProgramJSON { |
|
||||||
return { |
|
||||||
...pick(program, programProps), |
|
||||||
sequence: program.sequence.map(programItemToJSON), |
|
||||||
schedule: scheduleToJSON(program.schedule), |
|
||||||
}; |
}; |
||||||
} |
|
||||||
|
|
||||||
export function programFromJSON(program: s.Program, json: IProgramJSON) { |
|
||||||
assign(program, pick(json, programProps)); |
|
||||||
program.sequence = json.sequence.map((programItemJson) => |
|
||||||
programItemFromJSON(programItemJson)); |
|
||||||
scheduleFromJSON(program.schedule, json.schedule); |
|
||||||
} |
|
||||||
|
|
||||||
export interface ISectionRunJSON { |
|
||||||
id: number; |
|
||||||
section: number; |
|
||||||
duration: number; |
|
||||||
startTime?: number; |
|
||||||
pauseTime?: number; |
|
||||||
} |
|
||||||
const sectionRunProps = ["id", "section", "duration", "startTime", "pauseTime"]; |
|
||||||
|
|
||||||
export function sectionRunToJSON(sectionRun: s.SectionRun): ISectionRunJSON { |
export const timeOfDaySchema: ModelSchema<s.TimeOfDay> = { |
||||||
return { |
factory: () => new s.TimeOfDay(), |
||||||
...pick(sectionRun, sectionRunProps), |
props: { |
||||||
duration: sectionRun.duration.toSeconds(), |
hour: primitive(), |
||||||
|
minute: primitive(), |
||||||
|
second: primitive(), |
||||||
|
millisecond: primitive(), |
||||||
|
}, |
||||||
}; |
}; |
||||||
} |
|
||||||
|
|
||||||
export function sectionRunFromJSON(sectionRun: s.SectionRun, json: ISectionRunJSON) { |
export const sectionSchema: ModelSchema<s.Section> = { |
||||||
assign(sectionRun, pick(json, sectionRunProps)); |
factory: (c) => new (c.parentContext.target as s.SprinklersDevice).sectionConstructor( |
||||||
sectionRun.duration = s.Duration.fromSeconds(json.duration); |
c.parentContext.target, c.json.id), |
||||||
} |
props: { |
||||||
|
name: primitive(), |
||||||
|
state: primitive(), |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
interface ISectionRunnerJSON { |
export const sectionRunSchema: ModelSchema<s.SectionRun> = { |
||||||
queue: ISectionRunJSON[]; |
factory: (c) => new s.SectionRun(c.json.id), |
||||||
current: ISectionRunJSON | null; |
props: { |
||||||
paused: boolean; |
id: primitive(), |
||||||
} |
section: primitive(), |
||||||
const sectionRunnerProps = ["paused"]; |
duration: durationSchema, |
||||||
|
startTime: dateSchema, |
||||||
|
endTime: dateSchema, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
export function sectionRunnerToJSON(sectionRunner: s.SectionRunner): ISectionRunnerJSON { |
export const sectionRunnerSchema: ModelSchema<s.SectionRunner> = { |
||||||
return { |
factory: (c) => new (c.parentContext.target as s.SprinklersDevice).sectionRunnerConstructor( |
||||||
...pick(sectionRunner, sectionRunnerProps), |
c.parentContext.target), |
||||||
queue: sectionRunner.queue.map(sectionRunToJSON), |
props: { |
||||||
current: sectionRunner.current ? sectionRunToJSON(sectionRunner.current) : null, |
queue: list(object(sectionRunSchema)), |
||||||
|
current: object(sectionRunSchema), |
||||||
|
paused: primitive(), |
||||||
|
}, |
||||||
}; |
}; |
||||||
} |
|
||||||
|
|
||||||
export function sectionRunnerFromJSON(sectionRunner: s.SectionRunner, json: ISectionRunnerJSON) { |
export const scheduleSchema: ModelSchema<s.Schedule> = { |
||||||
assign(sectionRunner, pick(json, sectionRunnerProps)); |
factory: () => new s.Schedule(), |
||||||
sectionRunner.queue.length = json.queue.length; |
props: { |
||||||
sectionRunner.queue.forEach((sectionRun, i) => |
times: list(object(timeOfDaySchema)), |
||||||
sectionRunFromJSON(sectionRun, json.queue[i])); |
weekdays: list(primitive()), |
||||||
if (json.current == null) { |
from: object(dateOfYearSchema), |
||||||
sectionRunner.current = null; |
to: object(dateOfYearSchema), |
||||||
} else { |
}, |
||||||
if (sectionRunner.current == null) { |
}; |
||||||
sectionRunner.current = new s.SectionRun(); |
|
||||||
} |
|
||||||
sectionRunFromJSON(sectionRunner.current, json.current); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
interface ISprinklersDeviceJSON { |
export const programItemSchema: ModelSchema<s.ProgramItem> = { |
||||||
connected: boolean; |
factory: () => new s.ProgramItem(), |
||||||
sections: ISectionJSON[]; |
props: { |
||||||
sectionRunner: ISectionRunnerJSON; |
section: primitive(), |
||||||
programs: IProgramJSON[]; |
duration: durationSchema, |
||||||
} |
}, |
||||||
const sprinklersDeviceProps = ["connected"]; |
}; |
||||||
|
|
||||||
export function sprinklersDeviceToJSON(sprinklersDevice: s.SprinklersDevice): ISprinklersDeviceJSON { |
export const programSchema: ModelSchema<s.Program> = { |
||||||
return { |
factory: (c) => new (c.parentContext.target as s.SprinklersDevice).programConstructor( |
||||||
...pick(sprinklersDevice, sprinklersDeviceProps), |
c.parentContext.target, c.json.id), |
||||||
sections: sprinklersDevice.sections.map(sectionToJSON), |
props: { |
||||||
sectionRunner: sectionRunnerToJSON(sprinklersDevice.sectionRunner), |
name: primitive(), |
||||||
programs: sprinklersDevice.programs.map(programToJSON), |
enabled: primitive(), |
||||||
|
schedule: object(scheduleSchema), |
||||||
|
sequence: list(object(programItemSchema)), |
||||||
|
running: primitive(), |
||||||
|
}, |
||||||
}; |
}; |
||||||
} |
|
||||||
|
|
||||||
export function sprinklersDeviceFromJSON(sprinklersDevice: s.SprinklersDevice, json: ISprinklersDeviceJSON) { |
export const sprinklersDeviceSchema = createSimpleSchema({ |
||||||
assign(sprinklersDevice, pick(json, sprinklersDeviceProps)); |
connected: primitive(), |
||||||
sprinklersDevice.sections.length = json.sections.length; |
sections: list(object(sectionSchema)), |
||||||
sprinklersDevice.sections.forEach((section, i) => |
sectionRunner: object(sectionRunnerSchema), |
||||||
sectionFromJSON(section, json.sections[i])); |
programs: list(object(programSchema)), |
||||||
sectionRunnerFromJSON(sprinklersDevice.sectionRunner, json.sectionRunner); |
}); |
||||||
sprinklersDevice.programs.length = json.programs.length; |
|
||||||
sprinklersDevice.programs.forEach((program, i) => |
|
||||||
programFromJSON(program, json.programs[i])); |
|
||||||
} |
|
||||||
|
@ -0,0 +1,65 @@ |
|||||||
|
import { primitive, PropSchema } from "serializr"; |
||||||
|
|
||||||
|
function invariant(cond: boolean, message?: string) { |
||||||
|
if (!cond) { |
||||||
|
throw new Error("[serializr] " + (message || "Illegal State")); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function isPropSchema(thing: any) { |
||||||
|
return thing && thing.serializer && thing.deserializer; |
||||||
|
} |
||||||
|
|
||||||
|
function isAliasedPropSchema(propSchema: any) { |
||||||
|
return typeof propSchema === "object" && !!propSchema.jsonname; |
||||||
|
} |
||||||
|
|
||||||
|
function parallel(ar: any[], processor: (item: any, done: any) => void, cb: any) { |
||||||
|
if (ar.length === 0) { |
||||||
|
return void cb(null, []); |
||||||
|
} |
||||||
|
let left = ar.length; |
||||||
|
const resultArray: any[] = []; |
||||||
|
let failed = false; |
||||||
|
const processorCb = (idx: number, err: any, result: any) => { |
||||||
|
if (err) { |
||||||
|
if (!failed) { |
||||||
|
failed = true; |
||||||
|
cb(err); |
||||||
|
} |
||||||
|
} else if (!failed) { |
||||||
|
resultArray[idx] = result; |
||||||
|
if (--left === 0) { |
||||||
|
cb(null, resultArray); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
ar.forEach((value, idx) => processor(value, processorCb.bind(null, idx))); |
||||||
|
} |
||||||
|
|
||||||
|
export default function list(propSchema: PropSchema): PropSchema { |
||||||
|
propSchema = propSchema || primitive(); |
||||||
|
invariant(isPropSchema(propSchema), "expected prop schema as first argument"); |
||||||
|
invariant(!isAliasedPropSchema(propSchema), "provided prop is aliased, please put aliases first"); |
||||||
|
return { |
||||||
|
serializer(ar) { |
||||||
|
invariant(ar && typeof ar.length === "number" && typeof ar.map === "function", |
||||||
|
"expected array (like) object"); |
||||||
|
return ar.map(propSchema.serializer); |
||||||
|
}, |
||||||
|
deserializer(jsonArray, done, context) { |
||||||
|
if (jsonArray === null) { // sometimes go will return null in place of empty array
|
||||||
|
return void done(null, []); |
||||||
|
} |
||||||
|
if (!Array.isArray(jsonArray)) { |
||||||
|
return void done("[serializr] expected JSON array", null); |
||||||
|
} |
||||||
|
parallel( |
||||||
|
jsonArray, |
||||||
|
(item: any, itemDone: (err: any, targetPropertyValue: any) => void) => |
||||||
|
propSchema.deserializer(item, itemDone, context, undefined), |
||||||
|
done, |
||||||
|
); |
||||||
|
}, |
||||||
|
}; |
||||||
|
} |
Loading…
Reference in new issue