datajunction-ui 0.0.1-a48 → 0.0.1-a49.dev2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,74 @@
1
+ import React from 'react';
2
+ import { render, screen, waitFor } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import DJClientContext from '../../../providers/djclient';
5
+ import DimensionFilter from '../DimensionFilter';
6
+
7
+ // Mock DJClientContext
8
+ const mockDJClient = {
9
+ DataJunctionAPI: {
10
+ node: jest.fn(),
11
+ nodeData: jest.fn(),
12
+ },
13
+ };
14
+
15
+ const mockDimension = {
16
+ label: 'Dimension Label [Test]',
17
+ value: 'dimension_value',
18
+ metadata: {
19
+ node_name: 'test_node',
20
+ node_display_name: 'Test Node',
21
+ },
22
+ };
23
+
24
+ const getByTextStartsWith = (container, text) => {
25
+ return Array.from(container.querySelectorAll('*')).find(element => {
26
+ return element.textContent.trim().startsWith(text);
27
+ });
28
+ };
29
+
30
+ describe('DimensionFilter', () => {
31
+ it('fetches dimension data and renders correctly', async () => {
32
+ // Mock node response
33
+ const mockNodeResponse = {
34
+ type: 'dimension',
35
+ name: 'test_node',
36
+ columns: [
37
+ {
38
+ name: 'id',
39
+ attributes: [{ attribute_type: { name: 'primary_key' } }],
40
+ },
41
+ { name: 'name', attributes: [{ attribute_type: { name: 'label' } }] },
42
+ ],
43
+ };
44
+ mockDJClient.DataJunctionAPI.node.mockResolvedValue(mockNodeResponse);
45
+
46
+ // Mock node data response
47
+ const mockNodeDataResponse = {
48
+ results: [
49
+ {
50
+ columns: [{ semantic_entity: 'id' }, { semantic_entity: 'name' }],
51
+ rows: [
52
+ [1, 'Value 1'],
53
+ [2, 'Value 2'],
54
+ ],
55
+ },
56
+ ],
57
+ };
58
+ mockDJClient.DataJunctionAPI.nodeData.mockResolvedValue(
59
+ mockNodeDataResponse,
60
+ );
61
+
62
+ const { container } = render(
63
+ <DJClientContext.Provider value={mockDJClient}>
64
+ <DimensionFilter dimension={mockDimension} onChange={jest.fn()} />
65
+ </DJClientContext.Provider>,
66
+ );
67
+
68
+ // Check if the dimension label and node display name are rendered
69
+ expect(
70
+ getByTextStartsWith(container, 'Dimension Label'),
71
+ ).toBeInTheDocument();
72
+ expect(screen.getByText('Test Node')).toBeInTheDocument();
73
+ });
74
+ });
@@ -58,7 +58,6 @@ describe('<LinkDimensionPopover />', () => {
58
58
  fireEvent.keyDown(linkDimension.firstChild, { key: 'ArrowDown' });
59
59
  fireEvent.click(screen.getByText('Dimension 1'));
60
60
  fireEvent.click(getByText('Save'));
61
- getByText('Save').click();
62
61
 
63
62
  // Expect linkDimension to be called
64
63
  await waitFor(() => {
@@ -74,7 +73,6 @@ describe('<LinkDimensionPopover />', () => {
74
73
  fireEvent.keyDown(linkDimension.firstChild, { key: 'ArrowDown' });
75
74
  fireEvent.click(screen.getByText('[Remove dimension link]'));
76
75
  fireEvent.click(getByText('Save'));
77
- getByText('Save').click();
78
76
 
79
77
  // Expect unlinkDimension to be called
80
78
  await waitFor(() => {
@@ -133,7 +131,6 @@ describe('<LinkDimensionPopover />', () => {
133
131
  fireEvent.keyDown(linkDimension.firstChild, { key: 'ArrowDown' });
134
132
  fireEvent.click(screen.getByText('Dimension 1'));
135
133
  fireEvent.click(getByText('Save'));
136
- getByText('Save').click();
137
134
 
138
135
  // Expect linkDimension to be called
139
136
  await waitFor(() => {
@@ -151,7 +148,6 @@ describe('<LinkDimensionPopover />', () => {
151
148
  fireEvent.keyDown(linkDimension.firstChild, { key: 'ArrowDown' });
152
149
  fireEvent.click(screen.getByText('[Remove Dimension]'));
153
150
  fireEvent.click(getByText('Save'));
154
- getByText('Save').click();
155
151
 
156
152
  // Expect unlinkDimension to be called
157
153
  await waitFor(() => {
@@ -23,6 +23,7 @@ describe('<NodePage />', () => {
23
23
  DataJunctionAPI: {
24
24
  node: jest.fn(),
25
25
  metric: jest.fn(),
26
+ revalidate: jest.fn().mockReturnValue({ status: 'valid' }),
26
27
  node_dag: jest.fn().mockReturnValue(mocks.mockNodeDAG),
27
28
  clientCode: jest.fn().mockReturnValue('dj_client = DJClient()'),
28
29
  columns: jest.fn(),
@@ -39,6 +40,8 @@ describe('<NodePage />', () => {
39
40
  dimensions: jest.fn(),
40
41
  setPartition: jest.fn(),
41
42
  engines: jest.fn(),
43
+ streamNodeData: jest.fn(),
44
+ nodeDimensions: jest.fn(),
42
45
  },
43
46
  };
44
47
  };
@@ -652,31 +655,110 @@ describe('<NodePage />', () => {
652
655
  );
653
656
  }, 60000);
654
657
 
655
- it('renders the NodeSQL tab', async () => {
658
+ it('renders the NodeValidate tab', async () => {
656
659
  const djClient = mockDJClient();
660
+ window.scrollTo = jest.fn();
657
661
  djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
662
+ djClient.DataJunctionAPI.nodeDimensions.mockReturnValue(
663
+ mocks.mockMetricNodeJson.dimensions,
664
+ );
658
665
  djClient.DataJunctionAPI.metric.mockReturnValue(mocks.mockMetricNode);
659
666
  djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
660
- djClient.DataJunctionAPI.sql.mockReturnValue(mocks.metricNodeColumns);
667
+ djClient.DataJunctionAPI.sql.mockReturnValue({
668
+ sql: 'SELECT * FROM testNode',
669
+ });
670
+ const streamNodeData = {
671
+ onmessage: jest.fn(),
672
+ onerror: jest.fn(),
673
+ close: jest.fn(),
674
+ };
675
+ djClient.DataJunctionAPI.streamNodeData.mockResolvedValue(streamNodeData);
676
+ djClient.DataJunctionAPI.streamNodeData.mockResolvedValueOnce({
677
+ state: 'FINISHED',
678
+ results: [
679
+ {
680
+ columns: [{ name: 'column1' }, { name: 'column2' }],
681
+ rows: [
682
+ [1, 'value1'],
683
+ [2, 'value2'],
684
+ ],
685
+ },
686
+ ],
687
+ });
688
+
661
689
  const element = (
662
690
  <DJClientContext.Provider value={djClient}>
663
691
  <NodePage />
664
692
  </DJClientContext.Provider>
665
693
  );
666
694
  render(
667
- <MemoryRouter initialEntries={['/nodes/default.num_repair_orders/sql']}>
695
+ <MemoryRouter
696
+ initialEntries={['/nodes/default.num_repair_orders/validate']}
697
+ >
668
698
  <Routes>
669
699
  <Route path="nodes/:name/:tab" element={element} />
670
700
  </Routes>
671
701
  </MemoryRouter>,
672
702
  );
703
+
704
+ await waitFor(() => {
705
+ expect(screen.getByText('Group By')).toBeInTheDocument();
706
+ expect(screen.getByText('Add Filters')).toBeInTheDocument();
707
+ expect(screen.getByText('Generated Query')).toBeInTheDocument();
708
+ expect(screen.getByText('Results')).toBeInTheDocument();
709
+ });
710
+ // Click on the 'Validate' tab
711
+ fireEvent.click(screen.getByRole('button', { name: '► Validate' }));
712
+
673
713
  await waitFor(() => {
674
- const sqlButton = screen.getByRole('button', { name: 'SQL' });
675
- sqlButton.click();
714
+ expect(djClient.DataJunctionAPI.node).toHaveBeenCalledWith(
715
+ mocks.mockMetricNode.name,
716
+ );
676
717
  expect(djClient.DataJunctionAPI.sql).toHaveBeenCalledWith(
677
- 'default.num_repair_orders',
718
+ mocks.mockMetricNode.name,
678
719
  { dimensions: [], filters: [] },
679
720
  );
721
+ expect(djClient.DataJunctionAPI.nodeDimensions).toHaveBeenCalledWith(
722
+ mocks.mockMetricNode.name,
723
+ );
724
+ });
725
+
726
+ // Click on 'Run' to run the node query
727
+ const runButton = screen.getByText('► Run');
728
+ fireEvent.click(runButton);
729
+
730
+ await waitFor(() => {
731
+ expect(djClient.DataJunctionAPI.streamNodeData).toHaveBeenCalledWith(
732
+ mocks.mockMetricNode.name,
733
+ { dimensions: [], filters: [] },
734
+ );
735
+ expect(streamNodeData.onmessage).toBeDefined();
736
+ expect(streamNodeData.onerror).toBeDefined();
737
+ });
738
+
739
+ const infoTab = screen.getByRole('button', { name: 'QueryInfo' });
740
+ const resultsTab = screen.getByText('Results');
741
+
742
+ // Initially, the Results tab should be active
743
+ expect(resultsTab).toHaveClass('active');
744
+ expect(infoTab).not.toHaveClass('active');
745
+
746
+ // Click on the Info tab first
747
+ fireEvent.click(infoTab);
748
+
749
+ await waitFor(() => {
750
+ // Now, the Info tab should be active
751
+ expect(infoTab).toHaveClass('active');
752
+ expect(resultsTab).not.toHaveClass('active');
753
+ });
754
+
755
+ // Click on the Results tab
756
+ fireEvent.click(resultsTab);
757
+
758
+ await waitFor(() => {
759
+ // Now, the Results tab should be active again
760
+ expect(resultsTab).toHaveClass('active');
761
+ expect(infoTab).not.toHaveClass('active');
680
762
  });
681
763
  });
682
764
 
@@ -8,7 +8,7 @@ import NodeColumnTab from './NodeColumnTab';
8
8
  import NodeLineage from './NodeGraphTab';
9
9
  import NodeHistory from './NodeHistory';
10
10
  import DJClientContext from '../../providers/djclient';
11
- import NodeSQLTab from './NodeSQLTab';
11
+ import NodeValidateTab from './NodeValidateTab';
12
12
  import NodeMaterializationTab from './NodeMaterializationTab';
13
13
  import ClientCodePopover from './ClientCodePopover';
14
14
  import NodesWithDimension from './NodesWithDimension';
@@ -92,9 +92,9 @@ export function NodePage() {
92
92
  display: true,
93
93
  },
94
94
  {
95
- id: 'sql',
96
- name: 'SQL',
97
- display: node?.type !== 'dimension' && node?.type !== 'source',
95
+ id: 'validate',
96
+ name: '► Validate',
97
+ display: node?.type !== 'source',
98
98
  },
99
99
  {
100
100
  id: 'materializations',
@@ -134,9 +134,8 @@ export function NodePage() {
134
134
  case 'history':
135
135
  tabToDisplay = <NodeHistory node={node} djClient={djClient} />;
136
136
  break;
137
- case 'sql':
138
- tabToDisplay =
139
- node?.type === 'metric' ? <NodeSQLTab djNode={node} /> : <br />;
137
+ case 'validate':
138
+ tabToDisplay = <NodeValidateTab node={node} djClient={djClient} />;
140
139
  break;
141
140
  case 'materializations':
142
141
  tabToDisplay = <NodeMaterializationTab node={node} djClient={djClient} />;
@@ -363,13 +363,18 @@ export const DataJunctionAPI = {
363
363
  },
364
364
 
365
365
  sql: async function (metric_name, selection) {
366
+ const params = new URLSearchParams(selection);
367
+ for (const [key, value] of Object.entries(selection)) {
368
+ if (Array.isArray(value)) {
369
+ params.delete(key);
370
+ value.forEach(v => params.append(key, v));
371
+ }
372
+ }
373
+
366
374
  return await (
367
- await fetch(
368
- `${DJ_URL}/sql/${metric_name}?` + new URLSearchParams(selection),
369
- {
370
- credentials: 'include',
371
- },
372
- )
375
+ await fetch(`${DJ_URL}/sql/${metric_name}?${params}`, {
376
+ credentials: 'include',
377
+ })
373
378
  ).json();
374
379
  },
375
380
 
@@ -432,6 +437,31 @@ export const DataJunctionAPI = {
432
437
  ).json();
433
438
  },
434
439
 
440
+ nodeData: async function (nodeName, selection = null) {
441
+ if (selection === null) {
442
+ selection = {
443
+ dimensions: [],
444
+ filters: [],
445
+ };
446
+ }
447
+ const params = new URLSearchParams(selection);
448
+ for (const [key, value] of Object.entries(selection)) {
449
+ if (Array.isArray(value)) {
450
+ params.delete(key);
451
+ value.forEach(v => params.append(key, v));
452
+ }
453
+ }
454
+ params.append('limit', '1000');
455
+ params.append('async_', 'true');
456
+
457
+ return await (
458
+ await fetch(`${DJ_URL}/data/${nodeName}?${params}`, {
459
+ credentials: 'include',
460
+ headers: { 'Cache-Control': 'max-age=86400' },
461
+ })
462
+ ).json();
463
+ },
464
+
435
465
  stream: async function (metricSelection, dimensionSelection, filters) {
436
466
  const params = new URLSearchParams();
437
467
  metricSelection.map(metric => params.append('metrics', metric));
@@ -445,6 +475,28 @@ export const DataJunctionAPI = {
445
475
  );
446
476
  },
447
477
 
478
+ streamNodeData: async function (nodeName, selection = null) {
479
+ if (selection === null) {
480
+ selection = {
481
+ dimensions: [],
482
+ filters: [],
483
+ };
484
+ }
485
+ const params = new URLSearchParams(selection);
486
+ for (const [key, value] of Object.entries(selection)) {
487
+ if (Array.isArray(value)) {
488
+ params.delete(key);
489
+ value.forEach(v => params.append(key, v));
490
+ }
491
+ }
492
+ params.append('limit', '1000');
493
+ params.append('async_', 'true');
494
+
495
+ return new EventSource(`${DJ_URL}/stream/${nodeName}?${params}`, {
496
+ withCredentials: true,
497
+ });
498
+ },
499
+
448
500
  lineage: async function (node) {},
449
501
 
450
502
  compiledSql: async function (node) {
@@ -815,7 +867,6 @@ export const DataJunctionAPI = {
815
867
  return { status: response.status, json: await response.json() };
816
868
  },
817
869
  deleteMaterialization: async function (nodeName, materializationName) {
818
- console.log('deleting materialization', nodeName, materializationName);
819
870
  const response = await fetch(
820
871
  `${DJ_URL}/nodes/${nodeName}/materializations?materialization_name=${materializationName}`,
821
872
  {
@@ -605,6 +605,50 @@ describe('DataJunctionAPI', () => {
605
605
  );
606
606
  });
607
607
 
608
+ it('calls streamNodeData correctly', () => {
609
+ const nodes = ['transform1'];
610
+ const dimensionSelection = ['dimension1', 'dimension2'];
611
+ const filters = 'sampleFilter';
612
+
613
+ DataJunctionAPI.streamNodeData(nodes[0], {
614
+ dimensions: dimensionSelection,
615
+ filters: filters,
616
+ });
617
+
618
+ const params = new URLSearchParams();
619
+ params.append('dimensions', dimensionSelection, 'filters', filters);
620
+
621
+ expect(global.EventSource).toHaveBeenCalledWith(
622
+ `${DJ_URL}/stream/transform1?filters=sampleFilter&dimensions=dimension1&dimensions=dimension2&limit=1000&async_=true`,
623
+ { withCredentials: true },
624
+ );
625
+ });
626
+
627
+ it('calls nodeData correctly', () => {
628
+ const nodes = ['transform1'];
629
+ const dimensionSelection = ['dimension1', 'dimension2'];
630
+ const filters = 'sampleFilter';
631
+ fetch.mockResponseOnce(JSON.stringify({}));
632
+
633
+ DataJunctionAPI.nodeData(nodes[0], {
634
+ dimensions: dimensionSelection,
635
+ filters: filters,
636
+ });
637
+
638
+ const params = new URLSearchParams();
639
+ params.append('dimensions', dimensionSelection, 'filters', filters);
640
+
641
+ expect(fetch).toHaveBeenCalledWith(
642
+ `${DJ_URL}/data/transform1?filters=sampleFilter&dimensions=dimension1&dimensions=dimension2&limit=1000&async_=true`,
643
+ {
644
+ credentials: 'include',
645
+ headers: {
646
+ 'Cache-Control': 'max-age=86400',
647
+ },
648
+ },
649
+ );
650
+ });
651
+
608
652
  it('calls dag correctly and processes response', async () => {
609
653
  const mockResponse = [
610
654
  {
@@ -1019,4 +1063,22 @@ describe('DataJunctionAPI', () => {
1019
1063
  },
1020
1064
  );
1021
1065
  });
1066
+
1067
+ it('calls deleteMaterialization correctly', () => {
1068
+ const nodeName = 'transform1';
1069
+ const materializationName = 'sampleMaterialization';
1070
+ fetch.mockResponseOnce(JSON.stringify({}));
1071
+
1072
+ DataJunctionAPI.deleteMaterialization(nodeName, materializationName);
1073
+ expect(fetch).toHaveBeenCalledWith(
1074
+ `${DJ_URL}/nodes/${nodeName}/materializations?materialization_name=${materializationName}`,
1075
+ {
1076
+ method: 'DELETE',
1077
+ credentials: 'include',
1078
+ headers: {
1079
+ 'Content-Type': 'application/json',
1080
+ },
1081
+ },
1082
+ );
1083
+ });
1022
1084
  });
@@ -962,6 +962,9 @@ export const mocks = {
962
962
  dimensions: [
963
963
  {
964
964
  name: 'default.date_dim.dateint',
965
+ node_name: 'default.date_dim',
966
+ node_display_name: 'Date Dim',
967
+ is_primary_key: true,
965
968
  type: 'timestamp',
966
969
  path: [
967
970
  'default.repair_order_details.repair_order_id',
@@ -971,6 +974,9 @@ export const mocks = {
971
974
  },
972
975
  {
973
976
  name: 'default.date_dim.dateint',
977
+ node_name: 'default.date_dim',
978
+ node_display_name: 'Date Dim',
979
+ is_primary_key: true,
974
980
  type: 'timestamp',
975
981
  path: [
976
982
  'default.repair_order_details.repair_order_id',
@@ -980,6 +986,9 @@ export const mocks = {
980
986
  },
981
987
  {
982
988
  name: 'default.date_dim.day',
989
+ node_name: 'default.date_dim',
990
+ node_display_name: 'Date Dim',
991
+ is_primary_key: false,
983
992
  type: 'int',
984
993
  path: [
985
994
  'default.repair_order_details.repair_order_id',
@@ -989,6 +998,9 @@ export const mocks = {
989
998
  },
990
999
  {
991
1000
  name: 'default.date_dim.day',
1001
+ node_name: 'default.date_dim',
1002
+ node_display_name: 'Date Dim',
1003
+ is_primary_key: false,
992
1004
  type: 'int',
993
1005
  path: [
994
1006
  'default.repair_order_details.repair_order_id',
@@ -998,6 +1010,9 @@ export const mocks = {
998
1010
  },
999
1011
  {
1000
1012
  name: 'default.date_dim.month',
1013
+ node_name: 'default.date_dim',
1014
+ node_display_name: 'Date Dim',
1015
+ is_primary_key: false,
1001
1016
  type: 'int',
1002
1017
  path: [
1003
1018
  'default.repair_order_details.repair_order_id',
@@ -1007,6 +1022,9 @@ export const mocks = {
1007
1022
  },
1008
1023
  {
1009
1024
  name: 'default.date_dim.month',
1025
+ node_name: 'default.date_dim',
1026
+ node_display_name: 'Date Dim',
1027
+ is_primary_key: false,
1010
1028
  type: 'int',
1011
1029
  path: [
1012
1030
  'default.repair_order_details.repair_order_id',
@@ -1016,6 +1034,9 @@ export const mocks = {
1016
1034
  },
1017
1035
  {
1018
1036
  name: 'default.date_dim.year',
1037
+ node_name: 'default.date_dim',
1038
+ node_display_name: 'Date Dim',
1039
+ is_primary_key: false,
1019
1040
  type: 'int',
1020
1041
  path: [
1021
1042
  'default.repair_order_details.repair_order_id',
@@ -1025,6 +1046,9 @@ export const mocks = {
1025
1046
  },
1026
1047
  {
1027
1048
  name: 'default.date_dim.year',
1049
+ node_name: 'default.date_dim',
1050
+ node_display_name: 'Date Dim',
1051
+ is_primary_key: false,
1028
1052
  type: 'int',
1029
1053
  path: [
1030
1054
  'default.repair_order_details.repair_order_id',
@@ -1034,6 +1058,9 @@ export const mocks = {
1034
1058
  },
1035
1059
  {
1036
1060
  name: 'default.hard_hat.address',
1061
+ node_name: 'default.hard_hat',
1062
+ node_display_name: 'Hard hat',
1063
+ is_primary_key: false,
1037
1064
  type: 'string',
1038
1065
  path: [
1039
1066
  'default.repair_order_details.repair_order_id',
@@ -1042,6 +1069,9 @@ export const mocks = {
1042
1069
  },
1043
1070
  {
1044
1071
  name: 'default.hard_hat.birth_date',
1072
+ node_name: 'default.hard_hat',
1073
+ node_display_name: 'Hard hat',
1074
+ is_primary_key: false,
1045
1075
  type: 'date',
1046
1076
  path: [
1047
1077
  'default.repair_order_details.repair_order_id',
@@ -1050,6 +1080,9 @@ export const mocks = {
1050
1080
  },
1051
1081
  {
1052
1082
  name: 'default.hard_hat.city',
1083
+ node_name: 'default.hard_hat',
1084
+ node_display_name: 'Hard hat',
1085
+ is_primary_key: false,
1053
1086
  type: 'string',
1054
1087
  path: [
1055
1088
  'default.repair_order_details.repair_order_id',
@@ -1058,6 +1091,9 @@ export const mocks = {
1058
1091
  },
1059
1092
  {
1060
1093
  name: 'default.hard_hat.contractor_id',
1094
+ node_name: 'default.hard_hat',
1095
+ node_display_name: 'Hard hat',
1096
+ is_primary_key: false,
1061
1097
  type: 'int',
1062
1098
  path: [
1063
1099
  'default.repair_order_details.repair_order_id',
@@ -1066,6 +1102,9 @@ export const mocks = {
1066
1102
  },
1067
1103
  {
1068
1104
  name: 'default.hard_hat.country',
1105
+ node_name: 'default.hard_hat',
1106
+ node_display_name: 'Hard hat',
1107
+ is_primary_key: false,
1069
1108
  type: 'string',
1070
1109
  path: [
1071
1110
  'default.repair_order_details.repair_order_id',
@@ -1074,6 +1113,9 @@ export const mocks = {
1074
1113
  },
1075
1114
  {
1076
1115
  name: 'default.hard_hat.first_name',
1116
+ node_name: 'default.hard_hat',
1117
+ node_display_name: 'Hard hat',
1118
+ is_primary_key: false,
1077
1119
  type: 'string',
1078
1120
  path: [
1079
1121
  'default.repair_order_details.repair_order_id',
@@ -1082,6 +1124,9 @@ export const mocks = {
1082
1124
  },
1083
1125
  {
1084
1126
  name: 'default.hard_hat.hard_hat_id',
1127
+ node_name: 'default.hard_hat',
1128
+ node_display_name: 'Hard hat',
1129
+ is_primary_key: true,
1085
1130
  type: 'int',
1086
1131
  path: [
1087
1132
  'default.repair_order_details.repair_order_id',
@@ -1090,6 +1135,9 @@ export const mocks = {
1090
1135
  },
1091
1136
  {
1092
1137
  name: 'default.hard_hat.hire_date',
1138
+ node_name: 'default.hard_hat',
1139
+ node_display_name: 'Hard hat',
1140
+ is_primary_key: false,
1093
1141
  type: 'date',
1094
1142
  path: [
1095
1143
  'default.repair_order_details.repair_order_id',
@@ -1098,6 +1146,9 @@ export const mocks = {
1098
1146
  },
1099
1147
  {
1100
1148
  name: 'default.hard_hat.last_name',
1149
+ node_name: 'default.hard_hat',
1150
+ node_display_name: 'Hard hat',
1151
+ is_primary_key: false,
1101
1152
  type: 'string',
1102
1153
  path: [
1103
1154
  'default.repair_order_details.repair_order_id',
@@ -1106,6 +1157,9 @@ export const mocks = {
1106
1157
  },
1107
1158
  {
1108
1159
  name: 'default.hard_hat.manager',
1160
+ node_name: 'default.hard_hat',
1161
+ node_display_name: 'Hard hat',
1162
+ is_primary_key: false,
1109
1163
  type: 'int',
1110
1164
  path: [
1111
1165
  'default.repair_order_details.repair_order_id',
@@ -1114,6 +1168,9 @@ export const mocks = {
1114
1168
  },
1115
1169
  {
1116
1170
  name: 'default.hard_hat.postal_code',
1171
+ node_name: 'default.hard_hat',
1172
+ node_display_name: 'Hard hat',
1173
+ is_primary_key: false,
1117
1174
  type: 'string',
1118
1175
  path: [
1119
1176
  'default.repair_order_details.repair_order_id',
@@ -1122,6 +1179,9 @@ export const mocks = {
1122
1179
  },
1123
1180
  {
1124
1181
  name: 'default.hard_hat.state',
1182
+ node_name: 'default.hard_hat',
1183
+ node_display_name: 'Hard hat',
1184
+ is_primary_key: false,
1125
1185
  type: 'string',
1126
1186
  path: [
1127
1187
  'default.repair_order_details.repair_order_id',
@@ -1130,6 +1190,9 @@ export const mocks = {
1130
1190
  },
1131
1191
  {
1132
1192
  name: 'default.hard_hat.title',
1193
+ node_name: 'default.hard_hat',
1194
+ node_display_name: 'Hard hat',
1195
+ is_primary_key: false,
1133
1196
  type: 'string',
1134
1197
  path: [
1135
1198
  'default.repair_order_details.repair_order_id',