Skip to content

Commit 7fe4ff6

Browse files
Merge branch 'master' into Overview
2 parents 2fc68b0 + 73252e5 commit 7fe4ff6

File tree

15 files changed

+522
-51
lines changed

15 files changed

+522
-51
lines changed

desktop/conf.dist/hue.ini

+3
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,9 @@ tls=no
846846
# Whether Hue as OpenID Connect client verify SSL cert
847847
## oidc_verify_ssl=true
848848

849+
# OIDC authentication request extra params
850+
## oidc_auth_request_extra_params={}
851+
849852
# As relay party Hue URL path to redirect to after login
850853
## login_redirect_url=https://localhost:8888/oidc/callback/
851854

desktop/conf/pseudo-distributed.ini.tmpl

+3
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,9 @@
848848
# Whether Hue as OpenID Connect client verify SSL cert
849849
## oidc_verify_ssl=true
850850

851+
# OIDC authentication request extra params
852+
## oidc_auth_request_extra_params={}
853+
851854
# As relay party Hue URL path to redirect to after login
852855
## login_redirect_url=https://localhost:8888/oidc/callback/
853856

desktop/core/src/desktop/auth/backend.py

+3
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,9 @@ def authenticate(self, *args, **kwargs):
836836
),
837837
}
838838

839+
oidc_extra_params = import_from_settings('OIDC_AUTH_REQUEST_EXTRA_PARAMS', {})
840+
token_payload.update(oidc_extra_params)
841+
839842
# Get the token
840843
token_info = self.get_token(token_payload)
841844
id_token = token_info.get('id_token')

desktop/core/src/desktop/conf.py

+7
Original file line numberDiff line numberDiff line change
@@ -1662,6 +1662,13 @@ def is_gunicorn_report_enabled():
16621662
default=False
16631663
),
16641664

