Skip to content

Commit

Permalink
MPDX-7539 TntConnect Import Page (#956)
Browse files Browse the repository at this point in the history
Add Tools - TntConnect Import page
  • Loading branch information
caleballdrin authored Jun 20, 2024
1 parent 1197576 commit 31f7a54
Show file tree
Hide file tree
Showing 12 changed files with 956 additions and 15 deletions.
33 changes: 33 additions & 0 deletions pages/accountLists/[accountListId]/tools/tntConnect.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Head from 'next/head';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { loadSession } from 'pages/api/utils/pagePropsHelpers';
import Loading from 'src/components/Loading';
import TntConnect from 'src/components/Tool/TntConnect/TntConnect';
import { useAccountListId } from 'src/hooks/useAccountListId';
import useGetAppSettings from 'src/hooks/useGetAppSettings';

const TntConnectPage: React.FC = () => {
const { t } = useTranslation();
const accountListId = useAccountListId();
const { appName } = useGetAppSettings();

return (
<>
<Head>
<title>
{appName} | {t('Import Tnt')}
</title>
</Head>
{accountListId ? (
<TntConnect accountListId={accountListId} />
) : (
<Loading loading />
)}
</>
);
};

export const getServerSideProps = loadSession;

export default TntConnectPage;
File renamed without changes.
89 changes: 89 additions & 0 deletions pages/api/uploads/upload-tnt-connect-import.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { readFile } from 'fs/promises';
import { NextApiRequest, NextApiResponse } from 'next';
import formidable, { IncomingForm } from 'formidable';
import { getToken } from 'next-auth/jwt';
import fetch, { File, FormData } from 'node-fetch';

export const config = {
api: {
bodyParser: false,
},
};

const parseBody = async (
req: NextApiRequest,
): Promise<{ fields: formidable.Fields; files: formidable.Files }> => {
return new Promise((resolve, reject) => {
const form = new IncomingForm();
form.parse(req, (err, fields, files) => {
if (err) {
reject(err);
} else {
resolve({ fields, files });
}
});
});
};

const uploadTntConnect = async (
req: NextApiRequest,
res: NextApiResponse,
): Promise<void> => {
try {
if (req.method !== 'POST') {
res.status(405).send('Method Not Found');
return;
}

const jwt = await getToken({
req,
secret: process.env.JWT_SECRET,
});
const apiToken = jwt?.apiToken;
if (!apiToken) {
res.status(401).send('Unauthorized');
return;
}

const {
fields: { override, tag_list, accountListId },
files: { file },
} = await parseBody(req);
if (typeof override !== 'string') {
res.status(400).send('Missing override');
return;
}
if (!file || Array.isArray(file)) {
res.status(400).send('Missing file');
return;
}

const fileUpload = new File(
[await readFile(file.filepath)],
file.originalFilename ?? 'tntConnectUpload',
);
const form = new FormData();
form.append('data[type]', 'imports');
form.append('data[attributes][override]', override);
form.append(
'data[attributes][tag_list]',
Array.isArray(tag_list) ? tag_list.join(',') : tag_list,
);
form.append('data[attributes][file]', fileUpload);
const fetchRes = await fetch(
`${process.env.REST_API_URL}account_lists/${accountListId}/imports/tnt`,
{
method: 'POST',
headers: {
authorization: `Bearer ${apiToken}`,
},
body: form,
},
);
res.status(fetchRes.status).json({ success: fetchRes.status === 201 });
} catch (err) {
res.status(500).json({ success: false, error: err });
}
};

export default uploadTntConnect;
48 changes: 48 additions & 0 deletions pages/api/uploads/upload-tnt-connect-import.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { getToken } from 'next-auth/jwt';
import { createMocks } from 'node-mocks-http';
import uploadTntConnect from './upload-tnt-connect-import.page';
import 'node-fetch';

jest.mock('node-fetch', () => jest.fn());

jest.mock('next-auth/jwt', () => ({ getToken: jest.fn() }));
jest.mock('src/lib/apollo/ssrClient', () => jest.fn());

const accountListId = 'accountListId';
const file = new File(['contents1'], 'tnt1.xml', {
type: 'text/xml',
});
const override = 'false';
const tag_list = 'tag1';

describe('upload-tnt-connect-import', () => {
it('responds with error if unauthorized', async () => {
const { req, res } = createMocks({
method: 'POST',
body: {
override: override,
file,
tag_list,
accountListId,
},
});

await uploadTntConnect(req, res);

expect(res._getStatusCode()).toBe(401);
});

it('responds with error if not sent with POST', async () => {
(getToken as jest.Mock).mockReturnValue({
apiToken: 'accessToken',
userID: 'sessionUserID',
});
const { req, res } = createMocks({
method: 'GET',
});

await uploadTntConnect(req, res);

expect(res._getStatusCode()).toBe(405);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const uploadAvatar = async ({
form.append('personId', personId);
form.append('avatar', file);

const res = await fetch(`/api/upload-person-avatar`, {
const res = await fetch(`/api/uploads/upload-person-avatar`, {
method: 'POST',
body: form,
}).catch(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ export const ContactTags: React.FC<ContactTagsProps> = ({
autoHighlight
fullWidth
loading={loading}
popupIcon={<ContactTagIcon />}
filterSelectedOptions
value={tagList}
options={unusedTags || []}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Loading/Loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const useStyles = makeStyles()((theme: Theme) => ({
left: '50%',
marginLeft: '-28px',
marginTop: '-28px',
zIndex: 10,
zIndex: 1000,
opacity: 0,
transition: theme.transitions.create(['opacity', 'visibility'], {
duration: theme.transitions.duration.short,
Expand Down
13 changes: 1 addition & 12 deletions src/components/Tags/Tags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,7 @@ export const ContactTagInput = styled(TextField)(({ theme }) => ({
borderBottom: `2px solid ${theme.palette.divider}`,
},
'&& .MuiInputBase-input': {
minWidth: '200px',
},
'& ::placeholder': {
color: theme.palette.info.main,
opacity: 1,
},
'& :hover::placeholder': {
textDecoration: 'underline',
},
'& :focus::placeholder': {
textDecoration: 'none',
color: theme.palette.cruGrayMedium.main,
minWidth: '150px',
},
margin: theme.spacing(1),
marginLeft: '0',
Expand Down
Loading

0 comments on commit 31f7a54

Please sign in to comment.