Skip to content
Snippets Groups Projects
Commit f2bfcffe authored by Behrisch, M. (Michael)'s avatar Behrisch, M. (Michael)
Browse files

Merge branch 'feat/refactor-schema-panel' into develop

parents 0bbc7c35 378fcce2
No related branches found
No related tags found
1 merge request!13merge develop into main
Showing
with 959 additions and 82 deletions
{
"configurations": [
{
"name": "Debug Jest Tests",
"type": "node",
"request": "launch",
"runtimeArgs": [
"--inspect-brk",
"${workspaceRoot}/node_modules/jest/bin/jest.js",
"--runInBand"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"port": 9229
},
{
// Requires the extension Debugger for Chrome: https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome
"type": "chrome",
"request": "launch",
"name": "Storybook Debug",
"breakOnLoad": true,
"url": "http://localhost:4400/?path=/story/",
"sourceMaps": true,
"webRoot": "${workspaceFolder}",
"sourceMapPathOverrides": {
"webpack:///*": "${webRoot}/*",
"webpack:///./*": "${webRoot}/*",
"webpack:///src/*": "${webRoot}/*",
"webpack:///./~/*": "${webRoot}/node_modules/*"
}
}
]
}
......@@ -20,6 +20,8 @@
"vis-nl",
"vis-paoh",
"vis-schema",
"storybook"
]
"storybook",
"store"
],
"jest.jestCommandLine": "nx affected:test"
}
import {
handleSchemaLayout,
// handleSchemaLayout,
parseSchemaFromBackend
} from '@graphpolaris/schema/schema-usecases';
import {
......@@ -143,5 +143,5 @@ TestWithSchema.play = async () => {
});
//dispatch(setSchema(schema));
handleSchemaLayout(schema);
// handleSchemaLayout(schema);
};
import { useSchema } from '@graphpolaris/shared/data-access/store';
//import { useEffect } from 'react';
import ReactFlow from 'react-flow-renderer';
import styled from 'styled-components';
import { createReactFlowElements } from '@graphpolaris/schema/schema-usecases';
import { useEffect, useState } from 'react';
interface Props {
content: string;
}
const Div = styled.div`
background-color: red;
font: 'Arial';
width: 300px;
height: 600px;
`;
const initialElements = [
{
id: '1',
type: 'input',
data: { label: 'Input Node' },
position: { x: 250, y: 25 },
},
{
id: '2',
data: { label: 'Another Node' },
position: { x: 100, y: 125 },
},
];
const Schema = (props: Props) => {
const dbschema = useSchema();
console.log(dbschema);
const flowElements = createReactFlowElements(dbschema);
console.log(flowElements);
// const [dbschema, setSchema] = useState(useSchema());
// const [flowElements, setFlowElements] = useState(initialElements);
// In case the schema is updated
// useEffect(() => {
// const flowElements = createReactFlowElements(dbschema);
// console.log('update schema useEffect');
// }, [dbschema]);
console.log(dbschema);
// console.log(dbschema);
return (
<Div>
<p>hey</p>
<ReactFlow elements={createReactFlowElements(dbschema)} />
<p>hoi</p>
</Div>
<div
style={{
width: '100%',
height: '100%',
}}
>
<ReactFlow elements={createReactFlowElements(dbschema)} />
</div>
);
};
......
......@@ -10,7 +10,7 @@ export default {
* See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
* to learn how to generate automatic titles
*/
title: 'SemanticSubstrates',
title: 'SemanticSubstrates',
component: SemanticSubstrates,
decorators: [
(story) => <div style={{ padding: '3rem' }}>{story()}</div>,
......@@ -23,25 +23,25 @@ export default {
],
} as ComponentMeta<typeof SemanticSubstrates>;
declare module '@mui/material/styles' {
interface Theme {
graphpolaris: {
danger: string;
dataPointColors: string[];
};
}
// allow configuration using `createTheme`
interface ThemeOptions {
graphpolaris: {
danger: string;
dataPointColors: string[];
};
}
}
// declare module '@mui/material/styles' {
// interface Theme {
// palette: {
// primary: string;
// dataPointColors: string[];
// };
// }
// // allow configuration using `createTheme`
// interface ThemeOptions {
// palette: {
// danger: string;
// dataPointColors: string[];
// };
// }
// }
const mockTheme = createTheme({
graphpolaris: {
danger: 'orange',
palette: {
// danger: 'orange',
dataPointColors: ['blue', 'green'],
},
});
......
......@@ -24,8 +24,7 @@ const SemanticSubstrates = () => {
<Box
sx={{
// Using our custom palette dataPointColors property, cast theme to any because Theme doesn't know of this custom property
backgroundColor: (theme: Theme) =>
theme.graphpolaris.dataPointColors[0],
backgroundColor: (theme: Theme) => theme.palette.dataPointColors[0],
}}
>
<h1>semantic substrates h1</h1>
......@@ -44,30 +43,30 @@ const SemanticSubstrates = () => {
dispatch(
changeDataPointColors([
v.currentTarget.value,
...theme.graphpolaris.dataPointColors.slice(1),
...theme.palette.dataPointColors.slice(1),
])
)
}
type="color"
id="head"
name="head"
value={theme.graphpolaris.dataPointColors[0]}
value={theme.palette.dataPointColors[0]}
/>
Change dataPointColors reflects to local
<input
onChange={(v) =>
dispatch(
changeDataPointColors([
theme.graphpolaris.dataPointColors[0],
theme.palette.dataPointColors[0],
v.currentTarget.value,
...theme.graphpolaris.dataPointColors.slice(2),
...theme.palette.dataPointColors.slice(2),
])
)
}
type="color"
id="head"
name="head"
value={theme.graphpolaris.dataPointColors[1]}
value={theme.palette.dataPointColors[1]}
/>
Change dataPointColors reflects to global
</>
......
export * from './lib/schema-schema-usecases';
export * from './lib/schema-usecases';
\ No newline at end of file
import { schemaSchemaUsecases } from './schema-schema-usecases';
describe('schemaSchemaUsecases', () => {
it('should work', () => {
expect(schemaSchemaUsecases()).toEqual('schema-schema-usecases');
});
});
import { SchemaFromBackend } from '@graphpolaris/shared/data-access/store';
import { MultiGraph } from 'graphology';
import { parse } from 'path/posix';
import { parseSchemaFromBackend } from '..';
import { Attributes } from 'graphology-types';
import {
movieSchema,
northWindSchema,
simpleSchema,
twitterSchema,
} from 'libs/shared/mock-data/src';
describe('SchemaUsecases', () => {
test.each([
{ data: simpleSchema },
{ data: movieSchema },
{ data: northWindSchema },
{ data: twitterSchema },
])('parseSchemaFromBackend parsing should work', ({ data }) => {
// console.log('testinput', input);
const parsed = parseSchemaFromBackend(data as SchemaFromBackend);
expect(parsed).toBeDefined();
let parsedNodeAttributes: Attributes[] = [];
parsed.forEachNode((node, attr) => {
// console.log('Node', node, attr);
parsedNodeAttributes.push(attr.attributes);
});
let parsedEdgeAttributes: Attributes = [];
parsed.forEachEdge((edge, attr, source, target, sa, ta, undirected) => {
// console.log('Edge', edge, attr, source, target, sa, ta, undirected);
parsedEdgeAttributes.push(attr.attribute);
});
expect(data.nodes.length).toEqual(parsed.order);
expect(data.edges.length).toEqual(parsed.size);
let inputNodeAttributes: Attributes = [];
data.nodes.forEach((node) => {
inputNodeAttributes.push(node.attributes as Attributes);
});
let inputEdgeAttributes: Attributes = [];
data.edges.forEach((edge) => {
inputEdgeAttributes.push(edge.attributes as Attributes);
});
expect(inputNodeAttributes).toEqual(parsedNodeAttributes);
expect(inputEdgeAttributes).toEqual(parsedEdgeAttributes);
});
it('should export and reimport', () => {
const parsed = parseSchemaFromBackend(simpleSchema as SchemaFromBackend);
const reload = MultiGraph.from(parsed.export());
expect(parsed).toStrictEqual(reload);
});
test.each([
{ data: simpleSchema },
{ data: movieSchema },
{ data: northWindSchema },
{ data: twitterSchema },
])('should load my test json $data', ({ data }) => {
expect(data).toBeDefined();
expect(data.nodes).toBeDefined();
});
});
import Graph from 'graphology';
import cytoscape from 'cytoscape'; // eslint-disable-line
import Graph, { MultiGraph } from 'graphology';
// import cytoscape from 'cytoscape'; // eslint-disable-line
import { setSchema, store } from '@graphpolaris/shared/data-access/store';
import { Elements, Node, Edge } from 'react-flow-renderer';
import { SchemaFromBackend } from '@graphpolaris/shared/data-access/store';
import { Attributes } from 'graphology-types';
type CytoNode = {
data: {
......@@ -17,122 +18,122 @@ type CytoNode = {
};
};
// Layouts a given schema
export function handleSchemaLayout(graph: Graph): void {
const layout = createSchemaLayout(graph);
// // Layouts a given schema
// export function handleSchemaLayout(graph: Graph): void {
// const layout = createSchemaLayout(graph);
layout.then((cy) => {
//cy.cy.elements().forEach((elem) => {
cy.cy.nodes().forEach((elem) => {
const position = elem.position();
console.log(elem.id());
// layout.then((cy) => {
// //cy.cy.elements().forEach((elem) => {
// cy.cy.nodes().forEach((elem: any) => {
// const position = elem.position();
// console.log(elem.id());
graph.setNodeAttribute(elem.id(), 'x', position.x);
graph.setNodeAttribute(elem.id(), 'y', position.y);
});
// graph.setNodeAttribute(elem.id(), 'x', position.x);
// graph.setNodeAttribute(elem.id(), 'y', position.y);
// });
store.dispatch(setSchema(graph));
});
}
// store.dispatch(setSchema(graph));
// });
// }
// Creates a schema layout (async)
function createSchemaLayout(graph: Graph): Promise<cytoscape.EventObject> {
const cytonodes: CytoNode[] = trimSchema(graph);
// // Creates a schema layout (async)
// function createSchemaLayout(graph: Graph): Promise<cytoscape.EventObject> {
// const cytonodes: CytoNode[] = trimSchema(graph);
const cy = cytoscape({
elements: cytonodes,
});
// const cy = cytoscape({
// elements: cytonodes,
// });
const options = {
name: 'cose',
// const options = {
// name: 'cose',
// Whether to animate while running the layout
// true : Animate continuously as the layout is running
// false : Just show the end result
// 'end' : Animate with the end result, from the initial positions to the end positions
animate: true,
// // Whether to animate while running the layout
// // true : Animate continuously as the layout is running
// // false : Just show the end result
// // 'end' : Animate with the end result, from the initial positions to the end positions
// animate: true,
// Easing of the animation for animate:'end'
animationEasing: undefined,
// // Easing of the animation for animate:'end'
// animationEasing: undefined,
// The duration of the animation for animate:'end'
animationDuration: undefined,
// // The duration of the animation for animate:'end'
// animationDuration: undefined,
// A function that determines whether the node should be animated
// All nodes animated by default on animate enabled
// Non-animated nodes are positioned immediately when the layout starts
// animateFilter: function (node: any, i: any) {
// return true;
// },
// // A function that determines whether the node should be animated
// // All nodes animated by default on animate enabled
// // Non-animated nodes are positioned immediately when the layout starts
// // animateFilter: function (node: any, i: any) {
// // return true;
// // },
// The layout animates only after this many milliseconds for animate:true
// (prevents flashing on fast runs)
animationThreshold: 250,
// // The layout animates only after this many milliseconds for animate:true
// // (prevents flashing on fast runs)
// animationThreshold: 250,
// Number of iterations between consecutive screen positions update
refresh: 20,
// // Number of iterations between consecutive screen positions update
// refresh: 20,
// Whether to fit the network view after when done
fit: true,
// // Whether to fit the network view after when done
// fit: true,
// Padding on fit
padding: 30,
// // Padding on fit
// padding: 30,
// Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
boundingBox: undefined,
// // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
// boundingBox: undefined,
// Excludes the label when calculating node bounding boxes for the layout algorithm
nodeDimensionsIncludeLabels: false,
// // Excludes the label when calculating node bounding boxes for the layout algorithm
// nodeDimensionsIncludeLabels: false,
// Randomize the initial positions of the nodes (true) or use existing positions (false)
randomize: false,
// // Randomize the initial positions of the nodes (true) or use existing positions (false)
// randomize: false,
// Extra spacing between components in non-compound graphs
componentSpacing: 200, // 40
// // Extra spacing between components in non-compound graphs
// componentSpacing: 200, // 40
// Node repulsion (non overlapping) multiplier
nodeRepulsion: function (node: any) {
return 2048;
},
// // Node repulsion (non overlapping) multiplier
// nodeRepulsion: function (node: any) {
// return 2048;
// },
// Node repulsion (overlapping) multiplier
nodeOverlap: 4,
// // Node repulsion (overlapping) multiplier
// nodeOverlap: 4,
// Ideal edge (non nested) length
idealEdgeLength: function (edge: any) {
return 32;
},
// // Ideal edge (non nested) length
// idealEdgeLength: function (edge: any) {
// return 32;
// },
// Divisor to compute edge forces
edgeElasticity: function (edge: any) {
return 32;
},
// // Divisor to compute edge forces
// edgeElasticity: function (edge: any) {
// return 32;
// },
// Nesting factor (multiplier) to compute ideal edge length for nested edges
nestingFactor: 1.2,
// // Nesting factor (multiplier) to compute ideal edge length for nested edges
// nestingFactor: 1.2,
// Gravity force (constant)
gravity: 1,
// // Gravity force (constant)
// gravity: 1,
// Maximum number of iterations to perform
numIter: 1000,
// // Maximum number of iterations to perform
// numIter: 1000,
// Initial temperature (maximum node displacement)
initialTemp: 1000,
// // Initial temperature (maximum node displacement)
// initialTemp: 1000,
// Cooling factor (how the temperature is reduced between consecutive iterations
coolingFactor: 0.99,
// // Cooling factor (how the temperature is reduced between consecutive iterations
// coolingFactor: 0.99,
// Lower temperature threshold (below this point the layout will end)
minTemp: 1.0,
};
// // Lower temperature threshold (below this point the layout will end)
// minTemp: 1.0,
// };
const layout = cy.layout(options);
// const layout = cy.layout(options);
layout.run();
// layout.run();
return layout.pon('layoutstop');
}
// return layout.pon('layoutstop');
// }
// Takes the schema as input and creates a list of nodes and edges in a format that the layouting algorithm can use.
function trimSchema(graph: Graph): CytoNode[] {
......@@ -157,7 +158,7 @@ function trimSchema(graph: Graph): CytoNode[] {
export function createReactFlowElements(graph: Graph): Elements<Node | Edge> {
const initialElements: Elements<Node | Edge> = [];
graph.forEachNode((node, attributes) => {
graph.forEachNode((node: string, attributes: Attributes): void => {
const newNode: Node = {
id: node,
data: {
......@@ -168,7 +169,7 @@ export function createReactFlowElements(graph: Graph): Elements<Node | Edge> {
initialElements.push(newNode);
});
graph.forEachEdge((edge, _attributes, source, target) => {
graph.forEachEdge((edge, _attributes, source, target): void => {
const newEdge: Edge = {
id: edge,
source: source,
......@@ -185,12 +186,12 @@ export function parseSchemaFromBackend(
): Graph {
const { nodes, edges } = schemaFromBackend;
// Instantiate a directed graph that allows self loops and parallel edges
const schema = new Graph({ allowSelfLoops: true, multi: true });
console.log('Updating schema');
const schemaGraph = new MultiGraph({ allowSelfLoops: true });
// console.log('parsing schema');
// The graph schema needs a node for each node AND edge. These need then be connected
nodes.forEach((node) => {
schema.addNode(node.name, {
schemaGraph.addNode(node.name, {
name: node.name,
attributes: node.attributes,
x: 0,
......@@ -200,22 +201,12 @@ export function parseSchemaFromBackend(
// The name of the edge will be name + from + to, since edge names are not unique
edges.forEach((edge) => {
const edgeID = edge.name + edge.from + edge.to;
const edgeID = [edge.name, '_', edge.from, edge.to].join(''); //ensure that all interpreted as string
// This node is the actual edge
schema.addNode(edgeID, {
name: edge.name,
attributes: edge.attributes,
from: edge.from,
to: edge.to,
collection: edge.collection,
x: 0,
y: 0,
schemaGraph.addDirectedEdgeWithKey(edgeID, edge.from, edge.to, {
attribute: edge.attributes,
});
// These lines are simply for keeping the schema together
schema.addDirectedEdgeWithKey(edgeID + 'f', edge.from, edgeID);
schema.addDirectedEdgeWithKey(edgeID + 't', edgeID, edge.to);
});
return schema;
return schemaGraph;
}
......@@ -14,6 +14,7 @@
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
"noFallthroughCasesInSwitch": true,
"resolveJsonModule": true
}
}
import Graph from 'graphology';
import AbstractGraph, { DirectedGraph, MultiGraph } from 'graphology';
import { useSchema } from '..';
import reducer, { selectSchema, setSchema, initialState } from './schemaSlice';
// import { deleteBook, updateBook, addNewBook } from '../redux/bookSlice';
import { store } from './store';
describe('SchemaSlice Tests', () => {
it('should make a graphology graph', () => {
const graph = new MultiGraph({ allowSelfLoops: true });
expect(graph);
const graph2 = new MultiGraph();
expect(graph2);
const exported = graph.export();
expect(exported);
});
it('export and reimport equality check for graphology graph', () => {
const graph = new MultiGraph({ allowSelfLoops: true });
expect(graph);
const exported = graph.export();
expect(exported);
const graphReloaded = MultiGraph.from(exported);
expect(graphReloaded).toStrictEqual(graph);
});
it('get the initial state', () => {
expect(initialState);
});
it('should return the initial state', () => {
let state = store.getState();
const schema = state.schema;
expect(schema);
const graph = MultiGraph.from(schema.graphologySerialized);
// console.log(graph);
// console.log(initialState);
expect(graph);
});
// it('should handle a todo being added to an empty list', () => {
// let state = store.getState().schema;
// let schema = useSchema();
// // const unchangedBook = state.bookList.find((book) => book.id === '1');
// // expect(unchangedBook?.title).toBe('1984');
// // expect(unchangedBook?.author).toBe('George Orwell');
// // store.dispatch(updateBook({ id: '1', title: '1985', author: 'George Bush' }));
// // state = store.getState().book;
// // let changeBook = state.bookList.find((book) => book.id === '1');
// // expect(changeBook?.title).toBe('1985');
// // expect(changeBook?.author).toBe('George Bush');
// // store.dispatch(
// // updateBook({ id: '1', title: '1984', author: 'George Orwell' })
// // );
// // state = store.getState().book;
// // const backToUnchangedBook = state.bookList.find((book) => book.id === '1');
// // expect(backToUnchangedBook).toEqual(unchangedBook);
// // ]);
// });
});
// test('Deletes a book from list with id', () => {
// let state = store.getState().book;
// const initialBookCount = state.bookList.length;
// store.dispatch(deleteBook({ id: '1' }));
// state = store.getState().book;
// expect(state.bookList.length).toBeLessThan(initialBookCount); // Checking if new length smaller than inital length, which is 3
// });
// test('Adds a new book', () => {
// let state = store.getState().book;
// const initialBookCount = state.bookList.length;
// store.dispatch(
// addNewBook({ id: '4', author: 'Tester', title: 'Testers manual' })
// );
// state = store.getState().book;
// const newlyAddedBook = state.bookList.find((book) => book.id === '4');
// expect(newlyAddedBook?.author).toBe('Tester');
// expect(newlyAddedBook?.title).toBe('Testers manual');
// expect(state.bookList.length).toBeGreaterThan(initialBookCount);
// });
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import type { RootState } from './store';
import Graph from 'graphology';
import Graph, { MultiGraph } from 'graphology';
/*************** schema format from the backend *************** */
// TODO: should probably not live here
......@@ -34,7 +34,7 @@ export type Edge = {
// Define the initial state using that type
export const initialState = {
graphologySerialized: new Graph().export(),
graphologySerialized: new MultiGraph().export(),
};
export const schemaSlice = createSlice({
......@@ -43,6 +43,8 @@ export const schemaSlice = createSlice({
initialState,
reducers: {
setSchema: (state, action: PayloadAction<Graph>) => {
console.log('setSchema', action);
state.graphologySerialized = action.payload.export();
},
......@@ -52,38 +54,38 @@ export const schemaSlice = createSlice({
) => {
const { nodes, edges } = action.payload;
// Instantiate a directed graph that allows self loops and parallel edges
const schema = new Graph({ allowSelfLoops: true, multi: true });
const schema = new MultiGraph({ allowSelfLoops: true });
console.log('Updating schema');
// The graph schema needs a node for each node AND edge. These need then be connected
nodes.forEach((node) => {
schema.addNode(node.name, {
name: node.name,
attributes: node.attributes,
x: 0,
y: 0,
});
});
// The name of the edge will be name + from + to, since edge names are not unique
edges.forEach((edge) => {
const edgeID = edge.name + edge.from + edge.to;
// This node is the actual edge
schema.addNode(edgeID, {
name: edge.name,
attributes: edge.attributes,
from: edge.from,
to: edge.to,
collection: edge.collection,
x: 0,
y: 0,
});
// These lines are simply for keeping the schema together
schema.addDirectedEdgeWithKey(edgeID + 'f', edge.from, edgeID);
schema.addDirectedEdgeWithKey(edgeID + 't', edgeID, edge.to);
});
// nodes.forEach((node) => {
// schema.addNode(node.name, {
// name: node.name,
// attributes: node.attributes,
// x: 0,
// y: 0,
// });
// });
// // The name of the edge will be name + from + to, since edge names are not unique
// edges.forEach((edge) => {
// const edgeID = edge.name + edge.from + edge.to;
// // This node is the actual edge
// schema.addNode(edgeID, {
// name: edge.name,
// attributes: edge.attributes,
// from: edge.from,
// to: edge.to,
// collection: edge.collection,
// x: 0,
// y: 0,
// });
// // These lines are simply for keeping the schema together
// schema.addDirectedEdgeWithKey(edgeID + 'f', edge.from, edgeID);
// schema.addDirectedEdgeWithKey(edgeID + 't', edgeID, edge.to);
// });
state.graphologySerialized = schema.export();
},
......
{
"extends": ["../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
# shared-mock-data
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test shared-mock-data` to execute the unit tests via [Jest](https://jestjs.io).
module.exports = {
displayName: 'shared-mock-data',
preset: '../../../jest.preset.js',
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
},
},
transform: {
'^.+\\.[tj]s$': 'ts-jest',
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../../coverage/libs/shared/mock-data',
};
{
"root": "libs/shared/mock-data",
"sourceRoot": "libs/shared/mock-data/src",
"projectType": "library",
"targets": {
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["libs/shared/mock-data/**/*.ts"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/libs/shared/mock-data"],
"options": {
"jestConfig": "libs/shared/mock-data/jest.config.js",
"passWithNoTests": true
}
},
"version": {
"executor": "@jscutlery/semver:version",
"options": {
"commitMessageFormat": "chore(${projectName}): release version ${version}"
}
}
},
"tags": []
}
export * from './schema/simple';
export * from './schema/moviesSchema';
export * from './schema/northwindSchema';
export * from './schema/twitterSchema';
export const movieSchema = {
"nodes": [
{
"name": "Movie",
"attributes": [
{
"name": "tagline",
"type": "string"
},
{
"name": "title",
"type": "string"
},
{
"name": "released",
"type": "int"
},
{
"name": "votes",
"type": "int"
}
]
},
{
"name": "Person",
"attributes": [
{
"name": "born",
"type": "int"
},
{
"name": "name",
"type": "string"
}
]
}
],
"edges": [
{
"name": "ACTED_IN",
"collection": "ACTED_IN",
"from": "Person",
"to": "Movie",
"attributes": [
{
"name": "roles",
"type": "string"
}
]
},
{
"name": "REVIEWED",
"collection": "REVIEWED",
"from": "Person",
"to": "Movie",
"attributes": [
{
"name": "summary",
"type": "string"
},
{
"name": "rating",
"type": "int"
}
]
},
{
"name": "PRODUCED",
"collection": "PRODUCED",
"from": "Person",
"to": "Movie",
"attributes": []
},
{
"name": "WROTE",
"collection": "WROTE",
"from": "Person",
"to": "Movie",
"attributes": []
},
{
"name": "FOLLOWS",
"collection": "FOLLOWS",
"from": "Person",
"to": "Person",
"attributes": []
},
{
"name": "DIRECTED",
"collection": "DIRECTED",
"from": "Person",
"to": "Movie",
"attributes": []
}
]
}
\ No newline at end of file
export const northWindSchema = {
"nodes": [
{
"name": "Order",
"attributes": [
{
"name": "customerID",
"type": "string"
},
{
"name": "shipCity",
"type": "string"
},
{
"name": "orderID",
"type": "string"
},
{
"name": "freight",
"type": "string"
},
{
"name": "requiredDate",
"type": "string"
},
{
"name": "employeeID",
"type": "string"
},
{
"name": "shipName",
"type": "string"
},
{
"name": "shipPostalCode",
"type": "string"
},
{
"name": "orderDate",
"type": "string"
},
{
"name": "shipRegion",
"type": "string"
},
{
"name": "shipCountry",
"type": "string"
},
{
"name": "shippedDate",
"type": "string"
},
{
"name": "shipVia",
"type": "string"
},
{
"name": "shipAddress",
"type": "string"
}
]
},
{
"name": "Category",
"attributes": [
{
"name": "categoryID",
"type": "string"
},
{
"name": "description",
"type": "string"
},
{
"name": "categoryName",
"type": "string"
},
{
"name": "picture",
"type": "string"
}
]
},
{
"name": "Customer",
"attributes": [
{
"name": "country",
"type": "string"
},
{
"name": "address",
"type": "string"
},
{
"name": "contactTitle",
"type": "string"
},
{
"name": "city",
"type": "string"
},
{
"name": "phone",
"type": "string"
},
{
"name": "contactName",
"type": "string"
},
{
"name": "postalCode",
"type": "string"
},
{
"name": "companyName",
"type": "string"
},
{
"name": "fax",
"type": "string"
},
{
"name": "region",
"type": "string"
},
{
"name": "customerID",
"type": "string"
}
]
},
{
"name": "Product",
"attributes": [
{
"name": "reorderLevel",
"type": "int"
},
{
"name": "unitsInStock",
"type": "int"
},
{
"name": "unitPrice",
"type": "float"
},
{
"name": "supplierID",
"type": "string"
},
{
"name": "productID",
"type": "string"
},
{
"name": "discontinued",
"type": "bool"
},
{
"name": "quantityPerUnit",
"type": "string"
},
{
"name": "categoryID",
"type": "string"
},
{
"name": "unitsOnOrder",
"type": "int"
},
{
"name": "productName",
"type": "string"
}
]
},
{
"name": "Supplier",
"attributes": [
{
"name": "supplierID",
"type": "string"
},
{
"name": "country",
"type": "string"
},
{
"name": "address",
"type": "string"
},
{
"name": "contactTitle",
"type": "string"
},
{
"name": "city",
"type": "string"
},
{
"name": "phone",
"type": "string"
},
{
"name": "contactName",
"type": "string"
},
{
"name": "postalCode",
"type": "string"
},
{
"name": "companyName",
"type": "string"
},
{
"name": "fax",
"type": "string"
},
{
"name": "region",
"type": "string"
},
{
"name": "homePage",
"type": "string"
}
]
}
],
"edges": [
{
"name": "ORDERS",
"collection": "ORDERS",
"from": "Order",
"to": "Product",
"attributes": [
{
"name": "unitPrice",
"type": "string"
},
{
"name": "productID",
"type": "string"
},
{
"name": "orderID",
"type": "string"
},
{
"name": "discount",
"type": "string"
},
{
"name": "quantity",
"type": "int"
}
]
},
{
"name": "PART_OF",
"collection": "PART_OF",
"from": "Product",
"to": "Category",
"attributes": []
},
{
"name": "SUPPLIES",
"collection": "SUPPLIES",
"from": "Supplier",
"to": "Product",
"attributes": []
},
{
"name": "PURCHASED",
"collection": "PURCHASED",
"from": "Customer",
"to": "Order",
"attributes": []
}
]
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment