aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTyler Schicke <tyler_schicke@brown.edu>2019-07-19 19:24:03 -0400
committerTyler Schicke <tyler_schicke@brown.edu>2019-07-19 19:24:03 -0400
commitb9a8ff80b504d02dbecd007d56379a44b0d4e721 (patch)
tree3bb6c061c4a75395f674722ddaabd96bcf837229 /src
parentaa8a6f7a786410fe2f60cced3325eaee510adb09 (diff)
parent6da55377db662a8fda3a241e7c9d115d33baff83 (diff)
Merge branch 'master' of github-tsch-brown:browngraphicslab/Dash-Web
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts87
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) {