superset-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From b...@apache.org
Subject [incubator-superset] branch master updated: creating new circular-json safe stringify and replacing one call (#6772)
Date Tue, 29 Jan 2019 18:59:53 GMT
This is an automated email from the ASF dual-hosted git repository.

beto pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 11a7ad0  creating new circular-json safe stringify and replacing one call (#6772)
11a7ad0 is described below

commit 11a7ad00b7c44785752bf23ef90bcf4ebb58598e
Author: Michael McDuffee <mrmcduff@gmail.com>
AuthorDate: Tue Jan 29 10:59:42 2019 -0800

    creating new circular-json safe stringify and replacing one call (#6772)
---
 .../spec/javascripts/utils/safeStringify_spec.ts   | 110 +++++++++++++++++++++
 superset/assets/src/explore/exploreUtils.js        |   3 +-
 superset/assets/src/utils/safeStringify.ts         |  45 +++++++++
 3 files changed, 157 insertions(+), 1 deletion(-)

diff --git a/superset/assets/spec/javascripts/utils/safeStringify_spec.ts b/superset/assets/spec/javascripts/utils/safeStringify_spec.ts
new file mode 100644
index 0000000..6a019a2
--- /dev/null
+++ b/superset/assets/spec/javascripts/utils/safeStringify_spec.ts
@@ -0,0 +1,110 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { safeStringify } from '../../../src/utils/safeStringify';
+
+class Noise {
+  public next: Noise;
+}
+
+describe('Stringify utility testing', () => {
+  it('correctly parses a simple object just like JSON', () => {
+    const noncircular = {
+      b: 'foo',
+      c: 'bar',
+      d: [
+        {
+          e: 'hello',
+          f: ['world'],
+        },
+        {
+          e: 'hello',
+          f: ['darkness', 'my', 'old', 'friend'],
+        },
+      ],
+    };
+    expect(safeStringify(noncircular)).toEqual(JSON.stringify(noncircular));
+    // Checking that it works with quick-deepish-copies as well.
+    expect(JSON.parse(safeStringify(noncircular))).toEqual(JSON.parse(JSON.stringify(noncircular)));
+  });
+
+  it('handles simple circular json as expected', () => {
+    const ping = new Noise();
+    const pong = new Noise();
+    const pang = new Noise();
+    ping.next = pong;
+    pong.next = ping;
+
+    // ping.next is pong (the circular reference) now
+    const safeString = safeStringify(ping);
+    ping.next = pang;
+
+    // ping.next is pang now, which has no circular reference, so it's safe to use JSON.stringify
+    const ordinaryString = JSON.stringify(ping);
+    expect(safeString).toEqual(ordinaryString);
+  });
+
+  it('creates a parseable object even when the input is circular', () => {
+    const ping = new Noise();
+    const pong = new Noise();
+    ping.next = pong;
+    pong.next = ping;
+
+    const newNoise: Noise = JSON.parse(safeStringify(ping));
+    expect(newNoise).toBeTruthy();
+    expect(newNoise.next).toEqual({});
+  });
+
+  it('does not remove noncircular duplicates', () => {
+    const a = {
+      foo: 'bar',
+    };
+
+    const repeating = {
+      first: a,
+      second: a,
+      third: a,
+    };
+
+    expect(safeStringify(repeating)).toEqual(JSON.stringify(repeating));
+  });
+
+  it('does not remove nodes with empty objects', () => {
+    const emptyObjectValues = {
+      a: {},
+      b: 'foo',
+      c: {
+        d: 'good data here',
+        e: {},
+      },
+    };
+    expect(safeStringify(emptyObjectValues)).toEqual(JSON.stringify(emptyObjectValues));
+  });
+
+  it('does not remove nested same keys', () => {
+    const nestedKeys = {
+      a: 'b',
+      c: {
+        a: 'd',
+        x: 'y',
+      },
+    };
+
+    expect(safeStringify(nestedKeys)).toEqual(JSON.stringify(nestedKeys));
+  });
+});
diff --git a/superset/assets/src/explore/exploreUtils.js b/superset/assets/src/explore/exploreUtils.js
index 5148515..ec3496a 100644
--- a/superset/assets/src/explore/exploreUtils.js
+++ b/superset/assets/src/explore/exploreUtils.js
@@ -19,6 +19,7 @@
 /* eslint camelcase: 0 */
 import URI from 'urijs';
 import { availableDomains } from '../utils/hostNamesConfig';
+import { safeStringify } from '../utils/safeStringify';
 
 const MAX_URL_LENGTH = 8000;
 
@@ -71,7 +72,7 @@ export function getExploreLongUrl(formData, endpointType, allowOverflow
= true,
   Object.keys(extraSearch).forEach((key) => {
     search[key] = extraSearch[key];
   });
-  search.form_data = JSON.stringify(formData);
+  search.form_data = safeStringify(formData);
   if (endpointType === 'standalone') {
     search.standalone = 'true';
   }
diff --git a/superset/assets/src/utils/safeStringify.ts b/superset/assets/src/utils/safeStringify.ts
new file mode 100644
index 0000000..230dd35
--- /dev/null
+++ b/superset/assets/src/utils/safeStringify.ts
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * A Stringify function that will not crash when it runs into circular JSON references,
+ * unlike JSON.stringify. Any circular references are simply omitted, as if there had
+ * been no data present
+ * @param object any JSON object to be stringified
+ */
+export function safeStringify(object: any): string {
+  const cache = new Set();
+  return JSON.stringify(object, (key, value) => {
+    if (typeof value === 'object' && value !== null) {
+      if (cache.has(value)) {
+        // We've seen this object before
+        try {
+          // Quick deep copy to duplicate if this is a repeat rather than a circle.
+         return JSON.parse(JSON.stringify(value));
+        } catch (err) {
+          // Discard key if value cannot be duplicated.
+         return;
+        }
+      }
+      // Store the value in our cache.
+      cache.add(value);
+    }
+    return value;
+  });
+}


Mime
View raw message