diff --git a/scm-plugins/scm-legacy-plugin/package.json b/scm-plugins/scm-legacy-plugin/package.json new file mode 100644 index 0000000000..84f78f419a --- /dev/null +++ b/scm-plugins/scm-legacy-plugin/package.json @@ -0,0 +1,20 @@ +{ + "name": "@scm-manager/legacy-plugin", + "license": "BSD-3-Clause", + "main": "src/main/js/index.js", + "scripts": { + "build": "ui-bundler plugin", + "watch": "ui-bundler plugin -w", + "lint": "ui-bundler lint", + "flow": "flow check" + }, + "dependencies": { + "@scm-manager/ui-components": "latest", + "@scm-manager/ui-extensions": "^0.1.1", + "react-redux": "^5.0.7", + "@scm-manager/ui-types": "latest" + }, + "devDependencies": { + "@scm-manager/ui-bundler": "^0.0.25" + } +} diff --git a/scm-plugins/scm-legacy-plugin/pom.xml b/scm-plugins/scm-legacy-plugin/pom.xml index 52aff51dd2..6cfa74ea61 100644 --- a/scm-plugins/scm-legacy-plugin/pom.xml +++ b/scm-plugins/scm-legacy-plugin/pom.xml @@ -21,7 +21,13 @@ ${servlet.version} provided - + + javax.ws.rs + jsr311-api + 1.1.1 + compile + + diff --git a/scm-plugins/scm-legacy-plugin/src/main/java/sonia/scm/legacy/LegacyIndexHalEnricher.java b/scm-plugins/scm-legacy-plugin/src/main/java/sonia/scm/legacy/LegacyIndexHalEnricher.java new file mode 100644 index 0000000000..0914f92e25 --- /dev/null +++ b/scm-plugins/scm-legacy-plugin/src/main/java/sonia/scm/legacy/LegacyIndexHalEnricher.java @@ -0,0 +1,38 @@ +package sonia.scm.legacy; + +import com.google.inject.Inject; +import sonia.scm.api.v2.resources.Enrich; +import sonia.scm.api.v2.resources.HalAppender; +import sonia.scm.api.v2.resources.HalEnricher; +import sonia.scm.api.v2.resources.HalEnricherContext; +import sonia.scm.api.v2.resources.Index; +import sonia.scm.api.v2.resources.LinkBuilder; +import sonia.scm.api.v2.resources.ScmPathInfoStore; +import sonia.scm.plugin.Extension; + +import javax.inject.Provider; + +@Extension +@Enrich(Index.class) +public class LegacyIndexHalEnricher implements HalEnricher { + + private Provider scmPathInfoStoreProvider; + + @Inject + public LegacyIndexHalEnricher(Provider scmPathInfoStoreProvider) { + this.scmPathInfoStoreProvider = scmPathInfoStoreProvider; + } + + private String createLink() { + return new LinkBuilder(scmPathInfoStoreProvider.get().get(), LegacyRepositoryService.class) + .method("getNameAndNamespaceForRepositoryId") + .parameters("REPOID") + .href() + .replace("REPOID", "{id}"); + } + + @Override + public void enrich(HalEnricherContext context, HalAppender appender) { + appender.appendLink("nameAndNamespace", createLink()); + } +} diff --git a/scm-plugins/scm-legacy-plugin/src/main/java/sonia/scm/legacy/LegacyRepositoryService.java b/scm-plugins/scm-legacy-plugin/src/main/java/sonia/scm/legacy/LegacyRepositoryService.java new file mode 100644 index 0000000000..d6b923a927 --- /dev/null +++ b/scm-plugins/scm-legacy-plugin/src/main/java/sonia/scm/legacy/LegacyRepositoryService.java @@ -0,0 +1,43 @@ +package sonia.scm.legacy; + +import com.google.inject.Inject; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; +import sonia.scm.NotFoundException; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryManager; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("v2/legacy/repository") +public class LegacyRepositoryService { + + private RepositoryManager repositoryManager; + + @Inject + public LegacyRepositoryService(RepositoryManager repositoryManager) { + this.repositoryManager = repositoryManager; + } + + @GET + @Path("{id}") + @Produces(MediaType.APPLICATION_JSON) + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), + @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"repository:read:global\" privilege"), + @ResponseCode(code = 500, condition = "internal server error") + }) + public NamespaceAndNameDto getNameAndNamespaceForRepositoryId(@PathParam("id") String repositoryId) { + Repository repo = repositoryManager.get(repositoryId); + if (repo == null) { + throw new NotFoundException(Repository.class, repositoryId); + } + return new NamespaceAndNameDto(repo.getName(), repo.getNamespace()); + } +} + diff --git a/scm-plugins/scm-legacy-plugin/src/main/java/sonia/scm/legacy/NamespaceAndNameDto.java b/scm-plugins/scm-legacy-plugin/src/main/java/sonia/scm/legacy/NamespaceAndNameDto.java new file mode 100644 index 0000000000..2f7aae2dae --- /dev/null +++ b/scm-plugins/scm-legacy-plugin/src/main/java/sonia/scm/legacy/NamespaceAndNameDto.java @@ -0,0 +1,11 @@ +package sonia.scm.legacy; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class NamespaceAndNameDto { + private String name; + private String namespace; +} diff --git a/scm-plugins/scm-legacy-plugin/src/main/js/DummyComponent.js b/scm-plugins/scm-legacy-plugin/src/main/js/DummyComponent.js new file mode 100644 index 0000000000..396558f852 --- /dev/null +++ b/scm-plugins/scm-legacy-plugin/src/main/js/DummyComponent.js @@ -0,0 +1,14 @@ +//@flow +import React from "react"; +import { withRouter } from "react-router-dom"; + +class DummyComponent extends React.Component { + render() { + return ( + <> + + ); + } +} + +export default withRouter(DummyComponent); diff --git a/scm-plugins/scm-legacy-plugin/src/main/js/index.js b/scm-plugins/scm-legacy-plugin/src/main/js/index.js new file mode 100644 index 0000000000..97c3eb7e32 --- /dev/null +++ b/scm-plugins/scm-legacy-plugin/src/main/js/index.js @@ -0,0 +1,95 @@ +// @flow + +import React from "react"; +import { withRouter } from "react-router-dom"; +import { binder } from "@scm-manager/ui-extensions"; +import { + ProtectedRoute, + apiClient, + ErrorNotification, + ErrorBoundary +} from "@scm-manager/ui-components"; +import DummyComponent from "./DummyComponent"; +import type {Links} from "@scm-manager/ui-types"; + +type Props = { + authenticated?: boolean, + links: Links, + + //context objects + history: History +}; + +type State = { + error?: Error +}; + +class LegacyRepositoryRedirect extends React.Component { + constructor(props: Props, state: State) { + super(props, state); + this.state = { error: null }; + } + + handleError = (error: Error) => { + this.setState({ + error + }); + }; + + redirectLegacyRepository() { + const { history, links } = this.props; + if (location.href && location.href.includes("#diffPanel;")) { + let splittedUrl = location.href.split(";"); + let repoId = splittedUrl[1]; + let changeSetId = splittedUrl[2]; + + apiClient + .get(links.nameAndNamespace.href.replace("{id}", repoId)) + .then(response => response.json()) + .then(payload => + history.push( + "/repo/" + + payload.namespace + + "/" + + payload.name + + "/changeset/" + + changeSetId + ) + ) + .catch(this.handleError); + } + } + + render() { + const { authenticated } = this.props; + const { error } = this.state; + + if (error) { + return ( +
+
+ + + +
+
+ ); + } + + return ( + <> + {authenticated ? ( + this.redirectLegacyRepository() + ) : ( + + )} + + ); + } +} + +binder.bind("main.route", withRouter(LegacyRepositoryRedirect)); diff --git a/scm-plugins/scm-legacy-plugin/src/test/java/sonia/scm/legacy/LegacyRepositoryServiceTest.java b/scm-plugins/scm-legacy-plugin/src/test/java/sonia/scm/legacy/LegacyRepositoryServiceTest.java new file mode 100644 index 0000000000..169c80eae2 --- /dev/null +++ b/scm-plugins/scm-legacy-plugin/src/test/java/sonia/scm/legacy/LegacyRepositoryServiceTest.java @@ -0,0 +1,43 @@ +package sonia.scm.legacy; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import sonia.scm.NotFoundException; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryManager; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class LegacyRepositoryServiceTest { + + @Mock + private RepositoryManager repositoryManager; + + private LegacyRepositoryService legacyRepositoryService; + private final Repository repository = new Repository("abc123", "git", "space", "repo"); + + @Before + public void init() { + legacyRepositoryService = new LegacyRepositoryService(repositoryManager); + } + + @Test + public void findRepositoryNameSpaceAndNameForRepositoryId() { + when(repositoryManager.get(any(String.class))).thenReturn(repository); + NamespaceAndNameDto namespaceAndName = legacyRepositoryService.getNameAndNamespaceForRepositoryId("abc123"); + assertThat(namespaceAndName.getName()).isEqualToIgnoringCase("repo"); + assertThat(namespaceAndName.getNamespace()).isEqualToIgnoringCase("space"); + } + + @Test(expected = NotFoundException.class) + public void shouldGetNotFoundExceptionIfRepositoryNotExists() throws NotFoundException { + when(repositoryManager.get(any(String.class))).thenReturn(null); + legacyRepositoryService.getNameAndNamespaceForRepositoryId("456def"); + } +} diff --git a/scm-ui/src/containers/Main.js b/scm-ui/src/containers/Main.js index 8681f2457b..1d358ecc87 100644 --- a/scm-ui/src/containers/Main.js +++ b/scm-ui/src/containers/Main.js @@ -122,7 +122,6 @@ class Main extends React.Component { component={Profile} authenticated={authenticated} /> -