app/vidispine/field-group/VidispineFieldGroup.ts (134 lines of code) (raw):

import { createCheckers } from "ts-interface-checker"; import VidispineFieldGroupTI from "./VidispineFieldGroup-ti"; import { GetPlutoCustomData, PlutoCustomData } from "./CustomData"; import axios from "axios"; interface StringRestriction { minLength?: number; maxLength?: number; } interface Schema { min: number; max: number; name: string; } interface DataPair { key: string; value: string; } interface VidispineFieldIF { name: string; schema: Schema; type: string; //FIXME: set allowed values of this stringRestriction?: StringRestriction; data?: DataPair[]; defaultValue?: any; origin?: string; } interface VidispineFieldGroupIF { name: string; schema: Schema; field: VidispineFieldIF[]; origin?: string; inheritance?: string; //while actually a boolean value that is encoded as a string } const { StringRestriction, Schema, DataPair, VidispineFieldIF, VidispineFieldGroupIF, } = createCheckers(VidispineFieldGroupTI); /** * helper class that has a bunch of useful methods for interacting with the field */ class VidispineField implements VidispineFieldIF { name: string; schema: Schema; type: string; stringRestriction?: StringRestriction; data?: DataPair[]; defaultValue?: any; origin?: string; constructor(sourceObject: object) { //throws an exception if sourceObject does not validate VidispineFieldIF.check(sourceObject); const sourceField = <VidispineFieldIF>sourceObject; this.name = sourceField.name; this.schema = sourceField.schema; this.type = sourceField.type; this.stringRestriction = sourceField.stringRestriction; this.data = sourceField.data; this.defaultValue = sourceField.defaultValue; this.origin = sourceField.defaultValue; } /** * returns the PlutoCustomData block for the field, or undefined if it does not exist. * can throw an exception if the data block is not valid json or it fails structural validation */ getCustomData(): PlutoCustomData | undefined { const extradata = this.getDataValue("extradata"); if (extradata == undefined) return undefined; const parsed_data = JSON.parse(extradata); return GetPlutoCustomData(parsed_data); } /** * returns the value of the given custom data key as a string, or undefined if it does not exist * @param forKey the data key to look for */ getDataValue(forKey: string): string | undefined { if (this.data == undefined) return undefined; const datanodes = this.data.filter((pair) => pair.key === forKey); if (datanodes.length == 0) return undefined; return datanodes[0].value; } /** * tests whether the given string meets the restrictions placed on it * @param target string to test * @return a boolean value, true if the string validates, false if it does not. */ validateString(target: string): boolean { if (this.stringRestriction == undefined) return true; //no restrictions const stringlen = target.length; if (this.stringRestriction.minLength && this.stringRestriction.maxLength) { return ( stringlen >= this.stringRestriction.minLength && stringlen <= this.stringRestriction.maxLength ); } else if (this.stringRestriction.minLength) { return stringlen >= this.stringRestriction.minLength; } else if (this.stringRestriction.maxLength) { return stringlen <= this.stringRestriction.maxLength; } else { return true; } } } class VidispineFieldGroup implements VidispineFieldGroupIF { name: string; schema: Schema; field: VidispineFieldIF[]; origin?: string; inheritance?: string; //while actually a boolean value that is encoded as a string /** * construct a VidispineFieldGroup from an unchecked object. * checks that the provided object actually is a VidispineFieldGroup and throws an exception if not. * otherwise, copies the data and returns this object. * alternatively, if you _know_ that a given piece of data is a VidispineFieldGroupIF you can * simply cast it to VidispineFieldGroup: const g = <VidispineFieldGroup>groupdata; or * const g = groupdata as VidispineFieldGroup. This will reference rather than copy. * @param sourceObject object to copy. */ constructor(sourceObject: object) { VidispineFieldGroupIF.check(sourceObject); const sourceGroup = <VidispineFieldGroupIF>sourceObject; this.name = sourceGroup.name; this.schema = sourceGroup.schema; this.field = sourceGroup.field; this.origin = sourceGroup.origin; this.inheritance = sourceGroup.inheritance; } /** * returns a VidispineField object corresponding to the given field name. * returns undefined if no such field exists in the group. * @param fieldname the field name to search for */ getField(fieldname: string): VidispineField | undefined { const potentialFields = this.field.filter((f) => f.name === fieldname); if (potentialFields.length == 0) return undefined; return <VidispineField>potentialFields[0]; } /** * generator that yields a VidispineField object for all the fields in the group */ *getAllFields(): Generator<VidispineField> { //doing this with foreach() does not work, because foreach expects a function which is NOT a generator. //a very basic iteration loop here has no embedded anonymous function and therefore is free to yield for (let i = 0; i < this.field.length; i++) { yield <VidispineField>this.field[i]; } } } /** * loads in the given group from the server. Returns a Promise<VidispineFieldGroup> if successful or a failed promise * if the data is invalid or a server error occurred. Inspect the thrown value to find out what happened. * @param baseUrl Vidispine server base URL * @param groupname name of the group to load in */ async function LoadGroupFromServer( baseUrl: string, groupname: string ): Promise<VidispineFieldGroup> { console.log("Loading ", groupname); const url = baseUrl + `/API/metadata-field/field-group/${groupname}`; const result = await axios.get(url); console.log("got", result.data); try { return new VidispineFieldGroup(result.data); } catch (err) { console.error(err); console.error( `Could not load ${groupname} from ${url} - data validation failed because of ${err.toString()}` ); return Promise.reject("invalid data from server"); } } export type { DataPair }; //used by CustomData export { VidispineField, VidispineFieldGroup, LoadGroupFromServer };