diff options
author | Tyler Schicke <tyler_schicke@brown.edu> | 2019-07-19 19:24:03 -0400 |
---|---|---|
committer | Tyler Schicke <tyler_schicke@brown.edu> | 2019-07-19 19:24:03 -0400 |
commit | b9a8ff80b504d02dbecd007d56379a44b0d4e721 (patch) | |
tree | 3bb6c061c4a75395f674722ddaabd96bcf837229 /src | |
parent | aa8a6f7a786410fe2f60cced3325eaee510adb09 (diff) | |
parent | 6da55377db662a8fda3a241e7c9d115d33baff83 (diff) |
Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web
Diffstat (limited to 'src')
-rw-r--r-- | src/client/documents/Documents.ts | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3c248760b..5a6eff04c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -439,6 +439,93 @@ export namespace Docs { export namespace Get { + /** + * This function takes any valid JSON(-like) data, i.e. parsed or unparsed, and at arbitrarily + * deep levels of nesting, converts the data and structure into nested documents with the appropriate fields. + * + * After building a hierarchy within / below a top-level document, it then returns that top-level parent. + * + * @param input for convenience and flexibility, either a valid JSON string to be parsed, + * or the result of any JSON.parse() call. + * @param title an optional title to give to the highest parent document in the hierarchy + */ + export function DocumentHierarchyFromJson(input: any, title?: string): Opt<Doc>; + export function DocumentHierarchyFromJson(input: string, title?: string): Opt<Doc> { + // preliminary check - making a document out of null or undefined is meaningless + if (input === null || input === undefined) { + return undefined; + } + let parsed: any = input; + // if we've received a string, treat it like valid JSON and try to parse it into an object. + if (typeof input === "string") { + try { + parsed = JSON.parse(input); + } catch (e) { + // if this fails, the string is invalid JSON, so we should assume that the input is the + // result of a JSON.parse() call that returned a regular string value to be stored. + parsed = input; + } + } else if (!["object", "boolean", "number"].includes(typeof input)) { + // since the caller might also pass in the results of a JSON.parse() call, input + // might be an object, an array (still typeof object), a boolean or a number. + // anything else (a function, etc. that is passed in naively as any) is meaningless for this operation + return undefined; + } + let converted: Doc; + if (typeof parsed === "object" && !(parsed instanceof Array)) { + // JavaScript object: this gets converted directly to a document, which is a also a list of key value pairs + converted = convertObject(parsed); + } else { + // Array, a boolean, a string or a number: this gets stored as a field in a wrapper document, since no key value structure exists + (converted = new Doc).json = valueOf(parsed); + } + // if we passed in a title, assign it + title && (converted.title = title); + return converted; + } + + const convertObject = (object: any): Doc => { + // create the document that will store this object's data + let target = new Doc(); + // for each value of the document, recursively convert it to a document or other field + // and store the field at the appropriate key in the document + Object.keys(object).map(key => { + let result = valueOf(object[key]); + // if the result is undefined, ignore it + result && (target[key] = result); + }); + return target; + }; + + const convertList = (list: Array<any>): List<Field> => { + // create the list (Field implementation) that will store this Array's data + let thisLevel = new List(); + // for each element in the list, recursively convert it to a document or other field + // and push the field to the list + list.map(item => { + let result = valueOf(item); + // if the result is undefined, ignore it + result && thisLevel.push(result); + }); + return thisLevel; + }; + + const valueOf = (data: any): Opt<Field> => { + if (data === null || data === undefined) { + return undefined; + } + if (typeof data === "object") { + // recursively convert the object or array to the appropriate field value and return it + return data instanceof Array ? convertList(data) : convertObject(data); + } else if (["string", "number", "boolean"].includes(typeof data)) { + // no real conversion necessary - just return the data, already a valid field, to be stored + return data; + } else { + // any other type cannot be stored in JSON, but ya never know + throw new Error(`How did ${data} end up in JSON?`); + } + }; + export async function DocumentFromType(type: string, path: string, options: DocumentOptions): Promise<Opt<Doc>> { let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise<Doc | undefined>)) | undefined = undefined; if (type.indexOf("image") !== -1) { |