Merged in feature/redirect_issuetracker_links (pull request #283)

Feature/redirect issuetracker links
This commit is contained in:
Eduard Heimbuch
2019-07-08 14:17:24 +00:00
9 changed files with 271 additions and 2 deletions

View File

@@ -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"
}
}

View File

@@ -21,7 +21,13 @@
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>

View File

@@ -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<ScmPathInfoStore> scmPathInfoStoreProvider;
@Inject
public LegacyIndexHalEnricher(Provider<ScmPathInfoStore> 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());
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}

View File

@@ -0,0 +1,14 @@
//@flow
import React from "react";
import { withRouter } from "react-router-dom";
class DummyComponent extends React.Component<Props, State> {
render() {
return (
<>
</>
);
}
}
export default withRouter(DummyComponent);

View File

@@ -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<Props, State> {
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 (
<section className="section">
<div className="container">
<ErrorBoundary>
<ErrorNotification error={error} />
</ErrorBoundary>
</div>
</section>
);
}
return (
<>
{authenticated ? (
this.redirectLegacyRepository()
) : (
<ProtectedRoute
path="/index.html"
component={DummyComponent}
authenticated={authenticated}
/>
)}
</>
);
}
}
binder.bind("main.route", withRouter(LegacyRepositoryRedirect));

View File

@@ -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");
}
}

View File

@@ -122,7 +122,6 @@ class Main extends React.Component<Props> {
component={Profile}
authenticated={authenticated}
/>
<ExtensionPoint
name="main.route"
renderAll={true}