1665+
OIDC_AUTH_REQUEST_EXTRA_PARAMS=Config(
1666+
key="oidc_auth_request_extra_params",
1667+
help=_("OIDC authentication request extra params."),
1668+
type=coerce_json_dict,
1669+
default='{}'
1670+
),
1671+
16651672
LOGIN_REDIRECT_URL=Config(
16661673
key="login_redirect_url",
16671674
help=_("As relay party Hue URL path to redirect to after login."),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Licensed to Cloudera, Inc. under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. Cloudera, Inc. licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
@use 'variables' as vars;
18+
19+
.antd.cuix {
20+
.hue-importer-preview-page {
21+
display: flex;
22+
flex-direction: column;
23+
gap: 16px;
24+
padding: 8px 16px 16px 24px;
25+
height: 100%;
26+
27+
&__header {
28+
display: flex;
29+
justify-content: space-between;
30+
31+
&__title {
32+
display: flex;
33+
align-items: center;
34+
font-size: vars.$fluidx-heading-h3-size;
35+
font-weight: vars.$fluidx-heading-h3-weight;
36+
color: vars.$fluidx-gray-900;
37+
}
38+
39+
&__actions {
40+
display: flex;
41+
align-items: center;
42+
gap: 24px;
43+
44+
.action {
45+
margin-left: 10px;
46+
}
47+
}
48+
}
49+
50+
&__metadata {
51+
background-color: white;
52+
height: 68px;
53+
padding: 16px;
54+
font-size: 12px;
55+
color: #5a656d;
56+
}
57+
58+
&__main-section {
59+
display: flex;
60+
flex: 1;
61+
justify-content: center;
62+
background-color: white;
63+
padding: 16px 0 0 0;
64+
}
65+
}
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Licensed to Cloudera, Inc. under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. Cloudera, Inc. licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
import React from 'react';
18+
import '@testing-library/jest-dom';
19+
import { render, screen, waitFor } from '@testing-library/react';
20+
import ImporterFilePreview from './ImporterFilePreview';
21+
import { FileMetaData } from '../types';
22+
23+
const mockSave = jest.fn();
24+
jest.mock('../../../utils/hooks/useSaveData/useSaveData', () => ({
25+
__esModule: true,
26+
default: jest.fn(() => ({
27+
data: {
28+
columns: [{ name: 'Name' }, { name: 'Age' }],
29+
sample: [
30+
['Alice', '30'],
31+
['Bob', '25']
32+
]
33+
},
34+
save: mockSave,
35+
loading: false
36+
}))
37+
}));
38+
39+
describe('ImporterFilePreview', () => {
40+
const mockFileMetaData: FileMetaData = {
41+
source: 'localfile',
42+
type: 'csv',
43+
path: '/path/to/file.csv'
44+
};
45+
46+
it('should render correctly', async () => {
47+
render(<ImporterFilePreview fileMetaData={mockFileMetaData} />);
48+
49+
await waitFor(() => {
50+
expect(screen.getByText('Preview')).toBeInTheDocument();
51+
expect(screen.getByText('Cancel')).toBeInTheDocument();
52+
expect(screen.getByText('Finish Import')).toBeInTheDocument();
53+
});
54+
});
55+
56+
it('should call guessFormat and guessFields when the component mounts', async () => {
57+
render(<ImporterFilePreview fileMetaData={mockFileMetaData} />);
58+
59+
await waitFor(() => {
60+
expect(mockSave).toHaveBeenCalledTimes(2);
61+
});
62+
});
63+
64+
it('should display data in the table after previewData is available', async () => {
65+
render(<ImporterFilePreview fileMetaData={mockFileMetaData} />);
66+
67+
await waitFor(() => {
68+
expect(screen.getByText('Alice')).toBeInTheDocument();
69+
expect(screen.getByText('30')).toBeInTheDocument();
70+
expect(screen.getByText('Bob')).toBeInTheDocument();
71+
expect(screen.getByText('25')).toBeInTheDocument();
72+
});
73+
});
74+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Licensed to Cloudera, Inc. under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. Cloudera, Inc. licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
import React, { useEffect, useState } from 'react';
18+
import useSaveData from '../../../utils/hooks/useSaveData/useSaveData';
19+
import {
20+
FileFormatResponse,
21+
FileMetaData,
22+
GuessFieldTypesResponse,
23+
ImporterTableData
24+
} from '../types';
25+
import { convertToAntdColumns, convertToDataSource } from '../utils/utils';
26+
import { i18nReact } from '../../../utils/i18nReact';
27+
import { BorderlessButton, PrimaryButton } from 'cuix/dist/components/Button';
28+
import LoadingErrorWrapper from '../../../reactComponents/LoadingErrorWrapper/LoadingErrorWrapper';
29+
import useResizeObserver from '../../../utils/hooks/useResizeObserver/useResizeObserver';
30+
import PaginatedTable from '../../../reactComponents/PaginatedTable/PaginatedTable';
31+
import { GUESS_FORMAT_URL, GUESS_FIELD_TYPES_URL } from '../api';
32+
33+
import './ImporterFilePreview.scss';
34+
35+
interface ImporterFilePreviewProps {
36+
fileMetaData: FileMetaData;
37+
}
38+
39+
const ImporterFilePreview = ({ fileMetaData }: ImporterFilePreviewProps): JSX.Element => {
40+
const { t } = i18nReact.useTranslation();
41+
const [fileFormat, setFileFormat] = useState<FileFormatResponse | null>(null);
42+
43+
const { save: guessFormat, loading: guessingFormat } = useSaveData<FileFormatResponse>(
44+
GUESS_FORMAT_URL,
45+
{
46+
onSuccess: data => {
47+
setFileFormat(data);
48+
}
49+
}
50+
);
51+
52+
const {
53+
save: guessFields,
54+
data: previewData,
55+
loading: guessingFields
56+
} = useSaveData<GuessFieldTypesResponse>(GUESS_FIELD_TYPES_URL);
57+
58+
useEffect(() => {
59+
const guessFormatPayload = {
60+
inputFormat: fileMetaData.source,
61+
file_type: fileMetaData.type,
62+
path: fileMetaData.path
63+
};
64+
const guessFormatormData = new FormData();
65+
guessFormatormData.append('fileFormat', JSON.stringify(guessFormatPayload));
66+
guessFormat(guessFormatormData);
67+
}, [fileMetaData]);
68+
69+
useEffect(() => {
70+
if (!fileFormat) {
71+
return;
72+
}
73+
74+
const payload = {
75+
path: fileMetaData.path,
76+
format: fileFormat,
77+
inputFormat: fileMetaData.source
78+
};
79+
const formData = new FormData();
80+
formData.append('fileFormat', JSON.stringify(payload));
81+
guessFields(formData);
82+
}, [fileMetaData.path, fileFormat]);
83+
84+
const columns = convertToAntdColumns(previewData?.columns ?? []);
85+
const tableData = convertToDataSource(columns, previewData?.sample);
86+
87+
const [ref, rect] = useResizeObserver();
88+
89+
return (
90+
<div className="hue-importer-preview-page">
91+
<div className="hue-importer-preview-page__header">
92+
<div className="hue-importer-preview-page__header__title">{t('Preview')}</div>
93+
<div className="hue-importer-preview-page__header__actions">
94+
<BorderlessButton data-testid="hue-importer-preview-page__header__actions__cancel">
95+
{t('Cancel')}
96+
</BorderlessButton>
97+
<PrimaryButton data-testid="hue-importer-preview-page__header__actions__finish">
98+
{t('Finish Import')}
99+
</PrimaryButton>
100+
</div>
101+
</div>
102+
<div className="hue-importer-preview-page__metadata">{t('DESTINATION')}</div>
103+
<div className="hue-importer-preview-page__main-section" ref={ref}>
104+
<LoadingErrorWrapper loading={guessingFormat || guessingFields} errors={[]} hideChildren>
105+
<PaginatedTable<ImporterTableData>
106+
data={tableData}
107+
columns={columns}
108+
rowKey="importerDataKey"
109+
scroll={{
110+
y: Math.max(rect.height - 60, 100),
111+
x: true
112+
}}
113+
locale={{ emptyText: t('No data available') }}
114+
/>
115+
</LoadingErrorWrapper>
116+
</div>
117+
</div>
118+
);
119+
};
120+
121+
export default ImporterFilePreview;

desktop/core/src/desktop/js/apps/newimporter/ImporterPage.tsx

+9-2
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,30 @@
1414
// See the License for the specific language governing permissions and
1515
// limitations under the License.
1616

17-
import React from 'react';
17+
import React, { useState } from 'react';
1818
import DataInIcon from '@cloudera/cuix-core/icons/react/DataInIcon';
1919

2020
import { i18nReact } from '../../utils/i18nReact';
21+
import { FileMetaData } from './types';
2122
import CommonHeader from '../../reactComponents/CommonHeader/CommonHeader';
2223
import ImporterSourceSelector from './ImporterSourceSelector/ImporterSourceSelector';
24+
import ImporterFilePreview from './ImporterFilePreview/ImporterFilePreview';
2325

2426
import './ImporterPage.scss';
2527

2628
const ImporterPage = (): JSX.Element => {
2729
const { t } = i18nReact.useTranslation();
30+
const [fileMetaData, setFileMetaData] = useState<FileMetaData>();
2831

2932
return (
3033
<div className="hue-importer cuix antd">
3134
<CommonHeader title={t('Importer')} icon={<DataInIcon />} />
3235
<div className="hue-importer__container">
33-
<ImporterSourceSelector />
36+
{!fileMetaData?.path ? (
37+
<ImporterSourceSelector setFileMetaData={setFileMetaData} />
38+
) : (
39+
<ImporterFilePreview fileMetaData={fileMetaData} />
40+
)}
3441
</div>
3542
</div>
3643
);

0 commit comments

Comments
 (0)