datajunction-ui 0.0.14 → 0.0.16

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.
@@ -776,7 +776,7 @@ describe('DataJunctionAPI', () => {
776
776
  'Content-Type': 'application/json',
777
777
  },
778
778
  body: JSON.stringify({
779
- dimensionNode: dimensionNode,
779
+ dimension_node: dimensionNode,
780
780
  role: null,
781
781
  }),
782
782
  method: 'DELETE',
@@ -794,10 +794,10 @@ describe('DataJunctionAPI', () => {
794
794
  'Content-Type': 'application/json',
795
795
  },
796
796
  body: JSON.stringify({
797
- dimensionNode: dimensionNode,
798
- joinType: null,
799
- joinOn: joinOn,
800
- joinCardinality: null,
797
+ dimension_node: dimensionNode,
798
+ join_type: null,
799
+ join_on: joinOn,
800
+ join_cardinality: null,
801
801
  role: null,
802
802
  }),
803
803
  method: 'POST',
@@ -1569,4 +1569,550 @@ describe('DataJunctionAPI', () => {
1569
1569
  );
1570
1570
  expect(result).toEqual({ status: 200, json: mockResponse });
1571
1571
  });
1572
+
1573
+ // Reference dimension links
1574
+ it('calls addReferenceDimensionLink correctly', async () => {
1575
+ const mockResponse = { message: 'Success' };
1576
+ fetch.mockResponseOnce(JSON.stringify(mockResponse));
1577
+ const result = await DataJunctionAPI.addReferenceDimensionLink(
1578
+ 'default.node1',
1579
+ 'column1',
1580
+ 'default.dimension1',
1581
+ 'dimension_col',
1582
+ );
1583
+ expect(fetch).toHaveBeenCalledWith(
1584
+ `${DJ_URL}/nodes/default.node1/columns/column1/link?dimension_node=default.dimension1&dimension_column=dimension_col`,
1585
+ expect.objectContaining({
1586
+ method: 'POST',
1587
+ headers: {
1588
+ 'Content-Type': 'application/json',
1589
+ },
1590
+ credentials: 'include',
1591
+ }),
1592
+ );
1593
+ expect(result).toEqual({ status: 200, json: mockResponse });
1594
+ });
1595
+
1596
+ it('calls removeReferenceDimensionLink correctly', async () => {
1597
+ fetch.mockResponseOnce(JSON.stringify({ message: 'Success' }));
1598
+ const result = await DataJunctionAPI.removeReferenceDimensionLink(
1599
+ 'default.node1',
1600
+ 'column1',
1601
+ );
1602
+ expect(fetch).toHaveBeenCalledWith(
1603
+ `${DJ_URL}/nodes/default.node1/columns/column1/link`,
1604
+ expect.objectContaining({
1605
+ method: 'DELETE',
1606
+ }),
1607
+ );
1608
+ });
1609
+
1610
+ // Materialization
1611
+ it('calls deleteMaterialization with nodeVersion correctly', async () => {
1612
+ fetch.mockResponseOnce(JSON.stringify({ message: 'Success' }));
1613
+ await DataJunctionAPI.deleteMaterialization(
1614
+ 'default.node1',
1615
+ 'mat1',
1616
+ 'v1.0',
1617
+ );
1618
+ expect(fetch).toHaveBeenCalledWith(
1619
+ 'http://localhost:8000/nodes/default.node1/materializations?materialization_name=mat1&node_version=v1.0',
1620
+ expect.objectContaining({ method: 'DELETE' }),
1621
+ );
1622
+ });
1623
+
1624
+ it('calls deleteMaterialization without nodeVersion correctly', async () => {
1625
+ fetch.mockResponseOnce(JSON.stringify({ message: 'Success' }));
1626
+ await DataJunctionAPI.deleteMaterialization('default.node1', 'mat1');
1627
+ expect(fetch).toHaveBeenCalledWith(
1628
+ 'http://localhost:8000/nodes/default.node1/materializations?materialization_name=mat1',
1629
+ expect.objectContaining({ method: 'DELETE' }),
1630
+ );
1631
+ });
1632
+
1633
+ // Notifications
1634
+ it('calls getNotificationPreferences correctly', async () => {
1635
+ fetch.mockResponseOnce(JSON.stringify([]));
1636
+ await DataJunctionAPI.getNotificationPreferences({ entity_type: 'node' });
1637
+ expect(fetch).toHaveBeenCalledWith(
1638
+ 'http://localhost:8000/notifications/?entity_type=node',
1639
+ expect.objectContaining({ credentials: 'include' }),
1640
+ );
1641
+ });
1642
+
1643
+ it('calls subscribeToNotifications correctly', async () => {
1644
+ fetch.mockResponseOnce(JSON.stringify({ status: 'subscribed' }));
1645
+ await DataJunctionAPI.subscribeToNotifications({
1646
+ entity_type: 'node',
1647
+ entity_name: 'default.node1',
1648
+ activity_types: ['create'],
1649
+ alert_types: ['email'],
1650
+ });
1651
+ expect(fetch).toHaveBeenCalledWith(
1652
+ 'http://localhost:8000/notifications/subscribe',
1653
+ expect.objectContaining({
1654
+ method: 'POST',
1655
+ body: expect.any(String),
1656
+ }),
1657
+ );
1658
+ });
1659
+
1660
+ it('calls unsubscribeFromNotifications correctly', async () => {
1661
+ fetch.mockResponseOnce(JSON.stringify({ status: 'unsubscribed' }));
1662
+ await DataJunctionAPI.unsubscribeFromNotifications({
1663
+ entity_type: 'node',
1664
+ entity_name: 'default.node1',
1665
+ });
1666
+ expect(fetch).toHaveBeenCalledWith(
1667
+ expect.stringContaining('/notifications/unsubscribe'),
1668
+ expect.objectContaining({ method: 'DELETE' }),
1669
+ );
1670
+ });
1671
+
1672
+ // materializeCube (lines 1323-1339)
1673
+ it('calls materializeCube correctly', async () => {
1674
+ fetch.mockResponseOnce(JSON.stringify({ message: 'Success' }));
1675
+ const result = await DataJunctionAPI.materializeCube(
1676
+ 'default.cube1',
1677
+ 'spark_sql',
1678
+ 'incremental_time',
1679
+ '0 0 * * *',
1680
+ '1 day',
1681
+ );
1682
+ expect(fetch).toHaveBeenCalledWith(
1683
+ 'http://localhost:8000/nodes/default.cube1/materialization',
1684
+ expect.objectContaining({
1685
+ method: 'POST',
1686
+ body: expect.stringContaining('lookback_window'),
1687
+ }),
1688
+ );
1689
+ expect(result).toEqual({ status: 200, json: { message: 'Success' } });
1690
+ });
1691
+
1692
+ // runBackfill
1693
+ it('calls runBackfill correctly', async () => {
1694
+ fetch.mockResponseOnce(JSON.stringify({ message: 'Success' }));
1695
+ const result = await DataJunctionAPI.runBackfill('default.node1', 'mat1', [
1696
+ {
1697
+ columnName: 'date',
1698
+ range: ['2023-01-01', '2023-12-31'],
1699
+ values: [],
1700
+ },
1701
+ ]);
1702
+ expect(fetch).toHaveBeenCalledWith(
1703
+ 'http://localhost:8000/nodes/default.node1/materializations/mat1/backfill',
1704
+ expect.objectContaining({
1705
+ method: 'POST',
1706
+ body: expect.stringContaining('column_name'),
1707
+ }),
1708
+ );
1709
+ expect(result).toEqual({ status: 200, json: { message: 'Success' } });
1710
+ });
1711
+
1712
+ // addReferenceDimensionLink with role parameter
1713
+ it('calls addReferenceDimensionLink with role correctly', async () => {
1714
+ const mockResponse = { message: 'Success' };
1715
+ fetch.mockResponseOnce(JSON.stringify(mockResponse));
1716
+ const result = await DataJunctionAPI.addReferenceDimensionLink(
1717
+ 'default.node1',
1718
+ 'column1',
1719
+ 'default.dimension1',
1720
+ 'dimension_col',
1721
+ 'birth_date',
1722
+ );
1723
+ expect(fetch).toHaveBeenCalledWith(
1724
+ expect.stringContaining('role=birth_date'),
1725
+ expect.objectContaining({
1726
+ method: 'POST',
1727
+ }),
1728
+ );
1729
+ expect(result).toEqual({ status: 200, json: mockResponse });
1730
+ });
1731
+
1732
+ // Test getNotificationPreferences without params
1733
+ it('calls getNotificationPreferences without params correctly', async () => {
1734
+ fetch.mockResponseOnce(JSON.stringify([]));
1735
+ await DataJunctionAPI.getNotificationPreferences();
1736
+ expect(fetch).toHaveBeenCalledWith(
1737
+ 'http://localhost:8000/notifications/',
1738
+ expect.objectContaining({ credentials: 'include' }),
1739
+ );
1740
+ });
1741
+
1742
+ // Test listMetricMetadata
1743
+ it('calls listMetricMetadata correctly', async () => {
1744
+ fetch.mockResponseOnce(JSON.stringify([{ name: 'metric1' }]));
1745
+ const result = await DataJunctionAPI.listMetricMetadata();
1746
+ expect(fetch).toHaveBeenCalledWith(
1747
+ 'http://localhost:8000/metrics/metadata',
1748
+ expect.objectContaining({
1749
+ method: 'GET',
1750
+ headers: {
1751
+ 'Content-Type': 'application/json',
1752
+ },
1753
+ }),
1754
+ );
1755
+ expect(result).toEqual([{ name: 'metric1' }]);
1756
+ });
1757
+
1758
+ // Test materializationInfo
1759
+ it('calls materializationInfo correctly', async () => {
1760
+ fetch.mockResponseOnce(JSON.stringify({ info: 'test' }));
1761
+ const result = await DataJunctionAPI.materializationInfo();
1762
+ expect(fetch).toHaveBeenCalledWith(
1763
+ 'http://localhost:8000/materialization/info',
1764
+ expect.objectContaining({ credentials: 'include' }),
1765
+ );
1766
+ expect(result).toEqual({ info: 'test' });
1767
+ });
1768
+
1769
+ // Test revalidate
1770
+ it('calls revalidate correctly', async () => {
1771
+ fetch.mockResponseOnce(JSON.stringify({ status: 'valid' }));
1772
+ const result = await DataJunctionAPI.revalidate('default.node1');
1773
+ expect(fetch).toHaveBeenCalledWith(
1774
+ 'http://localhost:8000/nodes/default.node1/validate',
1775
+ expect.objectContaining({
1776
+ method: 'POST',
1777
+ headers: {
1778
+ 'Content-Type': 'application/json',
1779
+ },
1780
+ }),
1781
+ );
1782
+ expect(result).toEqual({ status: 'valid' });
1783
+ });
1784
+
1785
+ // Test getMetric (lines 322-360) - this is a GraphQL query
1786
+ it('calls getMetric correctly', async () => {
1787
+ fetch.mockResponseOnce(
1788
+ JSON.stringify({
1789
+ data: {
1790
+ findNodes: [
1791
+ {
1792
+ name: 'system.test.metric',
1793
+ current: {
1794
+ metricMetadata: { direction: 'higher_is_better' },
1795
+ },
1796
+ },
1797
+ ],
1798
+ },
1799
+ }),
1800
+ );
1801
+
1802
+ const result = await DataJunctionAPI.getMetric('system.test.metric');
1803
+ expect(result).toHaveProperty('name');
1804
+ });
1805
+
1806
+ // Test system metrics APIs
1807
+ it('calls system.node_counts_by_active correctly', async () => {
1808
+ fetch.mockResponseOnce(
1809
+ JSON.stringify([
1810
+ [
1811
+ { col: 'system.dj.is_active.active_id', value: true },
1812
+ { col: 'system.dj.number_of_nodes', value: 100 },
1813
+ ],
1814
+ ]),
1815
+ );
1816
+
1817
+ const result = await DataJunctionAPI.system.node_counts_by_active();
1818
+ expect(result).toEqual([{ name: 'true', value: 100 }]);
1819
+ });
1820
+
1821
+ it('calls system.node_counts_by_type correctly', async () => {
1822
+ fetch.mockResponseOnce(
1823
+ JSON.stringify([
1824
+ [
1825
+ { col: 'system.dj.node_type.type', value: 'metric' },
1826
+ { col: 'system.dj.number_of_nodes', value: 50 },
1827
+ ],
1828
+ ]),
1829
+ );
1830
+
1831
+ const result = await DataJunctionAPI.system.node_counts_by_type();
1832
+ expect(result).toEqual([{ name: 'metric', value: 50 }]);
1833
+ });
1834
+
1835
+ it('calls system.node_counts_by_status correctly', async () => {
1836
+ fetch.mockResponseOnce(
1837
+ JSON.stringify([
1838
+ [
1839
+ { col: 'system.dj.nodes.status', value: 'valid' },
1840
+ { col: 'system.dj.number_of_nodes', value: 80 },
1841
+ ],
1842
+ ]),
1843
+ );
1844
+
1845
+ const result = await DataJunctionAPI.system.node_counts_by_status();
1846
+ expect(result).toEqual([{ name: 'valid', value: 80 }]);
1847
+ });
1848
+
1849
+ it('calls system.nodes_without_description correctly', async () => {
1850
+ fetch.mockResponseOnce(
1851
+ JSON.stringify([
1852
+ [
1853
+ { col: 'system.dj.node_type.type', value: 'transform' },
1854
+ { col: 'system.dj.node_without_description', value: 10 },
1855
+ ],
1856
+ ]),
1857
+ );
1858
+
1859
+ const result = await DataJunctionAPI.system.nodes_without_description();
1860
+ expect(result).toEqual([{ name: 'transform', value: 10 }]);
1861
+ });
1862
+
1863
+ it('calls system.materialization_counts_by_type correctly', async () => {
1864
+ fetch.mockResponseOnce(
1865
+ JSON.stringify([
1866
+ [
1867
+ { col: 'system.dj.node_type.type', value: 'cube' },
1868
+ { col: 'system.dj.number_of_materializations', value: 5 },
1869
+ ],
1870
+ ]),
1871
+ );
1872
+
1873
+ const result =
1874
+ await DataJunctionAPI.system.materialization_counts_by_type();
1875
+ expect(result).toEqual([{ name: 'cube', value: 5 }]);
1876
+ });
1877
+
1878
+ it('calls system.dimensions correctly', async () => {
1879
+ fetch.mockResponseOnce(JSON.stringify(['dim1', 'dim2']));
1880
+
1881
+ const result = await DataJunctionAPI.system.dimensions();
1882
+ expect(fetch).toHaveBeenCalledWith(
1883
+ 'http://localhost:8000/system/dimensions',
1884
+ expect.objectContaining({ credentials: 'include' }),
1885
+ );
1886
+ expect(result).toEqual(['dim1', 'dim2']);
1887
+ });
1888
+
1889
+ it('calls system.node_trends correctly', async () => {
1890
+ fetch.mockResponseOnce(
1891
+ JSON.stringify([
1892
+ [
1893
+ { col: 'system.dj.nodes.created_at_week', value: 20240101 },
1894
+ { col: 'system.dj.node_type.type', value: 'metric' },
1895
+ { col: 'system.dj.number_of_nodes', value: 10 },
1896
+ ],
1897
+ ]),
1898
+ );
1899
+
1900
+ const result = await DataJunctionAPI.system.node_trends();
1901
+ expect(result.length).toBeGreaterThan(0);
1902
+ expect(result[0]).toHaveProperty('date');
1903
+ expect(result[0]).toHaveProperty('metric');
1904
+ });
1905
+
1906
+ // Test querySystemMetricSingleDimension edge cases
1907
+ it('handles missing values in querySystemMetricSingleDimension', async () => {
1908
+ fetch.mockResponseOnce(
1909
+ JSON.stringify([
1910
+ [
1911
+ { col: 'some_dimension', value: null },
1912
+ { col: 'some_metric', value: undefined },
1913
+ ],
1914
+ ]),
1915
+ );
1916
+
1917
+ const result = await DataJunctionAPI.querySystemMetricSingleDimension({
1918
+ metric: 'some_metric',
1919
+ dimension: 'some_dimension',
1920
+ });
1921
+
1922
+ expect(result[0].name).toBe('unknown');
1923
+ expect(result[0].value).toBe(0);
1924
+ });
1925
+
1926
+ // Test getNodeForEditing (lines 266-319)
1927
+ it('calls getNodeForEditing correctly', async () => {
1928
+ fetch.mockResponseOnce(
1929
+ JSON.stringify({
1930
+ data: {
1931
+ findNodes: [
1932
+ {
1933
+ name: 'default.node1',
1934
+ type: 'transform',
1935
+ current: {
1936
+ displayName: 'Node 1',
1937
+ description: 'Test node',
1938
+ query: 'SELECT * FROM table',
1939
+ },
1940
+ },
1941
+ ],
1942
+ },
1943
+ }),
1944
+ );
1945
+
1946
+ const result = await DataJunctionAPI.getNodeForEditing('default.node1');
1947
+ expect(result).toHaveProperty('name', 'default.node1');
1948
+ });
1949
+
1950
+ it('returns null when getNodeForEditing finds no nodes', async () => {
1951
+ fetch.mockResponseOnce(
1952
+ JSON.stringify({
1953
+ data: {
1954
+ findNodes: [],
1955
+ },
1956
+ }),
1957
+ );
1958
+
1959
+ const result = await DataJunctionAPI.getNodeForEditing('nonexistent');
1960
+ expect(result).toBeNull();
1961
+ });
1962
+
1963
+ // Test getCubeForEditing (lines 363-410)
1964
+ it('calls getCubeForEditing correctly', async () => {
1965
+ fetch.mockResponseOnce(
1966
+ JSON.stringify({
1967
+ data: {
1968
+ findNodes: [
1969
+ {
1970
+ name: 'default.cube1',
1971
+ type: 'cube',
1972
+ current: {
1973
+ displayName: 'Cube 1',
1974
+ description: 'Test cube',
1975
+ cubeMetrics: [{ name: 'metric1' }],
1976
+ cubeDimensions: [{ name: 'dim1', attribute: 'attr1' }],
1977
+ },
1978
+ },
1979
+ ],
1980
+ },
1981
+ }),
1982
+ );
1983
+
1984
+ const result = await DataJunctionAPI.getCubeForEditing('default.cube1');
1985
+ expect(result).toHaveProperty('name', 'default.cube1');
1986
+ expect(result.current.cubeMetrics).toHaveLength(1);
1987
+ });
1988
+
1989
+ it('returns null when getCubeForEditing finds no nodes', async () => {
1990
+ fetch.mockResponseOnce(
1991
+ JSON.stringify({
1992
+ data: {
1993
+ findNodes: [],
1994
+ },
1995
+ }),
1996
+ );
1997
+
1998
+ const result = await DataJunctionAPI.getCubeForEditing('nonexistent');
1999
+ expect(result).toBeNull();
2000
+ });
2001
+
2002
+ // Test logout (lines 225-230)
2003
+ it('calls logout correctly', async () => {
2004
+ fetch.mockResponseOnce('', { status: 200 });
2005
+
2006
+ await DataJunctionAPI.logout();
2007
+ expect(fetch).toHaveBeenCalledWith(
2008
+ 'http://localhost:8000/logout/',
2009
+ expect.objectContaining({
2010
+ method: 'POST',
2011
+ credentials: 'include',
2012
+ }),
2013
+ );
2014
+ });
2015
+
2016
+ // Test catalogs (lines 232-238)
2017
+ it('calls catalogs correctly', async () => {
2018
+ fetch.mockResponseOnce(
2019
+ JSON.stringify([{ name: 'catalog1' }, { name: 'catalog2' }]),
2020
+ );
2021
+
2022
+ const result = await DataJunctionAPI.catalogs();
2023
+ expect(fetch).toHaveBeenCalledWith(
2024
+ 'http://localhost:8000/catalogs',
2025
+ expect.objectContaining({ credentials: 'include' }),
2026
+ );
2027
+ expect(result).toHaveLength(2);
2028
+ });
2029
+
2030
+ // Test sql with array filters (lines 755-756)
2031
+ it('handles array filters in sql correctly', async () => {
2032
+ fetch.mockResponseOnce(JSON.stringify({ sql: 'SELECT * FROM table' }));
2033
+
2034
+ await DataJunctionAPI.sql('default.metric1', {
2035
+ dimensions: ['dim1', 'dim2'],
2036
+ filters: ['filter1', 'filter2'],
2037
+ });
2038
+
2039
+ const callUrl = fetch.mock.calls[0][0];
2040
+ expect(callUrl).toContain('dimensions=dim1');
2041
+ expect(callUrl).toContain('filters=filter1');
2042
+ });
2043
+
2044
+ // Test nodeData with null selection (lines 831-835)
2045
+ it('handles null selection in nodeData', async () => {
2046
+ fetch.mockResponseOnce(JSON.stringify({ data: [] }));
2047
+
2048
+ await DataJunctionAPI.nodeData('default.node1', null);
2049
+
2050
+ const callUrl = fetch.mock.calls[0][0];
2051
+ expect(callUrl).toContain('limit=1000');
2052
+ expect(callUrl).toContain('async_=true');
2053
+ });
2054
+
2055
+ // Test streamNodeData with null selection (lines 889-893) - just test it returns EventSource
2056
+ it('handles null selection in streamNodeData', async () => {
2057
+ const eventSource = await DataJunctionAPI.streamNodeData(
2058
+ 'default.node1',
2059
+ null,
2060
+ );
2061
+
2062
+ expect(eventSource).toBeInstanceOf(EventSource);
2063
+
2064
+ // EventSource mock might not have close method in test environment
2065
+ if (typeof eventSource.close === 'function') {
2066
+ eventSource.close();
2067
+ }
2068
+ });
2069
+
2070
+ // Test setColumnDescription (lines 1027-1036)
2071
+ it('calls setColumnDescription correctly', async () => {
2072
+ fetch.mockResponseOnce(JSON.stringify({ message: 'success' }));
2073
+
2074
+ await DataJunctionAPI.setColumnDescription(
2075
+ 'default.node1',
2076
+ 'column1',
2077
+ 'New description',
2078
+ );
2079
+
2080
+ expect(fetch).toHaveBeenCalledWith(
2081
+ expect.stringContaining(
2082
+ '/nodes/default.node1/columns/column1/description',
2083
+ ),
2084
+ expect.objectContaining({
2085
+ method: 'PATCH',
2086
+ credentials: 'include',
2087
+ }),
2088
+ );
2089
+ });
2090
+
2091
+ // Test nodeDimensions (lines 1046-1050)
2092
+ it('calls nodeDimensions correctly', async () => {
2093
+ fetch.mockResponseOnce(JSON.stringify(['dim1', 'dim2']));
2094
+
2095
+ const result = await DataJunctionAPI.nodeDimensions('default.node1');
2096
+
2097
+ expect(fetch).toHaveBeenCalledWith(
2098
+ 'http://localhost:8000/nodes/default.node1/dimensions',
2099
+ expect.objectContaining({ credentials: 'include' }),
2100
+ );
2101
+ expect(result).toEqual(['dim1', 'dim2']);
2102
+ });
2103
+
2104
+ // Test users (lines 1194-1198)
2105
+ it('calls users correctly', async () => {
2106
+ fetch.mockResponseOnce(
2107
+ JSON.stringify([{ username: 'user1' }, { username: 'user2' }]),
2108
+ );
2109
+
2110
+ const result = await DataJunctionAPI.users();
2111
+
2112
+ expect(fetch).toHaveBeenCalledWith(
2113
+ 'http://localhost:8000/users?with_activity=true',
2114
+ expect.objectContaining({ credentials: 'include' }),
2115
+ );
2116
+ expect(result).toHaveLength(2);
2117
+ });
1572
2118
  });
package/webpack.config.js CHANGED
@@ -18,6 +18,7 @@ module.exports = {
18
18
  },
19
19
  target: 'web',
20
20
  mode: 'development',
21
+ stats: 'minimal',
21
22
  output: {
22
23
  path: path.resolve(__dirname, './dist'),
23
24
  filename: 'static/[name].[fullhash].js',