diff --git a/examples/querysheet.http b/examples/querysheet.http index a7b16a6..4938196 100644 --- a/examples/querysheet.http +++ b/examples/querysheet.http @@ -4,6 +4,7 @@ # Authenticate as user POST http://localhost:8080/api/auth/user +Content-Type: application/json {"shareId": "shareId", "authCode":"secret"} -> jq-set-var :token . @@ -15,9 +16,30 @@ Content-Type: application/json {"adminName": "admin", "adminPassword":"123"} -> run-hook (restclient-set-var ":token" (buffer-substring-no-properties 1 (line-end-position))) +:token = Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUxNzY4OTksImV4cCI6MTU5NTM1Njg5OSwic3ViIjoiYWRtaW4ifQ.uRh_xBCrBiLQEBah9I8bYWM-Zph-V_pzQVdaGSU5Mlc + # Get all flings GET http://localhost:8080/api/fling -Authorization: Bearer :token +Content-Type: application/json +:token + +# Add a new fling +POST http://localhost:8080/api/fling +Content-Type: application/json +:token +{"name": "Unshared Fling from querysheet", "expirationClicks": 12, "shared": false} + +# Add a new fling +POST http://localhost:8080/api/fling +Content-Type: application/json +:token +{"name": "Fling from querysheet with Auth", "expirationClicks": 12, "shared": true, "authCode": "abc"} + +# Add a new fling +POST http://localhost:8080/api/fling +Content-Type: application/json +:token +{"name": "Fling from querysheet with Auth and very long name", "expirationClicks": 12, "shared": true, "authCode": "abc"} # :flingId = dfc208a3-5924-43b4-aa6a-c263541dca5e diff --git a/service/fling/src/main/java/net/friedl/fling/service/archive/impl/FileSystemArchive.java b/service/fling/src/main/java/net/friedl/fling/service/archive/impl/FileSystemArchive.java index 4189a55..86eb9f8 100644 --- a/service/fling/src/main/java/net/friedl/fling/service/archive/impl/FileSystemArchive.java +++ b/service/fling/src/main/java/net/friedl/fling/service/archive/impl/FileSystemArchive.java @@ -129,7 +129,11 @@ public class FileSystemArchive implements ArchiveService { Path zipDiskPath = archivePath.resolve(flingId.toString() + ".zip"); log.debug("Deleting fling [.id={}] at {}", flingId, zipDiskPath); - Files.delete(zipDiskPath); + if(Files.exists(zipDiskPath)) { + Files.delete(zipDiskPath); + } else { + log.warn("No fling disk found at {}", zipDiskPath); + } artifactRepository.findAllByFlingId(flingId).forEach(ar -> ar.setArchived(false)); } diff --git a/service/fling/src/test/java/net/friedl/fling/service/archive/FileSystemArchiveTest.java b/service/fling/src/test/java/net/friedl/fling/service/archive/FileSystemArchiveTest.java index 0f6b5bb..112b1e1 100644 --- a/service/fling/src/test/java/net/friedl/fling/service/archive/FileSystemArchiveTest.java +++ b/service/fling/src/test/java/net/friedl/fling/service/archive/FileSystemArchiveTest.java @@ -4,6 +4,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.when; import java.io.File; @@ -218,6 +219,14 @@ public class FileSystemArchiveTest { equalTo(false)); } + @Test + public void deleteFling_zipDiskNotFound_noThrow() throws IOException { + assertThat(Files.exists(tempDir.resolve(artifactEntity2.getFling().getId() + ".zip")), + equalTo(false)); + + assertDoesNotThrow(() -> fileSystemArchive.deleteFling(artifactEntity2.getFling().getId())); + } + private void repopulateArchivePath() throws IOException, URISyntaxException { Files.walkFileTree(tempDir, new SimpleFileVisitor() { @Override diff --git a/web/fling/src/components/admin/FlingAdmin.jsx b/web/fling/src/components/admin/FlingAdmin.jsx index 41d6784..2233132 100644 --- a/web/fling/src/components/admin/FlingAdmin.jsx +++ b/web/fling/src/components/admin/FlingAdmin.jsx @@ -18,7 +18,7 @@ export default function FlingAdmin() { useEffect(() => { if (flingId) { - dispatch(setActiveFling(flingId)) + dispatch(setActiveFling(flingId)); } }, [flingId, dispatch]); diff --git a/web/fling/src/components/admin/FlingTile.jsx b/web/fling/src/components/admin/FlingTile.jsx index c46f82a..4a02862 100644 --- a/web/fling/src/components/admin/FlingTile.jsx +++ b/web/fling/src/components/admin/FlingTile.jsx @@ -3,10 +3,26 @@ import React, { useRef } from 'react'; import classNames from 'classnames'; import { NavLink } from "react-router-dom"; -import { flingClient } from '../../util/flingclient'; +import { useSelector, useDispatch } from "react-redux"; + +import { deleteFling } from "../../redux/actions"; function TileAction(props) { let shareUrlRef = useRef(null); + const dispatch = useDispatch(); + + function copyShareUrl() { + shareUrlRef.current.focus(); + shareUrlRef.current.select(); + + try { + let successful = document.execCommand('copy'); + let msg = successful ? 'successful' : 'unsuccessful'; + console.log('Copying to clipoard ' + msg); + } catch (err) { + log.error("Couldn't copy to clipboard: ", err); + } + } return (
@@ -27,8 +43,7 @@ function TileAction(props) {
  • @@ -36,7 +51,7 @@ function TileAction(props) {
  • @@ -44,34 +59,14 @@ function TileAction(props) {
    ); - function copyShareUrl() { - shareUrlRef.current.focus(); - shareUrlRef.current.select(); - - try { - let successful = document.execCommand('copy'); - let msg = successful ? 'successful' : 'unsuccessful'; - console.log('Copying to clipoard ' + msg); - } catch (err) { - log.error("Couldn't copy to clipboard: ", err); - } - } - - async function deleteFling() { - await flingClient.deleteFling(props.fling.id); - await props.refreshFlingListFn(); - } - - async function toggleShared() { - await flingClient.putFling(props.fling.id, { "sharing": { "shared": !props.fling.shared } }); - await props.refreshFlingListFn(); - } } export default function FlingTile(props) { + const activeFling = useSelector((state) => state.flings.activeFling); + let tileClasses = classNames( "tile", "tile-centered", "p-2", "c-hand", - { "active": props.activeFling === props.fling.id } + { "active": activeFling ? activeFling.id === props.fling.id : false } ); return ( @@ -81,11 +76,13 @@ export default function FlingTile(props) {
    {props.fling.name}
    - 14MB · Public · 1 Jan, 2017 + {`${props.fling.shared ? "Shared" : "Private"}` + + ` · ${(new Date(props.fling.creationTime)).toLocaleDateString()}` + + ` · ${props.fling.authCode ? "Protected" : "Unprotected"}`}
    - + ); diff --git a/web/fling/src/redux/actionTypes.js b/web/fling/src/redux/actionTypes.js index ab31344..8519a5f 100644 --- a/web/fling/src/redux/actionTypes.js +++ b/web/fling/src/redux/actionTypes.js @@ -1,3 +1,4 @@ export const SET_FLINGS = "SET_FLINGS"; export const ADD_FLING = "ADD_FLING"; +export const DELETE_FLING = "DELETE_FLING"; export const SET_ACTIVE_FLING = "SET_ACTIVE_FLING"; diff --git a/web/fling/src/redux/actions.js b/web/fling/src/redux/actions.js index 27a09fe..0e59239 100644 --- a/web/fling/src/redux/actions.js +++ b/web/fling/src/redux/actions.js @@ -24,15 +24,14 @@ function setActiveFlingAction(fling) { } } - function setActiveFling(id) { return (dispatch, getState) => { if (!id) { log.debug(`Not setting active Fling. No id given.`); - return; + return false; } const { flings: { flings } } = getState(); - let foundFling = flings.find(f => f.id === id); + const foundFling = flings.find(f => f.id === id); if (foundFling) { log.info(`Found active fling ${id} in local storage`); @@ -48,7 +47,7 @@ function setActiveFling(id) { dispatch(setActiveFlingAction(fling)) }) .catch(error => { - log.warn(`Could not find active fling. ` + + log.warn(`Could not find active fling: ${error} \n` + `Resetting active fling`); dispatch(setActiveFlingAction(undefined)); }) @@ -57,11 +56,34 @@ function setActiveFling(id) { } function retrieveFlings() { - return (dispatch) => { - let flingClient = new FlingClient(); + return (dispatch, getState) => { + const { flings: { activeFling } } = getState(); + const flingClient = new FlingClient(); + flingClient.getFlings() - .then(flings => dispatch(setFlingsAction(flings))); + .then(flings => { + dispatch(setFlingsAction(flings)); + if (activeFling) { + dispatch(setActiveFling(activeFling.id)); + } + }); } } -export { retrieveFlings, setActiveFling }; +function deleteFling(id) { + return (dispatch, getState) => { + if (!id) { + log.debug(`Not deleting Fling. No id given.`); + return; + } + + const flingClient = new FlingClient(); + + flingClient.deleteFling(id) + .then(() => dispatch(retrieveFlings())) + .catch(error => + log.error(`Could not delete fling ${id}: ${error}`)); + } +} + +export { retrieveFlings, setActiveFling, deleteFling }; diff --git a/web/fling/src/redux/reducers/flings.js b/web/fling/src/redux/reducers/flings.js index f5d1f08..31f3ac8 100644 --- a/web/fling/src/redux/reducers/flings.js +++ b/web/fling/src/redux/reducers/flings.js @@ -1,3 +1,4 @@ +import log from "loglevel"; import produce from "immer"; import { SET_FLINGS, SET_ACTIVE_FLING, ADD_FLING } from "../actionTypes"; @@ -16,7 +17,19 @@ export default produce((draft, action) => { draft.flings = action.payload; break; case ADD_FLING: - draft.flings.push(action.payload); + // Check storage again here, otherwise there could be a race + // condition due to async calls of SET_FLINGS and ADD_FLING + let foundFlingIdx = draft.flings.findIndex(fling => + fling.id === action.payload.id); + + if (foundFlingIdx === -1) { + log.debug(`Adding new fling with id ${action.payload.id}`) + draft.flings.push(action.payload); + } else { + log.debug(`Fling already exists. ` + + `Updating fling with id ${action.payload.id}`) + draft.flings[foundFlingIdx] = action.payload + } break; case SET_ACTIVE_FLING: draft.activeFling = action.payload; diff --git a/web/fling/src/redux/selectorTypes.js b/web/fling/src/redux/selectorTypes.js deleted file mode 100644 index 0d0fda6..0000000 --- a/web/fling/src/redux/selectorTypes.js +++ /dev/null @@ -1,3 +0,0 @@ -export const FLING_FILTERS = { - ALL: "all" -} diff --git a/web/fling/src/redux/selectors.js b/web/fling/src/redux/selectors.js deleted file mode 100644 index e69de29..0000000 diff --git a/web/fling/src/redux/selectors/flingSelectors.js b/web/fling/src/redux/selectors/flingSelectors.js deleted file mode 100644 index a6126f2..0000000 --- a/web/fling/src/redux/selectors/flingSelectors.js +++ /dev/null @@ -1,10 +0,0 @@ -import { FLING_FILTERS } from "../selectorTypes"; - -export const flingSelector = (store, flingFilter) => { - switch(flingFilter) { - case FLING_FILTERS.ALL: - return store.flings.flings; - default: - return []; - } -}