Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

267 add url test to resources #306

Merged
merged 36 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
519ef2c
add debounce util function
sorochak Jan 15, 2024
4c9dcbf
added checkURL misc util function
sorochak Jan 15, 2024
1c0815b
validateURL already defined in validate.js, remove definition from Re…
sorochak Jan 16, 2024
cfe7e21
add isURLActive
sorochak Jan 16, 2024
42d78ba
update debounce to handle promises and be more readable
sorochak Jan 16, 2024
44dc768
add isURLActive Firebase Function
sorochak Jan 16, 2024
3ccfb11
fix url assignment
sorochak Jan 17, 2024
fc5749f
use url variable
sorochak Jan 17, 2024
8a829d8
Merge remote-tracking branch 'origin/main' into 267-add-url-test-to-r…
sorochak Jan 17, 2024
045dc6c
update checkURLActive function
sorochak Jan 17, 2024
7b9584f
implement checkURLActive call in Resources component
sorochak Jan 17, 2024
c1b71d5
add more debugging logs for checkURLActive:
sorochak Jan 23, 2024
ec2d3f6
broken - debounce url input
sorochak Jan 24, 2024
b05b0db
urlMessage
sorochak Jan 24, 2024
ae48f4a
provide positive feedback for active URL
sorochak Jan 24, 2024
1c69d16
this seems to get pretty close to what we want. There is still an err…
fostermh Jan 24, 2024
463cfef
Merge branch '267-add-url-test-to-resources' of https://github.com/ci…
fostermh Jan 24, 2024
7fa9b7b
url warning validation - broken
sorochak Jan 25, 2024
edd8ce1
Merge branch '267-add-url-test-to-resources' of github.com:cioos-sioo…
sorochak Jan 25, 2024
377f86b
super hack
fostermh Jan 25, 2024
8671b68
remove unused custom debounce util func
sorochak Jan 25, 2024
e98c5f9
reduce debounce time from 3s to 1s
sorochak Jan 25, 2024
9d243df
fix 'record' naming collision as per linter error
sorochak Jan 25, 2024
510cf09
fix linter warning Assignment to function parameter 'val'
sorochak Jan 25, 2024
c1b8cf4
Refactor to replace for...of loop with array methods as per linter wa…
sorochak Jan 25, 2024
e5b7188
fix memory leak errors
sorochak Jan 25, 2024
423361f
reduce debounce time
sorochak Jan 25, 2024
e251517
cleanup logs and unused code
sorochak Jan 25, 2024
ea8303f
remove irrelevant comment
sorochak Jan 25, 2024
c75ff37
Merge remote-tracking branch 'origin/main' into 267-add-url-test-to-r…
sorochak Jan 25, 2024
1f93788
attempt to solve debounce bug
sorochak Jan 25, 2024
6a248dc
reduce debounce time
sorochak Jan 25, 2024
deb4982
add headings for errors & warnings on submit page
sorochak Jan 26, 2024
c107106
fix debounce on resouces
fostermh Jan 26, 2024
9934569
Merge remote-tracking branch 'origin/da_debounce' into 267-add-url-te…
sorochak Jan 26, 2024
2c97aba
cleanup useEffect
sorochak Jan 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions firebase-functions/functions/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const admin = require("firebase-admin");
const { translate } = require("./translate");
const { createDraftDoi, updateDraftDoi, deleteDraftDoi } = require("./datacite");
const { checkURLActive } = require("./serverUtils");
const { notifyReviewer, notifyUser } = require("./notify");
const {
updatesRecordCreate,
Expand All @@ -23,3 +24,4 @@ exports.regenerateXMLforRecord = regenerateXMLforRecord;
exports.createDraftDoi = createDraftDoi;
exports.deleteDraftDoi = deleteDraftDoi;
exports.updateDraftDoi = updateDraftDoi;
exports.checkURLActive = checkURLActive;
25 changes: 25 additions & 0 deletions firebase-functions/functions/serverUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const functions = require("firebase-functions");
const fetch = require('node-fetch');

// Function to check if a given URL is active
exports.checkURLActive = functions.https.onCall(async (data) => {
let url = data;
functions.logger.log('Received URL:', url);

if (!url) {
throw new functions.https.HttpsError('invalid-argument', 'The function must be called with one argument "url".');
}

// Prepend 'http://' if the URL does not start with 'http://' or 'https://'
if (!url.startsWith('http://') && !url.startsWith('https://')) {
url = 'http://' + url;
}

try {
const response = await fetch(url, {method: "HEAD" });
return response.ok; // Return true if response is OK, otherwise false
} catch (error) {
console.error('Error in checkURLActive:', error); // Log the error for debugging
return false; // Return false if an error occurs
}
})
55 changes: 42 additions & 13 deletions src/components/FormComponents/Resources.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useContext, useState, useEffect, useCallback } from "react";
import {
Add,
Delete,
Expand All @@ -11,14 +11,40 @@ import { En, Fr, I18n } from "../I18n";

import BilingualTextInput from "./BilingualTextInput";
import RequiredMark from "./RequiredMark";
import { deepCopy } from "../../utils/misc";
import { deepCopy, debounce } from "../../utils/misc";
import { validateURL } from "../../utils/validate";
import { QuestionText, paperClass, SupplementalText } from "./QuestionStyles";

const validateURL = (url) => !url || validator.isURL(url);
import { UserContext } from "../../providers/UserProvider";

const Resources = ({ updateResources, resources, disabled }) => {

const { checkURLActive } = useContext(UserContext);

const [urlIsActive, setUrlIsActive] = useState(false);
const emptyResource = { url: "", name: "", description: { en: "", fr: "" } };

const debouncedcheckURLActive = useCallback(
debounce(checkURLActive, 500),
[checkURLActive]
);

useEffect(() => {
resources.forEach((resource, index) => {
if (resource.url && validateURL(resource.url)) {
debouncedcheckURLActive(resource.url)
.then((isActive) => {
setUrlIsActive((prevStatus) => ({ ...prevStatus, [index]: isActive }));
})
.catch(() => {
setUrlIsActive((prevStatus) => ({ ...prevStatus, [index]: false }));
});
} else {
// URL is empty, set isActive to true
setUrlIsActive((prevStatus) => ({ ...prevStatus, [index]: true }));
}
});
}, [resources, debouncedcheckURLActive]);

function addResource() {
updateResources(resources.concat(deepCopy(emptyResource)));
}
Expand All @@ -35,13 +61,15 @@ const Resources = ({ updateResources, resources, disabled }) => {
resources.splice(newIndex, 0, element);
updateResources(resources);
}

const nameLabel = <I18n en="Name" fr="Titre" />;
const descriptionLabel = <I18n en="Description" fr="Description" />;

return (
<div>
{resources.map((dist = deepCopy(emptyResource), i) => {
const urlIsValid = !dist.url || validateURL(dist.url);
{resources.map((resourceItem = deepCopy(emptyResource), i) => {
const urlIsValid = !resourceItem.url || validateURL(resourceItem.url);

function handleResourceChange(key) {
return (e) => {
const newValue = [...resources];
Expand All @@ -58,11 +86,11 @@ const Resources = ({ updateResources, resources, disabled }) => {
<En>Enter a name for the resource</En>
<Fr>Entrez un titre pour la ressource</Fr>
</I18n>
<RequiredMark passes={dist.name?.en || dist.name?.fr} />
<RequiredMark passes={resourceItem.name?.en || resourceItem.name?.fr} />
</QuestionText>
<BilingualTextInput
label={nameLabel}
value={dist.name}
value={resourceItem.name}
onChange={handleResourceChange("name")}
fullWidth
disabled={disabled}
Expand All @@ -75,7 +103,7 @@ const Resources = ({ updateResources, resources, disabled }) => {
<Fr>Entrez l'URL de la ressource</Fr>
</I18n>

<RequiredMark passes={validator.isURL(dist.url)} />
<RequiredMark passes={validator.isURL(resourceItem.url)} />
<SupplementalText>
<I18n>
<En>
Expand All @@ -99,11 +127,12 @@ const Resources = ({ updateResources, resources, disabled }) => {

<TextField
helperText={
!urlIsValid && <I18n en="Invalid URL" fr="URL non valide" />
(!urlIsValid && <I18n en="Invalid URL" fr="URL non valide" />)
|| (urlIsActive[i] && urlIsActive[i].data === false && <I18n en="URL is not active" fr="L'URL n'est pas active" />)
}
error={!urlIsValid}
error={!urlIsValid || (urlIsActive[i] && urlIsActive[i].data === false)}
label="URL"
value={dist.url}
value={resourceItem.url}
onChange={handleResourceChange("url")}
fullWidth
disabled={disabled}
Expand All @@ -119,7 +148,7 @@ const Resources = ({ updateResources, resources, disabled }) => {
<BilingualTextInput
name="description"
label={descriptionLabel}
value={dist.description}
value={resourceItem.description}
onChange={handleResourceChange("description")}
disabled={disabled}
/>
Expand Down
2 changes: 2 additions & 0 deletions src/providers/UserProvider.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class UserProvider extends FormClassTemplate {
const createDraftDoi = firebase.functions().httpsCallable("createDraftDoi");
const updateDraftDoi = firebase.functions().httpsCallable("updateDraftDoi");
const deleteDraftDoi = firebase.functions().httpsCallable("deleteDraftDoi");
const checkURLActive = firebase.functions().httpsCallable("checkURLActive");

return (
<UserContext.Provider
Expand All @@ -98,6 +99,7 @@ class UserProvider extends FormClassTemplate {
createDraftDoi,
updateDraftDoi,
deleteDraftDoi,
checkURLActive,
}}
>
{children}
Expand Down
30 changes: 30 additions & 0 deletions src/utils/misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,36 @@ export function deepCopy(obj) {
export function deepEquals(obj1, obj2) {
return JSON.stringify(obj1) === JSON.stringify(obj2);
}

/**
* Creates a debounced version of a function.
*
* @param {Function} mainFunction - The function to debounce.
* @param {number} delay - The amount of time (in milliseconds) to delay.
* @return {Function} A debounced version of the specified function.
*/
export const debounce = (mainFunction, delay) => {
let timer;

const debouncedFunction = (...args) => {
// Return a promise that resolves or rejects based on the mainFunction's execution
return new Promise((resolve, reject) => {
// Clear existing timer to reset the debounce period
clearTimeout(timer);

// Set a new timer to delay the execution of mainFunction
timer = setTimeout(() => {
// Execute mainFunction and handle its promise
Promise.resolve(mainFunction(...args))
.then(resolve)
.catch(reject);
}, delay);
});
};

return debouncedFunction;
}

/*
Convert firebase to javascript, mostly just used to get real array elements
*/
Expand Down
Loading