mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 22:45:45 +01:00
Get links for repositories by namespaces from server
This commit is contained in:
@@ -39,12 +39,19 @@ export type RepositoryCreation = Repository & {
|
|||||||
contextEntries: { [key: string]: any };
|
contextEntries: { [key: string]: any };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Namespace = {
|
||||||
|
namespace: string;
|
||||||
|
_links: Links;
|
||||||
|
};
|
||||||
|
|
||||||
export type RepositoryCollection = PagedCollection & {
|
export type RepositoryCollection = PagedCollection & {
|
||||||
_embedded: {
|
_embedded: {
|
||||||
repositories: Repository[] | string[];
|
repositories: Repository[] | string[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type NamespaceCollection = Namespace[];
|
||||||
|
|
||||||
export type RepositoryGroup = {
|
export type RepositoryGroup = {
|
||||||
name: string;
|
name: string;
|
||||||
repositories: Repository[];
|
repositories: Repository[];
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export { Me } from "./Me";
|
|||||||
export { DisplayedUser, User } from "./User";
|
export { DisplayedUser, User } from "./User";
|
||||||
export { Group, Member } from "./Group";
|
export { Group, Member } from "./Group";
|
||||||
|
|
||||||
export { Repository, RepositoryCollection, RepositoryGroup, RepositoryCreation } from "./Repositories";
|
export { Repository, RepositoryCollection, RepositoryGroup, RepositoryCreation, Namespace, NamespaceCollection } from "./Repositories";
|
||||||
export { RepositoryType, RepositoryTypeCollection } from "./RepositoryTypes";
|
export { RepositoryType, RepositoryTypeCollection } from "./RepositoryTypes";
|
||||||
|
|
||||||
export { Branch, BranchRequest } from "./Branches";
|
export { Branch, BranchRequest } from "./Branches";
|
||||||
|
|||||||
@@ -234,6 +234,10 @@ export function getRepositoriesLink(state: object) {
|
|||||||
return getLink(state, "repositories");
|
return getLink(state, "repositories");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getNamespacesLink(state: object) {
|
||||||
|
return getLink(state, "namespaces");
|
||||||
|
}
|
||||||
|
|
||||||
export function getHgConfigLink(state: object) {
|
export function getHgConfigLink(state: object) {
|
||||||
return getLink(state, "hgConfig");
|
return getLink(state, "hgConfig");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import React from "react";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { RouteComponentProps, withRouter } from "react-router-dom";
|
import { RouteComponentProps, withRouter } from "react-router-dom";
|
||||||
import { WithTranslation, withTranslation } from "react-i18next";
|
import { WithTranslation, withTranslation } from "react-i18next";
|
||||||
import { RepositoryCollection } from "@scm-manager/ui-types";
|
import { NamespaceCollection, RepositoryCollection } from "@scm-manager/ui-types";
|
||||||
import {
|
import {
|
||||||
CreateButton,
|
CreateButton,
|
||||||
LinkPaginator,
|
LinkPaginator,
|
||||||
@@ -35,13 +35,16 @@ import {
|
|||||||
PageActions,
|
PageActions,
|
||||||
urls
|
urls
|
||||||
} from "@scm-manager/ui-components";
|
} from "@scm-manager/ui-components";
|
||||||
import { getRepositoriesLink } from "../../modules/indexResource";
|
import { getNamespacesLink, getRepositoriesLink } from "../../modules/indexResource";
|
||||||
import {
|
import {
|
||||||
fetchReposByPage,
|
fetchReposByPage,
|
||||||
getFetchReposFailure,
|
getFetchReposFailure,
|
||||||
getRepositoryCollection,
|
getRepositoryCollection,
|
||||||
isAbleToCreateRepos,
|
isAbleToCreateRepos,
|
||||||
isFetchReposPending
|
isFetchReposPending,
|
||||||
|
isFetchNamespacesPending,
|
||||||
|
getNamespaceCollection,
|
||||||
|
fetchNamespaces
|
||||||
} from "../modules/repos";
|
} from "../modules/repos";
|
||||||
import RepositoryList from "../components/list";
|
import RepositoryList from "../components/list";
|
||||||
|
|
||||||
@@ -51,30 +54,52 @@ type Props = WithTranslation &
|
|||||||
error: Error;
|
error: Error;
|
||||||
showCreateButton: boolean;
|
showCreateButton: boolean;
|
||||||
collection: RepositoryCollection;
|
collection: RepositoryCollection;
|
||||||
|
namespaces: NamespaceCollection;
|
||||||
page: number;
|
page: number;
|
||||||
namespace: string;
|
namespace: string;
|
||||||
reposLink: string;
|
reposLink: string;
|
||||||
|
namespacesLink: string;
|
||||||
|
|
||||||
// dispatched functions
|
// dispatched functions
|
||||||
fetchReposByPage: (link: string, page: number, namespace?: string, filter?: string) => void;
|
fetchReposByPage: (link: string, page: number, namespace?: string, filter?: string) => void;
|
||||||
|
fetchNamespaces: (link: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Overview extends React.Component<Props> {
|
class Overview extends React.Component<Props> {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { fetchReposByPage, reposLink, namespace, page, location } = this.props;
|
const { fetchReposByPage, fetchNamespaces, namespacesLink, namespace, page, location } = this.props;
|
||||||
fetchReposByPage(reposLink, page, namespace, urls.getQueryStringFromLocation(location));
|
fetchNamespaces(namespacesLink);
|
||||||
|
const link = this.getReposLink();
|
||||||
|
if (link) {
|
||||||
|
fetchReposByPage(link, page, namespace, urls.getQueryStringFromLocation(location));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate = (prevProps: Props) => {
|
componentDidUpdate = (prevProps: Props) => {
|
||||||
const { loading, collection, namespace, page, reposLink, location, fetchReposByPage } = this.props;
|
const { loading, collection, namespaces, namespace, page, location, fetchReposByPage } = this.props;
|
||||||
if (collection && page && !loading) {
|
if (!collection && namespace && prevProps.namespaces !== namespaces) {
|
||||||
|
const link = this.getReposLink();
|
||||||
|
fetchReposByPage(link, page, namespace, urls.getQueryStringFromLocation(location));
|
||||||
|
} else if (collection && page && !loading) {
|
||||||
const statePage: number = collection.page + 1;
|
const statePage: number = collection.page + 1;
|
||||||
if (page !== statePage || prevProps.location.search !== location.search) {
|
if (page !== statePage || prevProps.location.search !== location.search) {
|
||||||
fetchReposByPage(reposLink, page, namespace, urls.getQueryStringFromLocation(location));
|
const link = this.getReposLink();
|
||||||
|
if (link) {
|
||||||
|
fetchReposByPage(link, page, namespace, urls.getQueryStringFromLocation(location));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getReposLink = () => {
|
||||||
|
const { namespace, namespaces, reposLink } = this.props;
|
||||||
|
if (namespace) {
|
||||||
|
return namespaces?.find(n => n.namespace === namespace)?._links?.repositories?.href;
|
||||||
|
} else {
|
||||||
|
return reposLink;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { error, loading, showCreateButton, namespace, t } = this.props;
|
const { error, loading, showCreateButton, namespace, t } = this.props;
|
||||||
|
|
||||||
@@ -134,26 +159,33 @@ class Overview extends React.Component<Props> {
|
|||||||
const mapStateToProps = (state: any, ownProps: Props) => {
|
const mapStateToProps = (state: any, ownProps: Props) => {
|
||||||
const { match } = ownProps;
|
const { match } = ownProps;
|
||||||
const collection = getRepositoryCollection(state);
|
const collection = getRepositoryCollection(state);
|
||||||
const loading = isFetchReposPending(state);
|
const namespaces = getNamespaceCollection(state);
|
||||||
|
const loading = isFetchReposPending(state) || isFetchNamespacesPending(state);
|
||||||
const error = getFetchReposFailure(state);
|
const error = getFetchReposFailure(state);
|
||||||
const { namespace, page } = urls.getNamespaceAndPageFromMatch(match);
|
const { namespace, page } = urls.getNamespaceAndPageFromMatch(match);
|
||||||
const showCreateButton = isAbleToCreateRepos(state);
|
const showCreateButton = isAbleToCreateRepos(state);
|
||||||
const reposLink = getRepositoriesLink(state);
|
const reposLink = getRepositoriesLink(state);
|
||||||
|
const namespacesLink = getNamespacesLink(state);
|
||||||
return {
|
return {
|
||||||
collection,
|
collection,
|
||||||
|
namespaces,
|
||||||
loading,
|
loading,
|
||||||
error,
|
error,
|
||||||
page,
|
page,
|
||||||
namespace,
|
namespace,
|
||||||
showCreateButton,
|
showCreateButton,
|
||||||
reposLink
|
reposLink,
|
||||||
|
namespacesLink
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch: any) => {
|
const mapDispatchToProps = (dispatch: any) => {
|
||||||
return {
|
return {
|
||||||
fetchReposByPage: (link: string, page: number, namespace?: string, filter?: string) => {
|
fetchReposByPage: (link: string, page: number, filter?: string) => {
|
||||||
dispatch(fetchReposByPage(link, page, namespace, filter));
|
dispatch(fetchReposByPage(link, page, filter));
|
||||||
|
},
|
||||||
|
fetchNamespaces: (link: string) => {
|
||||||
|
dispatch(fetchNamespaces(link));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
import { apiClient } from "@scm-manager/ui-components";
|
import { apiClient } from "@scm-manager/ui-components";
|
||||||
import * as types from "../../modules/types";
|
import * as types from "../../modules/types";
|
||||||
import { Action, Repository, RepositoryCollection, RepositoryCreation } from "@scm-manager/ui-types";
|
import { Action, Repository, RepositoryCollection, RepositoryCreation, NamespaceCollection } from "@scm-manager/ui-types";
|
||||||
import { isPending } from "../../modules/pending";
|
import { isPending } from "../../modules/pending";
|
||||||
import { getFailure } from "../../modules/failure";
|
import { getFailure } from "../../modules/failure";
|
||||||
|
|
||||||
@@ -33,6 +33,11 @@ export const FETCH_REPOS_PENDING = `${FETCH_REPOS}_${types.PENDING_SUFFIX}`;
|
|||||||
export const FETCH_REPOS_SUCCESS = `${FETCH_REPOS}_${types.SUCCESS_SUFFIX}`;
|
export const FETCH_REPOS_SUCCESS = `${FETCH_REPOS}_${types.SUCCESS_SUFFIX}`;
|
||||||
export const FETCH_REPOS_FAILURE = `${FETCH_REPOS}_${types.FAILURE_SUFFIX}`;
|
export const FETCH_REPOS_FAILURE = `${FETCH_REPOS}_${types.FAILURE_SUFFIX}`;
|
||||||
|
|
||||||
|
export const FETCH_NAMESPACES = "scm/repos/FETCH_NAMESPACES";
|
||||||
|
export const FETCH_NAMESPACES_PENDING = `${FETCH_NAMESPACES}_${types.PENDING_SUFFIX}`;
|
||||||
|
export const FETCH_NAMESPACES_SUCCESS = `${FETCH_NAMESPACES}_${types.SUCCESS_SUFFIX}`;
|
||||||
|
export const FETCH_NAMESPACES_FAILURE = `${FETCH_NAMESPACES}_${types.FAILURE_SUFFIX}`;
|
||||||
|
|
||||||
export const FETCH_REPO = "scm/repos/FETCH_REPO";
|
export const FETCH_REPO = "scm/repos/FETCH_REPO";
|
||||||
export const FETCH_REPO_PENDING = `${FETCH_REPO}_${types.PENDING_SUFFIX}`;
|
export const FETCH_REPO_PENDING = `${FETCH_REPO}_${types.PENDING_SUFFIX}`;
|
||||||
export const FETCH_REPO_SUCCESS = `${FETCH_REPO}_${types.SUCCESS_SUFFIX}`;
|
export const FETCH_REPO_SUCCESS = `${FETCH_REPO}_${types.SUCCESS_SUFFIX}`;
|
||||||
@@ -67,11 +72,10 @@ export function fetchRepos(link: string) {
|
|||||||
return fetchReposByLink(link);
|
return fetchReposByLink(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchReposByPage(link: string, page: number, namespace?: string, filter?: string) {
|
export function fetchReposByPage(link: string, page: number, filter?: string) {
|
||||||
const namespacePath = namespace ? `${namespace}/` : "";
|
const linkWithPage = `${link}?page=${page - 1}`;
|
||||||
const linkWithPage = `${link}${namespacePath}?page=${page - 1}`;
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
return fetchReposByLink(`${linkWithPage}}&q=${decodeURIComponent(filter)}`);
|
return fetchReposByLink(`${linkWithPage}&q=${decodeURIComponent(filter)}`);
|
||||||
}
|
}
|
||||||
return fetchReposByLink(linkWithPage);
|
return fetchReposByLink(linkWithPage);
|
||||||
}
|
}
|
||||||
@@ -125,6 +129,42 @@ export function fetchReposFailure(err: Error): Action {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fetch namespaces
|
||||||
|
export function fetchNamespaces(link: string) {
|
||||||
|
return function(dispatch: any) {
|
||||||
|
dispatch(fetchNamespacesPending());
|
||||||
|
return apiClient
|
||||||
|
.get(link)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(namespaces => {
|
||||||
|
dispatch(fetchNamespacesSuccess(namespaces));
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
dispatch(fetchNamespacesFailure(err));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchNamespacesPending(): Action {
|
||||||
|
return {
|
||||||
|
type: FETCH_NAMESPACES_PENDING
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchNamespacesSuccess(namespaces: NamespaceCollection): Action {
|
||||||
|
return {
|
||||||
|
type: FETCH_NAMESPACES_SUCCESS,
|
||||||
|
payload: namespaces
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchNamespacesFailure(err: Error): Action {
|
||||||
|
return {
|
||||||
|
type: FETCH_NAMESPACES_FAILURE,
|
||||||
|
payload: err
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// fetch repo
|
// fetch repo
|
||||||
export function fetchRepoByLink(repo: Repository) {
|
export function fetchRepoByLink(repo: Repository) {
|
||||||
return fetchRepo(repo._links.self.href, repo.namespace, repo.name);
|
return fetchRepo(repo._links.self.href, repo.namespace, repo.name);
|
||||||
@@ -348,7 +388,7 @@ function createIdentifier(repository: Repository) {
|
|||||||
return repository.namespace + "/" + repository.name;
|
return repository.namespace + "/" + repository.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeByNamespaceAndName(repositoryCollection: RepositoryCollection) {
|
function normalizeByNamespaceAndName(state: object, repositoryCollection: RepositoryCollection) {
|
||||||
const names = [];
|
const names = [];
|
||||||
const byNames = {};
|
const byNames = {};
|
||||||
for (const repository of repositoryCollection._embedded.repositories) {
|
for (const repository of repositoryCollection._embedded.repositories) {
|
||||||
@@ -357,6 +397,7 @@ function normalizeByNamespaceAndName(repositoryCollection: RepositoryCollection)
|
|||||||
byNames[identifier] = repository;
|
byNames[identifier] = repository;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
...state,
|
||||||
list: {
|
list: {
|
||||||
...repositoryCollection,
|
...repositoryCollection,
|
||||||
_embedded: {
|
_embedded: {
|
||||||
@@ -378,6 +419,13 @@ const reducerByNames = (state: object, repository: Repository) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const reducerForNamespaces = (state: object, namespaces: NamespaceCollection) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
namespaces: namespaces._embedded
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default function reducer(
|
export default function reducer(
|
||||||
state: object = {},
|
state: object = {},
|
||||||
action: Action = {
|
action: Action = {
|
||||||
@@ -390,7 +438,9 @@ export default function reducer(
|
|||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case FETCH_REPOS_SUCCESS:
|
case FETCH_REPOS_SUCCESS:
|
||||||
return normalizeByNamespaceAndName(action.payload);
|
return normalizeByNamespaceAndName(state, action.payload);
|
||||||
|
case FETCH_NAMESPACES_SUCCESS:
|
||||||
|
return reducerForNamespaces(state, action.payload);
|
||||||
case FETCH_REPO_SUCCESS:
|
case FETCH_REPO_SUCCESS:
|
||||||
return reducerByNames(state, action.payload);
|
return reducerByNames(state, action.payload);
|
||||||
default:
|
default:
|
||||||
@@ -408,6 +458,7 @@ export function getRepositoryCollection(state: object) {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...state.repos.list,
|
...state.repos.list,
|
||||||
|
...state.repos.namespaces,
|
||||||
_embedded: {
|
_embedded: {
|
||||||
repositories
|
repositories
|
||||||
}
|
}
|
||||||
@@ -415,6 +466,10 @@ export function getRepositoryCollection(state: object) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getNamespaceCollection(state: object) {
|
||||||
|
return state.repos.namespaces?.namespaces;
|
||||||
|
}
|
||||||
|
|
||||||
export function isFetchReposPending(state: object) {
|
export function isFetchReposPending(state: object) {
|
||||||
return isPending(state, FETCH_REPOS);
|
return isPending(state, FETCH_REPOS);
|
||||||
}
|
}
|
||||||
@@ -429,6 +484,20 @@ export function getRepository(state: object, namespace: string, name: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isFetchNamespacesPending(state: object) {
|
||||||
|
return isPending(state, FETCH_NAMESPACES);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFetchNamespacesFailure(state: object) {
|
||||||
|
return getFailure(state, FETCH_NAMESPACES);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNamespace(state: object, namespace: string) {
|
||||||
|
if (state.namespaces) {
|
||||||
|
return state.namespaces[namespace];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function isFetchRepoPending(state: object, namespace: string, name: string) {
|
export function isFetchRepoPending(state: object, namespace: string, name: string) {
|
||||||
return isPending(state, FETCH_REPO, namespace + "/" + name);
|
return isPending(state, FETCH_REPO, namespace + "/" + name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ public class IndexDtoGenerator extends HalAppenderMapper {
|
|||||||
builder.single(link("config", resourceLinks.config().self()));
|
builder.single(link("config", resourceLinks.config().self()));
|
||||||
}
|
}
|
||||||
builder.single(link("repositories", resourceLinks.repositoryCollection().self()));
|
builder.single(link("repositories", resourceLinks.repositoryCollection().self()));
|
||||||
|
builder.single(link("namespaces", resourceLinks.namespaceCollection().self()));
|
||||||
if (PermissionPermissions.list().isPermitted()) {
|
if (PermissionPermissions.list().isPermitted()) {
|
||||||
builder.single(link("permissions", resourceLinks.permissions().self()));
|
builder.single(link("permissions", resourceLinks.permissions().self()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ package sonia.scm.api.v2.resources;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static de.otto.edison.hal.Link.link;
|
||||||
import static de.otto.edison.hal.Links.linkingTo;
|
import static de.otto.edison.hal.Links.linkingTo;
|
||||||
|
|
||||||
class NamespaceToNamespaceDtoMapper {
|
class NamespaceToNamespaceDtoMapper {
|
||||||
@@ -38,6 +39,12 @@ class NamespaceToNamespaceDtoMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NamespaceDto map(String namespace) {
|
NamespaceDto map(String namespace) {
|
||||||
return new NamespaceDto(namespace, linkingTo().self(links.namespace().self(namespace)).build());
|
return new NamespaceDto(
|
||||||
|
namespace,
|
||||||
|
linkingTo()
|
||||||
|
.self(links.namespace().self(namespace))
|
||||||
|
.single(link("repositories", links.repositoryCollection().forNamespace(namespace)))
|
||||||
|
.build()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -911,5 +911,9 @@ class ResourceLinks {
|
|||||||
String self(String namespace) {
|
String self(String namespace) {
|
||||||
return namespaceLinkBuilder.method("getNamespaceResource").parameters().method("get").parameters(namespace).href();
|
return namespaceLinkBuilder.method("getNamespaceResource").parameters().method("get").parameters(namespace).href();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String repositories(String namespace) {
|
||||||
|
return namespaceLinkBuilder.method("getNamespaceResource").parameters().method("get").parameters(namespace).href();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user