Skip to content

Commit

Permalink
fix(websocket): Real use of websocket without HTTP request in parallel
Browse files Browse the repository at this point in the history
  • Loading branch information
annelhote committed Sep 27, 2024
1 parent 7bc781d commit ebfd5c0
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 132 deletions.
3 changes: 1 addition & 2 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@
"react-router-dom": "^6.11.1",
"react-tooltip": "^5.18.1",
"react-use-websocket": "^4.8.1",
"remixicon": "^4.2.0",
"uuid": "^10.0.0"
"remixicon": "^4.2.0"
},
"devDependencies": {
"@types/react": "^18.0.28",
Expand Down
5 changes: 3 additions & 2 deletions client/src/pages/actions/actionsOpenalex.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { useSearchParams } from 'react-router-dom';
import PropTypes from 'prop-types';
import { useSearchParams } from 'react-router-dom';

import ButtonDropdown from '../../components/button-dropdown';

export default function ActionsOpenalex({
allOpenalexCorrections,
}) {
const [searchParams] = useSearchParams();

return (
<ButtonDropdown
data={allOpenalexCorrections}
Expand All @@ -18,7 +20,6 @@ export default function ActionsOpenalex({
ActionsOpenalex.propTypes = {
allOpenalexCorrections: PropTypes.arrayOf(PropTypes.shape({
affiliations: PropTypes.arrayOf(PropTypes.object),
allIds: PropTypes.arrayOf(PropTypes.object).isRequired,
datasource: PropTypes.arrayOf(PropTypes.string).isRequired,
id: PropTypes.string.isRequired,
status: PropTypes.string.isRequired,
Expand Down
7 changes: 3 additions & 4 deletions client/src/pages/actions/actionsOpenalexFeedback.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ import PropTypes from 'prop-types';
import { useEffect, useState } from 'react';

import useToast from '../../hooks/useToast';
import { sendGitHubIssue } from '../../utils/github';

export default function ActionsOpenalexFeedback({ allOpenalexCorrections, uuid }) {
export default function ActionsOpenalexFeedback({ allOpenalexCorrections, sendJsonMessage }) {
const [isModalOpen, setIsModalOpen] = useState(false);
const [userEmail, setUserEmail] = useState(null);
const [validEmail, setValidEmail] = useState(null);
Expand All @@ -22,7 +21,7 @@ export default function ActionsOpenalexFeedback({ allOpenalexCorrections, uuid }

const feedback = async () => {
try {
sendGitHubIssue({ data: allOpenalexCorrections, email: userEmail, uuid });
sendJsonMessage({ data: allOpenalexCorrections, email: userEmail });
} catch (error) {
toast({
description: error.message,
Expand Down Expand Up @@ -88,5 +87,5 @@ ActionsOpenalexFeedback.propTypes = {
worksOpenAlex: PropTypes.arrayOf(PropTypes.string).isRequired,
}),
).isRequired,
uuid: PropTypes.string.isRequired,
sendJsonMessage: PropTypes.func.isRequired,
};
9 changes: 2 additions & 7 deletions client/src/pages/views/openalex.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import {
Title,
} from '@dataesr/dsfr-plus';
import PropTypes from 'prop-types';
import { useState } from 'react';
import useWebSocket from 'react-use-websocket';
import { v4 as uuidv4 } from 'uuid';

import useToast from '../../hooks/useToast';
import ActionsOpenalex from '../actions/actionsOpenalex';
Expand All @@ -21,10 +19,8 @@ export default function Openalex({
}) {
const { VITE_WS_HOST } = import.meta.env;
const { toast } = useToast();
const [uuid] = useState(uuidv4());

useWebSocket(`${VITE_WS_HOST}/ws?uuid=${uuid}`, {
onClose: () => console.log(`Websocket connection closed: ${uuid}`),
const { sendJsonMessage } = useWebSocket(`${VITE_WS_HOST}/ws`, {
onError: (event) => console.error(event),
onMessage: (event) => {
const { autoDismissAfter, description, title, toastType } = JSON.parse(event.data);
Expand All @@ -36,7 +32,6 @@ export default function Openalex({
toastType: toastType ?? 'info',
});
},
onOpen: () => console.log(`Websocket connection open: ${uuid}`),
share: true,
});

Expand Down Expand Up @@ -68,7 +63,7 @@ export default function Openalex({
<Col xs="3">
<ActionsOpenalexFeedback
allOpenalexCorrections={allOpenalexCorrections}
uuid={uuid}
sendJsonMessage={sendJsonMessage}
/>
</Col>
</Row>
Expand Down
19 changes: 0 additions & 19 deletions client/src/utils/github.jsx

This file was deleted.

15 changes: 1 addition & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions server/src/router.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import express from 'express';

import filesRouter from './routes/files.routes';
import githubRouter from './routes/github.routes';
import worksRouter from './routes/works.routes';

const router = new express.Router();

router.use(filesRouter);
router.use(githubRouter);
router.use(worksRouter);

export default router;
69 changes: 5 additions & 64 deletions server/src/routes/github.routes.js → server/src/utils/github.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import crypto from 'crypto';
import express from 'express';
import { Octokit } from '@octokit/rest';
import { throttling } from '@octokit/plugin-throttling';

import { chunkArray } from '../utils/utils';
import wss from '../webSocketServer';
import { Octokit } from '@octokit/rest';
import crypto from 'crypto';

const MyOctokit = Octokit.plugin(throttling);
const auth = process.env.GITHUB_PAT;
const router = new express.Router();

const ALGORITHM = 'aes-256-ctr';
const IV_LENGTH = 16;
Expand Down Expand Up @@ -75,60 +70,6 @@ const createIssue = (issue, email) => {
});
};

router.route('/github-issue').post(async (req, res) => {
const data = req.body?.data || [];
const email = req.body?.email || '';
const uuid = req.body?.uuid || '';

let toast = {
autoDismissAfter: 5000,
description:
'Your correction(s) are currently submitted to the <a href="https://github.com/dataesr/openalex-affiliations/issues" target="_blank">Github repository</a>',
id: 'initOpenAlex',
title: 'OpenAlex corrections submitted',
};
wss.send({ message: JSON.stringify(toast), uuid });

const perChunk = 30;
const results = [];
for (const [i, d] of chunkArray({ array: data, perChunk }).entries()) {
const promises = d.map((item) => createIssue(item, email).catch((error) => error));
const r = await Promise.all(promises);
results.push(...r);
toast = {
description: `${Math.min(data.length, (i + 1) * perChunk)} / ${
data.length
} issue(s) submitted`,
id: `processOpenAlex${i}`,
title: 'OpenAlex corrections are being processed',
};
wss.send({ message: JSON.stringify(toast), uuid });
}

const firstError = results.find(
(result) => !result.status.toString().startsWith('2'),
);
if (firstError?.status) {
toast = {
description: `Error while submitting Github issues : ${firstError?.message}`,
id: 'errorOpenAlex',
title: `Error ${firstError.status}`,
toastType: 'error',
};
wss.send({ message: JSON.stringify(toast), uuid });
} else {
toast = {
description: `${data.length} correction(s) to OpenAlex have been saved -
see <a href="https://github.com/dataesr/openalex-affiliations/issues" target="_blank">https://github.com/dataesr/openalex-affiliations/issues</a>`,
id: 'successOpenAlex',
title: 'OpenAlex corrections sent',
toastType: 'success',
};
wss.send({ message: JSON.stringify(toast), uuid });
}
res
.status(firstError?.status ?? 200)
.json({ message: firstError?.message ?? 'GitHub issues created' });
});

export default router;
export {
createIssue,
};
71 changes: 53 additions & 18 deletions server/src/webSocketServer.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,61 @@
import { WebSocketServer } from 'ws';

import { chunkArray } from './utils/utils';
import { createIssue } from './utils/github';

const webSocketServer = new WebSocketServer({ noServer: true, path: '/ws' });
const webSockets = {};

webSocketServer.on('connection', (webSocket, request) => {
const uuid = request?.url.match(/ws\?uuid=([\w-]*)/)[1];
webSockets[uuid] = webSocket;
console.log(`Websocket connection opened: ${uuid}`);
webSocket.on('close', () => {
delete webSockets[uuid];
console.log(`Closing websocket ${uuid}`);
});
webSocketServer.on('connection', (webSocket) => {
webSocket.on('error', console.error);
webSocket.on('open', () => { console.log(`Opening websocket ${uuid}`); });
});
webSocket.on('message', async (json) => {
const { data, email } = JSON.parse(json);
let toast = {
autoDismissAfter: 5000,
description:
'Your correction(s) are currently submitted to the <a href="https://github.com/dataesr/openalex-affiliations/issues" target="_blank">Github repository</a>',
id: 'initOpenAlex',
title: 'OpenAlex corrections submitted',
};
webSocket.send(JSON.stringify(toast));

webSocketServer.send = ({ message, uuid }) => {
if (Object.keys(webSockets).includes(uuid)) {
webSockets[uuid].send(message);
} else {
console.error(`Websocket client ${uuid} does not exist.`);
}
};
const perChunk = 30;
const results = [];
for (const [i, d] of chunkArray({ array: data, perChunk }).entries()) {
const promises = d.map((item) => createIssue(item, email).catch((error) => error));
const r = await Promise.all(promises);
results.push(...r);
toast = {
description: `${Math.min(data.length, (i + 1) * perChunk)} / ${
data.length
} issue(s) submitted`,
id: `processOpenAlex${i}`,
title: 'OpenAlex corrections are being processed',
};
webSocket.send(JSON.stringify(toast));
}

const firstError = results.find(
(result) => !result.status.toString().startsWith('2'),
);
if (firstError?.status) {
toast = {
description: `Error while submitting Github issues : ${firstError?.message}`,
id: 'errorOpenAlex',
title: `Error ${firstError.status}`,
toastType: 'error',
};
webSocket.send(JSON.stringify(toast));
} else {
toast = {
description: `${data.length} correction(s) to OpenAlex have been saved -
see <a href="https://github.com/dataesr/openalex-affiliations/issues" target="_blank">https://github.com/dataesr/openalex-affiliations/issues</a>`,
id: 'successOpenAlex',
title: 'OpenAlex corrections sent',
toastType: 'success',
};
webSocket.send(JSON.stringify(toast));
}
});
});

export default webSocketServer;

0 comments on commit ebfd5c0

Please sign in to comment.