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 <[email protected]>
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;
+ });
+}