From 2f4f78f16d7233ae07d84e739bba81fe3e9ae8d5 Mon Sep 17 00:00:00 2001 From: Hugo Bove Date: Thu, 15 Aug 2019 14:04:27 +0200 Subject: [PATCH 1/5] added lodash-es --- jest.config.js | 1 + package.json | 1 + yarn.lock | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/jest.config.js b/jest.config.js index 55acd19baed..cf5fa61d467 100644 --- a/jest.config.js +++ b/jest.config.js @@ -12,6 +12,7 @@ module.exports = { '/dist/', '/node_modules/' ], + transformIgnorePatterns: [`/node_modules/(?!lodash-es)`], moduleNameMapper: { '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|html)$': '/test_utils/__mocks__/fileMock.js', diff --git a/package.json b/package.json index 4b62505760f..ed34572c7ea 100644 --- a/package.json +++ b/package.json @@ -134,6 +134,7 @@ "firebase": "^5.8.3", "isomorphic-fetch": "^2.2.1", "jsonic": "^0.3.0", + "lodash-es": "^4.17.15", "neo4j-driver": "^1.7.5", "react": "^16.8.1", "react-addons-pure-render-mixin": "^15.0.2", diff --git a/yarn.lock b/yarn.lock index b2ea50b2169..3300b5f0bdf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6550,6 +6550,11 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" +lodash-es@^4.17.15: + version "4.17.15" + resolved "https://neo.jfrog.io/neo/api/npm/npm/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" + integrity sha1-Ib2Wg5NUQS8j16EDQOXqxu5FXXg= + lodash-es@^4.2.1: version "4.17.11" resolved "https://neo.jfrog.io/neo/api/npm/npm/lodash-es/-/lodash-es-4.17.11.tgz#145ab4a7ac5c5e52a3531fb4f310255a152b4be0" From 17107bbc2f5f068d0bb7e229d6ec753104b2d1bc Mon Sep 17 00:00:00 2001 From: Hugo Bove Date: Thu, 15 Aug 2019 14:10:21 +0200 Subject: [PATCH 2/5] added JSON export of data, trying to mimic APOC --- .../modules/Stream/CypherFrame/helpers.js | 96 +++- .../Stream/CypherFrame/helpers.test.js | 484 +++++++++++++++++- src/browser/modules/Stream/FrameTitlebar.jsx | 25 +- src/browser/modules/Stream/styled.jsx | 3 +- 4 files changed, 604 insertions(+), 4 deletions(-) diff --git a/src/browser/modules/Stream/CypherFrame/helpers.js b/src/browser/modules/Stream/CypherFrame/helpers.js index af8e3fa7025..669cff9fb83 100644 --- a/src/browser/modules/Stream/CypherFrame/helpers.js +++ b/src/browser/modules/Stream/CypherFrame/helpers.js @@ -18,8 +18,19 @@ * along with this program. If not, see . */ -import bolt from 'services/bolt/bolt' import { v1 as neo4j } from 'neo4j-driver' +import { + entries, + get, + includes, + isObjectLike, + lowerCase, + map, + omit, + reduce +} from 'lodash-es' + +import bolt from 'services/bolt/bolt' import * as viewTypes from 'shared/modules/stream/frameViewTypes' import { recursivelyExtractGraphItems, @@ -287,3 +298,86 @@ const arrayifyPath = (types = neo4j.types, path) => { ].filter(part => part !== null) }) } + +/** + * Converts a raw Neo4j record into a JSON friendly format, mimicking APOC output + * @param {Record} record + * @return {*} + */ +export function recordToJSONMapper (record) { + const keys = get(record, 'keys', []) + + return reduce( + keys, + (agg, key) => { + const field = record.get(key) + + return { + ...agg, + [key]: mapNeo4jValuesToPlainValues(field) + } + }, + {} + ) +} + +/** + * Recursively converts Neo4j values to plain values, leaving other types untouched + * @param {*} values + * @return {*} + */ +function mapNeo4jValuesToPlainValues (values) { + if (!isObjectLike(values)) { + return values + } + + if (Array.isArray(values)) { + return map(values, mapNeo4jValuesToPlainValues) + } + + // could be a Node or Relationship + const type = lowerCase(get(values, 'constructor.name', '')) + + if (includes(['relationship', 'node'], type)) { + const labels = + type === 'relationship' + ? { label: get(values, 'type') } + : { labels: get(values, 'labels', []) } + + return { + type, + ...labels, + ...mapNeo4jValuesToPlainValues(omit(values, ['type', 'labels'])) + } + } + + return reduce( + entries(values), + (agg, [key, value]) => ({ + ...agg, + [key]: mapNeo4jValuesToPlainValues(neo4jValueToPlainValue(value)) + }), + {} + ) +} + +/** + * Recursively convert Neo4j value to plain value, leaving other types untouched + * @param {*} value + * @return {*} + */ +function neo4jValueToPlainValue (value) { + switch (get(value, 'constructor')) { + case neo4j.types.Date: + case neo4j.types.DateTime: + case neo4j.types.Duration: + case neo4j.types.LocalDateTime: + case neo4j.types.LocalTime: + case neo4j.types.Time: + case neo4j.types.Point: + case neo4j.types.Integer: // not exposed in typings but still there + return value.toString() + default: + return value + } +} diff --git a/src/browser/modules/Stream/CypherFrame/helpers.test.js b/src/browser/modules/Stream/CypherFrame/helpers.test.js index c719df12af6..3036ce84e1f 100644 --- a/src/browser/modules/Stream/CypherFrame/helpers.test.js +++ b/src/browser/modules/Stream/CypherFrame/helpers.test.js @@ -32,7 +32,8 @@ import { initialView, extractRecordsToResultArray, flattenGraphItemsInResultArray, - stringifyResultArray + stringifyResultArray, + recordToJSONMapper } from './helpers' import { stringModifier, csvFormat } from 'services/bolt/cypherTypesFormatting' @@ -726,4 +727,485 @@ describe('helpers', () => { ]) }) }) + + describe('recordToJSONMapper', () => { + describe('Nodes', () => { + test('handles integer values', () => { + const node = new neo4j.types.Node(1, ['foo'], { bar: new neo4j.int(3) }) + const record = new neo4j.types.Record(['n'], [node]) + const expected = { + n: { + identity: 1, + type: 'node', + labels: ['foo'], + properties: { + bar: '3' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + + test('handles string values', () => { + const node = new neo4j.types.Node(1, ['foo'], { bar: 'baz' }) + const record = new neo4j.types.Record(['n'], [node]) + const expected = { + n: { + identity: 1, + type: 'node', + labels: ['foo'], + properties: { + bar: 'baz' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + + test('handles date values', () => { + const node = new neo4j.types.Node(1, ['foo'], { + bar: new neo4j.types.Date(1970, 1, 1) + }) + const record = new neo4j.types.Record(['n'], [node]) + const expected = { + n: { + identity: 1, + type: 'node', + labels: ['foo'], + properties: { + bar: '1970-01-01' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + + test('handles time values', () => { + const node = new neo4j.types.Node(1, ['foo'], { + bar: new neo4j.types.Time(11, 1, 12, 0, 0) + }) + const record = new neo4j.types.Record(['n'], [node]) + const expected = { + n: { + identity: 1, + type: 'node', + labels: ['foo'], + properties: { + bar: '11:01:12Z' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + + test('handles local time values', () => { + const node = new neo4j.types.Node(1, ['foo'], { + bar: new neo4j.types.LocalTime(11, 1, 12, 0) + }) + const record = new neo4j.types.Record(['n'], [node]) + const expected = { + n: { + identity: 1, + type: 'node', + labels: ['foo'], + properties: { + bar: '11:01:12' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + + test('handles datetime values', () => { + const node = new neo4j.types.Node(1, ['foo'], { + bar: new neo4j.types.DateTime(1970, 1, 1, 11, 1, 12, 0, 0) + }) + const record = new neo4j.types.Record(['n'], [node]) + const expected = { + n: { + identity: 1, + type: 'node', + labels: ['foo'], + properties: { + bar: '1970-01-01T11:01:12Z' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + + test('handles local datetime values', () => { + const node = new neo4j.types.Node(1, ['foo'], { + bar: new neo4j.types.LocalDateTime(1970, 1, 1, 11, 1, 12, 0) + }) + const record = new neo4j.types.Record(['n'], [node]) + const expected = { + n: { + identity: 1, + type: 'node', + labels: ['foo'], + properties: { + bar: '1970-01-01T11:01:12' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + + test('handles point values', () => { + const node = new neo4j.types.Node(1, ['foo'], { + bar: new neo4j.types.Point(1, 10, 10, 10) + }) + const record = new neo4j.types.Record(['n'], [node]) + const expected = { + n: { + identity: 1, + type: 'node', + labels: ['foo'], + properties: { + bar: 'Point{srid=1.0, x=10.0, y=10.0, z=10.0}' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + + test('handles duration values', () => { + const node = new neo4j.types.Node(1, ['foo'], { + bar: new neo4j.types.Duration(10, 5, 1, 0) + }) + const record = new neo4j.types.Record(['n'], [node]) + const expected = { + n: { + identity: 1, + type: 'node', + labels: ['foo'], + properties: { + bar: 'P10M5DT1S' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + }) + + describe('Relationships', () => { + test('handles integer values', () => { + const relationship = new neo4j.types.Relationship(1, 2, 3, 'foo', { + bar: new neo4j.int(3) + }) + const record = new neo4j.types.Record(['r'], [relationship]) + const expected = { + r: { + identity: 1, + start: 2, + end: 3, + type: 'relationship', + label: 'foo', + properties: { + bar: '3' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + + test('handles string values', () => { + const relationship = new neo4j.types.Relationship(1, 2, 3, 'foo', { + bar: 'baz' + }) + const record = new neo4j.types.Record(['r'], [relationship]) + const expected = { + r: { + identity: 1, + start: 2, + end: 3, + type: 'relationship', + label: 'foo', + properties: { + bar: 'baz' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + + test('handles date values', () => { + const relationship = new neo4j.types.Relationship(1, 2, 3, 'foo', { + bar: new neo4j.types.Date(1970, 1, 1) + }) + const record = new neo4j.types.Record(['r'], [relationship]) + const expected = { + r: { + identity: 1, + start: 2, + end: 3, + type: 'relationship', + label: 'foo', + properties: { + bar: '1970-01-01' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + + test('handles time values', () => { + const relationship = new neo4j.types.Relationship(1, 2, 3, 'foo', { + bar: new neo4j.types.Time(11, 1, 12, 0, 0) + }) + const record = new neo4j.types.Record(['r'], [relationship]) + const expected = { + r: { + identity: 1, + start: 2, + end: 3, + type: 'relationship', + label: 'foo', + properties: { + bar: '11:01:12Z' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + + test('handles local time values', () => { + const relationship = new neo4j.types.Relationship(1, 2, 3, 'foo', { + bar: new neo4j.types.LocalTime(11, 1, 12, 0) + }) + const record = new neo4j.types.Record(['r'], [relationship]) + const expected = { + r: { + identity: 1, + start: 2, + end: 3, + type: 'relationship', + label: 'foo', + properties: { + bar: '11:01:12' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + + test('handles datetime values', () => { + const relationship = new neo4j.types.Relationship(1, 2, 3, 'foo', { + bar: new neo4j.types.DateTime(1970, 1, 1, 11, 1, 12, 0, 0) + }) + const record = new neo4j.types.Record(['r'], [relationship]) + const expected = { + r: { + identity: 1, + start: 2, + end: 3, + type: 'relationship', + label: 'foo', + properties: { + bar: '1970-01-01T11:01:12Z' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + + test('handles local datetime values', () => { + const relationship = new neo4j.types.Relationship(1, 2, 3, 'foo', { + bar: new neo4j.types.LocalDateTime(1970, 1, 1, 11, 1, 12, 0) + }) + const record = new neo4j.types.Record(['r'], [relationship]) + const expected = { + r: { + identity: 1, + start: 2, + end: 3, + type: 'relationship', + label: 'foo', + properties: { + bar: '1970-01-01T11:01:12' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + + test('handles point values', () => { + const relationship = new neo4j.types.Relationship(1, 2, 3, 'foo', { + bar: new neo4j.types.Point(1, 10, 10, 10) + }) + const record = new neo4j.types.Record(['r'], [relationship]) + const expected = { + r: { + identity: 1, + start: 2, + end: 3, + type: 'relationship', + label: 'foo', + properties: { + bar: 'Point{srid=1.0, x=10.0, y=10.0, z=10.0}' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + + test('handles duration values', () => { + const relationship = new neo4j.types.Relationship(1, 2, 3, 'foo', { + bar: new neo4j.types.Duration(10, 5, 1, 0) + }) + const record = new neo4j.types.Record(['r'], [relationship]) + const expected = { + r: { + identity: 1, + start: 2, + end: 3, + type: 'relationship', + label: 'foo', + properties: { + bar: 'P10M5DT1S' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + }) + + describe('Nodes and Relationships', () => { + test('Node -> Relationship -> Node', () => { + const node1 = new neo4j.types.Node(1, ['foo'], { + bar: new neo4j.int(3) + }) + const relationship = new neo4j.types.Relationship(3, 1, 2, 'bom', { + bar: 'apa' + }) + const node2 = new neo4j.types.Node(2, ['bam'], { + bar: new neo4j.types.Date(1970, 1, 1) + }) + const record = new neo4j.types.Record( + ['n1', 'r1', 'n2'], + [node1, relationship, node2] + ) + const expected = { + n1: { + identity: 1, + type: 'node', + labels: ['foo'], + properties: { + bar: '3' + } + }, + r1: { + identity: 3, + type: 'relationship', + label: 'bom', + start: 1, + end: 2, + properties: { + bar: 'apa' + } + }, + n2: { + identity: 2, + type: 'node', + labels: ['bam'], + properties: { + bar: '1970-01-01' + } + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + }) + + describe('Paths', () => { + test('MATCH p = (:Person)-[r]-(:Movie) RETURN p', () => { + const node1 = new neo4j.types.Node(1, ['foo'], { + bar: new neo4j.int(3) + }) + const relationship = new neo4j.types.Relationship(3, 1, 2, 'bom', { + bar: 'apa' + }) + const node2 = new neo4j.types.Node(2, ['bam'], { + bar: new neo4j.types.Date(1970, 1, 1) + }) + const path = new neo4j.types.Path(node1, node2, [ + new neo4j.types.PathSegment(node1, relationship, node2) + ]) + const record = new neo4j.types.Record(['p'], [path]) + const expected = { + p: { + length: 1, + start: { + identity: 1, + type: 'node', + labels: ['foo'], + properties: { + bar: '3' + } + }, + end: { + identity: 2, + type: 'node', + labels: ['bam'], + properties: { + bar: '1970-01-01' + } + }, + segments: [ + { + start: { + identity: 1, + type: 'node', + labels: ['foo'], + properties: { + bar: '3' + } + }, + relationship: { + identity: 3, + type: 'relationship', + label: 'bom', + start: 1, + end: 2, + properties: { + bar: 'apa' + } + }, + end: { + identity: 2, + type: 'node', + labels: ['bam'], + properties: { + bar: '1970-01-01' + } + } + } + ] + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + }) + }) }) diff --git a/src/browser/modules/Stream/FrameTitlebar.jsx b/src/browser/modules/Stream/FrameTitlebar.jsx index c8d93b1564d..52f96b4121e 100644 --- a/src/browser/modules/Stream/FrameTitlebar.jsx +++ b/src/browser/modules/Stream/FrameTitlebar.jsx @@ -22,6 +22,8 @@ import { connect } from 'react-redux' import React, { Component } from 'react' import { withBus } from 'react-suber' import { saveAs } from 'file-saver' +import { map } from 'lodash-es' + import * as editor from 'shared/modules/editor/editorDuck' import * as commands from 'shared/modules/commands/commandsDuck' import { @@ -60,11 +62,14 @@ import { } from 'shared/services/exporting/imageUtils' import { stringifyResultArray, - transformResultRecordsToResultArray + transformResultRecordsToResultArray, + recordToJSONMapper } from 'browser/modules/Stream/CypherFrame/helpers' import { csvFormat } from 'services/bolt/cypherTypesFormatting' import arrayHasItems from 'shared/utils/array-has-items' +const JSON_EXPORT_INDENT = 2 + class FrameTitlebar extends Component { hasData () { return this.props.numRecords > 0 @@ -107,6 +112,19 @@ class FrameTitlebar extends Component { } } + exportJSON (records) { + const data = JSON.stringify( + map(records, recordToJSONMapper), + null, + JSON_EXPORT_INDENT + ) + const blob = new Blob([data], { + type: 'text/plain;charset=utf-8' + }) + + saveAs(blob, 'records.json') + } + exportPNG () { const { svgElement, graphElement, type } = this.props.visElement downloadPNGFromSVG(svgElement, graphElement, type) @@ -179,6 +197,11 @@ class FrameTitlebar extends Component { > Export CSV + this.exportJSON(props.getRecords())} + > + Export JSON + diff --git a/src/browser/modules/Stream/styled.jsx b/src/browser/modules/Stream/styled.jsx index 7b6f3bd56fe..661e1e742e0 100644 --- a/src/browser/modules/Stream/styled.jsx +++ b/src/browser/modules/Stream/styled.jsx @@ -346,8 +346,9 @@ export const DropdownContent = styled.li` box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); border-radius: 6px; z-index: 1; + text-align: left; line-height: 30px; - padding: 5px 0 5px 0; + padding: 5px 10px; ` export const DropdownItem = styled.a` From 8280de05a7949efad87a4d07ef82235e6705a817 Mon Sep 17 00:00:00 2001 From: Hugo Bove Date: Fri, 16 Aug 2019 11:27:25 +0200 Subject: [PATCH 3/5] Changes from review - CSS fix - Export int as Number - Export Point as GeoJSON - Reorder and cleanup --- .../modules/Stream/CypherFrame/helpers.js | 39 ++++++++++++- .../Stream/CypherFrame/helpers.test.js | 57 ++++++++++++++++--- src/browser/modules/Stream/styled.jsx | 3 +- 3 files changed, 86 insertions(+), 13 deletions(-) diff --git a/src/browser/modules/Stream/CypherFrame/helpers.js b/src/browser/modules/Stream/CypherFrame/helpers.js index 669cff9fb83..45247dbfa92 100644 --- a/src/browser/modules/Stream/CypherFrame/helpers.js +++ b/src/browser/modules/Stream/CypherFrame/helpers.js @@ -335,6 +335,10 @@ function mapNeo4jValuesToPlainValues (values) { return map(values, mapNeo4jValuesToPlainValues) } + if (isNeo4jValue(values)) { + return neo4jValueToPlainValue(values) + } + // could be a Node or Relationship const type = lowerCase(get(values, 'constructor.name', '')) @@ -355,7 +359,7 @@ function mapNeo4jValuesToPlainValues (values) { entries(values), (agg, [key, value]) => ({ ...agg, - [key]: mapNeo4jValuesToPlainValues(neo4jValueToPlainValue(value)) + [key]: mapNeo4jValuesToPlainValues(value) }), {} ) @@ -374,10 +378,39 @@ function neo4jValueToPlainValue (value) { case neo4j.types.LocalDateTime: case neo4j.types.LocalTime: case neo4j.types.Time: - case neo4j.types.Point: - case neo4j.types.Integer: // not exposed in typings but still there return value.toString() + case neo4j.types.Integer: // not exposed in typings but still there + return value.inSafeRange() ? value.toInt() : value.toNumber() + case neo4j.types.Point: + return { + type: neo4j.types.Point.name, + coordinates: + value.z !== undefined + ? [value.x, value.y, value.z] + : [value.x, value.y] + } default: return value } } + +/** + * checks if a value is a neo4j value + * @param value + * @return {boolean} + */ +function isNeo4jValue (value) { + switch (get(value, 'constructor')) { + case neo4j.types.Date: + case neo4j.types.DateTime: + case neo4j.types.Duration: + case neo4j.types.LocalDateTime: + case neo4j.types.LocalTime: + case neo4j.types.Time: + case neo4j.types.Point: + case neo4j.types.Integer: // not exposed in typings but still there + return true + default: + return false + } +} diff --git a/src/browser/modules/Stream/CypherFrame/helpers.test.js b/src/browser/modules/Stream/CypherFrame/helpers.test.js index 3036ce84e1f..9d60da4bb90 100644 --- a/src/browser/modules/Stream/CypherFrame/helpers.test.js +++ b/src/browser/modules/Stream/CypherFrame/helpers.test.js @@ -739,7 +739,7 @@ describe('helpers', () => { type: 'node', labels: ['foo'], properties: { - bar: '3' + bar: 3 } } } @@ -861,7 +861,7 @@ describe('helpers', () => { test('handles point values', () => { const node = new neo4j.types.Node(1, ['foo'], { - bar: new neo4j.types.Point(1, 10, 10, 10) + bar: new neo4j.types.Point(1, 10, 5, 15) }) const record = new neo4j.types.Record(['n'], [node]) const expected = { @@ -870,7 +870,10 @@ describe('helpers', () => { type: 'node', labels: ['foo'], properties: { - bar: 'Point{srid=1.0, x=10.0, y=10.0, z=10.0}' + bar: { + type: 'Point', + coordinates: [10, 5, 15] + } } } } @@ -912,7 +915,7 @@ describe('helpers', () => { type: 'relationship', label: 'foo', properties: { - bar: '3' + bar: 3 } } } @@ -1048,7 +1051,7 @@ describe('helpers', () => { test('handles point values', () => { const relationship = new neo4j.types.Relationship(1, 2, 3, 'foo', { - bar: new neo4j.types.Point(1, 10, 10, 10) + bar: new neo4j.types.Point(1, 10, 5, 15) }) const record = new neo4j.types.Record(['r'], [relationship]) const expected = { @@ -1059,7 +1062,10 @@ describe('helpers', () => { type: 'relationship', label: 'foo', properties: { - bar: 'Point{srid=1.0, x=10.0, y=10.0, z=10.0}' + bar: { + type: 'Point', + coordinates: [10, 5, 15] + } } } } @@ -1110,7 +1116,7 @@ describe('helpers', () => { type: 'node', labels: ['foo'], properties: { - bar: '3' + bar: 3 } }, r1: { @@ -1160,7 +1166,7 @@ describe('helpers', () => { type: 'node', labels: ['foo'], properties: { - bar: '3' + bar: 3 } }, end: { @@ -1178,7 +1184,7 @@ describe('helpers', () => { type: 'node', labels: ['foo'], properties: { - bar: '3' + bar: 3 } }, relationship: { @@ -1207,5 +1213,38 @@ describe('helpers', () => { expect(recordToJSONMapper(record)).toEqual(expected) }) }) + + describe('Returns of raw data', () => { + test('RETURN {...data} as foo', () => { + const record = new neo4j.types.Record( + ['foo'], + [ + { + data: [ + new neo4j.int(1), + 'car', + new neo4j.types.Point(1, 10, 5, 15), + new neo4j.types.Date(1970, 1, 1) + ] + } + ] + ) + const expected = { + foo: { + data: [ + 1, + 'car', + { + type: 'Point', + coordinates: [10, 5, 15] + }, + '1970-01-01' + ] + } + } + + expect(recordToJSONMapper(record)).toEqual(expected) + }) + }) }) }) diff --git a/src/browser/modules/Stream/styled.jsx b/src/browser/modules/Stream/styled.jsx index 661e1e742e0..719cc18284c 100644 --- a/src/browser/modules/Stream/styled.jsx +++ b/src/browser/modules/Stream/styled.jsx @@ -348,7 +348,7 @@ export const DropdownContent = styled.li` z-index: 1; text-align: left; line-height: 30px; - padding: 5px 10px; + padding: 5px 0; ` export const DropdownItem = styled.a` @@ -356,6 +356,7 @@ export const DropdownItem = styled.a` color: ${props => props.theme.secondaryButtonText}; width: 100%; display: inline-block; + padding: 0 10px; &:hover { color: ${props => props.theme.secondaryButtonTextHover}; text-decoration: none; From 4f50386e44e13395bc91c82967d7928f6cf2d3a0 Mon Sep 17 00:00:00 2001 From: Hugo Bove Date: Fri, 16 Aug 2019 12:41:43 +0200 Subject: [PATCH 4/5] return Point as plain object --- .../modules/Stream/CypherFrame/helpers.js | 9 --------- .../Stream/CypherFrame/helpers.test.js | 20 +++---------------- 2 files changed, 3 insertions(+), 26 deletions(-) diff --git a/src/browser/modules/Stream/CypherFrame/helpers.js b/src/browser/modules/Stream/CypherFrame/helpers.js index 45247dbfa92..96bc8095426 100644 --- a/src/browser/modules/Stream/CypherFrame/helpers.js +++ b/src/browser/modules/Stream/CypherFrame/helpers.js @@ -381,14 +381,6 @@ function neo4jValueToPlainValue (value) { return value.toString() case neo4j.types.Integer: // not exposed in typings but still there return value.inSafeRange() ? value.toInt() : value.toNumber() - case neo4j.types.Point: - return { - type: neo4j.types.Point.name, - coordinates: - value.z !== undefined - ? [value.x, value.y, value.z] - : [value.x, value.y] - } default: return value } @@ -407,7 +399,6 @@ function isNeo4jValue (value) { case neo4j.types.LocalDateTime: case neo4j.types.LocalTime: case neo4j.types.Time: - case neo4j.types.Point: case neo4j.types.Integer: // not exposed in typings but still there return true default: diff --git a/src/browser/modules/Stream/CypherFrame/helpers.test.js b/src/browser/modules/Stream/CypherFrame/helpers.test.js index 9d60da4bb90..30fd4c12642 100644 --- a/src/browser/modules/Stream/CypherFrame/helpers.test.js +++ b/src/browser/modules/Stream/CypherFrame/helpers.test.js @@ -870,10 +870,7 @@ describe('helpers', () => { type: 'node', labels: ['foo'], properties: { - bar: { - type: 'Point', - coordinates: [10, 5, 15] - } + bar: { srid: 1, x: 10, y: 5, z: 15 } } } } @@ -1062,10 +1059,7 @@ describe('helpers', () => { type: 'relationship', label: 'foo', properties: { - bar: { - type: 'Point', - coordinates: [10, 5, 15] - } + bar: { srid: 1, x: 10, y: 5, z: 15 } } } } @@ -1231,15 +1225,7 @@ describe('helpers', () => { ) const expected = { foo: { - data: [ - 1, - 'car', - { - type: 'Point', - coordinates: [10, 5, 15] - }, - '1970-01-01' - ] + data: [1, 'car', { srid: 1, x: 10, y: 5, z: 15 }, '1970-01-01'] } } From 5bb680852905b41255f0f0699987ee9ea11e87c4 Mon Sep 17 00:00:00 2001 From: Hugo Bove Date: Fri, 30 Aug 2019 14:02:58 +0200 Subject: [PATCH 5/5] changes from review --- .../modules/Stream/CypherFrame/helpers.js | 15 +--- .../Stream/CypherFrame/helpers.test.js | 74 +++++++++---------- 2 files changed, 41 insertions(+), 48 deletions(-) diff --git a/src/browser/modules/Stream/CypherFrame/helpers.js b/src/browser/modules/Stream/CypherFrame/helpers.js index 96bc8095426..753df4b9c7d 100644 --- a/src/browser/modules/Stream/CypherFrame/helpers.js +++ b/src/browser/modules/Stream/CypherFrame/helpers.js @@ -26,7 +26,6 @@ import { isObjectLike, lowerCase, map, - omit, reduce } from 'lodash-es' @@ -340,18 +339,12 @@ function mapNeo4jValuesToPlainValues (values) { } // could be a Node or Relationship - const type = lowerCase(get(values, 'constructor.name', '')) - - if (includes(['relationship', 'node'], type)) { - const labels = - type === 'relationship' - ? { label: get(values, 'type') } - : { labels: get(values, 'labels', []) } + const elementType = lowerCase(get(values, 'constructor.name', '')) + if (includes(['relationship', 'node'], elementType)) { return { - type, - ...labels, - ...mapNeo4jValuesToPlainValues(omit(values, ['type', 'labels'])) + elementType, + ...mapNeo4jValuesToPlainValues({ ...values }) } } diff --git a/src/browser/modules/Stream/CypherFrame/helpers.test.js b/src/browser/modules/Stream/CypherFrame/helpers.test.js index 30fd4c12642..d9d8a467429 100644 --- a/src/browser/modules/Stream/CypherFrame/helpers.test.js +++ b/src/browser/modules/Stream/CypherFrame/helpers.test.js @@ -736,7 +736,7 @@ describe('helpers', () => { const expected = { n: { identity: 1, - type: 'node', + elementType: 'node', labels: ['foo'], properties: { bar: 3 @@ -753,7 +753,7 @@ describe('helpers', () => { const expected = { n: { identity: 1, - type: 'node', + elementType: 'node', labels: ['foo'], properties: { bar: 'baz' @@ -772,7 +772,7 @@ describe('helpers', () => { const expected = { n: { identity: 1, - type: 'node', + elementType: 'node', labels: ['foo'], properties: { bar: '1970-01-01' @@ -791,7 +791,7 @@ describe('helpers', () => { const expected = { n: { identity: 1, - type: 'node', + elementType: 'node', labels: ['foo'], properties: { bar: '11:01:12Z' @@ -810,7 +810,7 @@ describe('helpers', () => { const expected = { n: { identity: 1, - type: 'node', + elementType: 'node', labels: ['foo'], properties: { bar: '11:01:12' @@ -829,7 +829,7 @@ describe('helpers', () => { const expected = { n: { identity: 1, - type: 'node', + elementType: 'node', labels: ['foo'], properties: { bar: '1970-01-01T11:01:12Z' @@ -848,7 +848,7 @@ describe('helpers', () => { const expected = { n: { identity: 1, - type: 'node', + elementType: 'node', labels: ['foo'], properties: { bar: '1970-01-01T11:01:12' @@ -867,7 +867,7 @@ describe('helpers', () => { const expected = { n: { identity: 1, - type: 'node', + elementType: 'node', labels: ['foo'], properties: { bar: { srid: 1, x: 10, y: 5, z: 15 } @@ -886,7 +886,7 @@ describe('helpers', () => { const expected = { n: { identity: 1, - type: 'node', + elementType: 'node', labels: ['foo'], properties: { bar: 'P10M5DT1S' @@ -909,8 +909,8 @@ describe('helpers', () => { identity: 1, start: 2, end: 3, - type: 'relationship', - label: 'foo', + elementType: 'relationship', + type: 'foo', properties: { bar: 3 } @@ -930,8 +930,8 @@ describe('helpers', () => { identity: 1, start: 2, end: 3, - type: 'relationship', - label: 'foo', + elementType: 'relationship', + type: 'foo', properties: { bar: 'baz' } @@ -951,8 +951,8 @@ describe('helpers', () => { identity: 1, start: 2, end: 3, - type: 'relationship', - label: 'foo', + elementType: 'relationship', + type: 'foo', properties: { bar: '1970-01-01' } @@ -972,8 +972,8 @@ describe('helpers', () => { identity: 1, start: 2, end: 3, - type: 'relationship', - label: 'foo', + elementType: 'relationship', + type: 'foo', properties: { bar: '11:01:12Z' } @@ -993,8 +993,8 @@ describe('helpers', () => { identity: 1, start: 2, end: 3, - type: 'relationship', - label: 'foo', + elementType: 'relationship', + type: 'foo', properties: { bar: '11:01:12' } @@ -1014,8 +1014,8 @@ describe('helpers', () => { identity: 1, start: 2, end: 3, - type: 'relationship', - label: 'foo', + elementType: 'relationship', + type: 'foo', properties: { bar: '1970-01-01T11:01:12Z' } @@ -1035,8 +1035,8 @@ describe('helpers', () => { identity: 1, start: 2, end: 3, - type: 'relationship', - label: 'foo', + elementType: 'relationship', + type: 'foo', properties: { bar: '1970-01-01T11:01:12' } @@ -1056,8 +1056,8 @@ describe('helpers', () => { identity: 1, start: 2, end: 3, - type: 'relationship', - label: 'foo', + elementType: 'relationship', + type: 'foo', properties: { bar: { srid: 1, x: 10, y: 5, z: 15 } } @@ -1077,8 +1077,8 @@ describe('helpers', () => { identity: 1, start: 2, end: 3, - type: 'relationship', - label: 'foo', + elementType: 'relationship', + type: 'foo', properties: { bar: 'P10M5DT1S' } @@ -1107,7 +1107,7 @@ describe('helpers', () => { const expected = { n1: { identity: 1, - type: 'node', + elementType: 'node', labels: ['foo'], properties: { bar: 3 @@ -1115,8 +1115,8 @@ describe('helpers', () => { }, r1: { identity: 3, - type: 'relationship', - label: 'bom', + elementType: 'relationship', + type: 'bom', start: 1, end: 2, properties: { @@ -1125,7 +1125,7 @@ describe('helpers', () => { }, n2: { identity: 2, - type: 'node', + elementType: 'node', labels: ['bam'], properties: { bar: '1970-01-01' @@ -1157,7 +1157,7 @@ describe('helpers', () => { length: 1, start: { identity: 1, - type: 'node', + elementType: 'node', labels: ['foo'], properties: { bar: 3 @@ -1165,7 +1165,7 @@ describe('helpers', () => { }, end: { identity: 2, - type: 'node', + elementType: 'node', labels: ['bam'], properties: { bar: '1970-01-01' @@ -1175,7 +1175,7 @@ describe('helpers', () => { { start: { identity: 1, - type: 'node', + elementType: 'node', labels: ['foo'], properties: { bar: 3 @@ -1183,8 +1183,8 @@ describe('helpers', () => { }, relationship: { identity: 3, - type: 'relationship', - label: 'bom', + elementType: 'relationship', + type: 'bom', start: 1, end: 2, properties: { @@ -1193,7 +1193,7 @@ describe('helpers', () => { }, end: { identity: 2, - type: 'node', + elementType: 'node', labels: ['bam'], properties: { bar: '1970-01-01'