implemented create repository

This commit is contained in:
Sebastian Sdorra
2018-08-03 08:52:02 +02:00
parent a70420bb06
commit 211551a5be
4 changed files with 277 additions and 25 deletions

View File

@@ -11,26 +11,50 @@ import {
getRepositoryTypes,
isFetchRepositoryTypesPending
} from "../modules/repository-types";
import {
createRepo,
createRepoReset,
getCreateRepoFailure,
isCreateRepoPending
} from "../modules/repos";
import type { Repository } from "../types/Repositories";
import type { History } from "history";
type Props = {
repositoryTypes: RepositoryType[],
typesLoading: boolean,
createLoading: boolean,
error: Error,
// dispatch functions
fetchRepositoryTypesIfNeeded: () => void,
createRepo: (Repository, callback: () => void) => void,
resetForm: () => void,
// context props
t: string => string
t: string => string,
history: History
};
class Create extends React.Component<Props> {
componentDidMount() {
this.props.resetForm();
this.props.fetchRepositoryTypesIfNeeded();
}
repoCreated = () => {
const { history } = this.props;
history.push("/repos");
};
render() {
const { typesLoading, repositoryTypes, error } = this.props;
const {
typesLoading,
createLoading,
repositoryTypes,
createRepo,
error
} = this.props;
const { t } = this.props;
return (
@@ -39,8 +63,15 @@ class Create extends React.Component<Props> {
subtitle={t("create.subtitle")}
loading={typesLoading}
error={error}
showContentOnError={true}
>
<RepositoryForm repositoryTypes={repositoryTypes} />
<RepositoryForm
repositoryTypes={repositoryTypes}
loading={createLoading}
submitForm={repo => {
createRepo(repo, this.repoCreated);
}}
/>
</Page>
);
}
@@ -49,10 +80,13 @@ class Create extends React.Component<Props> {
const mapStateToProps = state => {
const repositoryTypes = getRepositoryTypes(state);
const typesLoading = isFetchRepositoryTypesPending(state);
const error = getFetchRepositoryTypesFailure(state);
const createLoading = isCreateRepoPending(state);
const error =
getFetchRepositoryTypesFailure(state) || getCreateRepoFailure(state);
return {
repositoryTypes,
typesLoading,
createLoading,
error
};
};
@@ -61,6 +95,12 @@ const mapDispatchToProps = dispatch => {
return {
fetchRepositoryTypesIfNeeded: () => {
dispatch(fetchRepositoryTypesIfNeeded());
},
createRepo: (repository: Repository, callback: () => void) => {
dispatch(createRepo(repository, callback));
},
resetForm: () => {
dispatch(createRepoReset());
}
};
};

View File

@@ -10,6 +10,7 @@ import {
fetchReposByPage,
getFetchReposFailure,
getRepositoryCollection,
isAbleToCreateRepos,
isFetchReposPending
} from "../modules/repos";
import { translate } from "react-i18next";
@@ -25,11 +26,13 @@ type Props = {
collection: RepositoryCollection,
loading: boolean,
error: Error,
showCreateButton: boolean,
// dispatched functions
fetchRepos: () => void,
fetchReposByPage: number => void,
fetchReposByLink: string => void,
// context props
t: string => string,
history: History
@@ -69,21 +72,31 @@ class Overview extends React.Component<Props> {
}
renderList() {
const { collection, fetchReposByLink, t } = this.props;
const { collection, fetchReposByLink } = this.props;
if (collection) {
return (
<div>
<RepositoryList repositories={collection._embedded.repositories} />
<Paginator collection={collection} onPageChange={fetchReposByLink} />
<CreateButton
label={t("overview.create-button")}
link="/repos/create"
/>
{this.renderCreateButton()}
</div>
);
}
return null;
}
renderCreateButton() {
const { showCreateButton, t } = this.props;
if (showCreateButton) {
return (
<CreateButton
label={t("overview.create-button")}
link="/repos/create"
/>
);
}
return null;
}
}
const getPageFromProps = props => {
@@ -101,11 +114,13 @@ const mapStateToProps = (state, ownProps) => {
const collection = getRepositoryCollection(state);
const loading = isFetchReposPending(state);
const error = getFetchReposFailure(state);
const showCreateButton = isAbleToCreateRepos(state);
return {
page,
collection,
loading,
error
error,
showCreateButton
};
};

View File

@@ -2,9 +2,9 @@
import { apiClient } from "../../apiclient";
import * as types from "../../modules/types";
import type { Action } from "../../types/Action";
import type {Repository, RepositoryCollection} from "../types/Repositories";
import {isPending} from "../../modules/pending";
import {getFailure} from "../../modules/failure";
import type { Repository, RepositoryCollection } from "../types/Repositories";
import { isPending } from "../../modules/pending";
import { getFailure } from "../../modules/failure";
export const FETCH_REPOS = "scm/repos/FETCH_REPOS";
export const FETCH_REPOS_PENDING = `${FETCH_REPOS}_${types.PENDING_SUFFIX}`;
@@ -16,8 +16,16 @@ export const FETCH_REPO_PENDING = `${FETCH_REPO}_${types.PENDING_SUFFIX}`;
export const FETCH_REPO_SUCCESS = `${FETCH_REPO}_${types.SUCCESS_SUFFIX}`;
export const FETCH_REPO_FAILURE = `${FETCH_REPO}_${types.FAILURE_SUFFIX}`;
export const CREATE_REPO = "scm/repos/FETCH_REPO";
export const CREATE_REPO_PENDING = `${CREATE_REPO}_${types.PENDING_SUFFIX}`;
export const CREATE_REPO_SUCCESS = `${CREATE_REPO}_${types.SUCCESS_SUFFIX}`;
export const CREATE_REPO_FAILURE = `${CREATE_REPO}_${types.FAILURE_SUFFIX}`;
export const CREATE_REPO_RESET = `${CREATE_REPO}_${types.RESET_SUFFIX}`;
const REPOS_URL = "repositories";
const CONTENT_TYPE = "application/vnd.scmm-repository+json;v=2";
// fetch repos
const SORT_BY = "sortBy=namespaceAndName";
@@ -84,15 +92,16 @@ export function fetchReposFailure(err: Error): Action {
export function fetchRepo(namespace: string, name: string) {
return function(dispatch: any) {
dispatch(fetchRepoPending(namespace, name));
return apiClient.get(`${REPOS_URL}/${namespace}/${name}`)
return apiClient
.get(`${REPOS_URL}/${namespace}/${name}`)
.then(response => response.json())
.then( repository => {
dispatch(fetchRepoSuccess(repository))
} )
.then(repository => {
dispatch(fetchRepoSuccess(repository));
})
.catch(err => {
dispatch(fetchRepoFailure(namespace, name, err))
dispatch(fetchRepoFailure(namespace, name, err));
});
}
};
}
export function fetchRepoPending(namespace: string, name: string): Action {
@@ -114,7 +123,11 @@ export function fetchRepoSuccess(repository: Repository): Action {
};
}
export function fetchRepoFailure(namespace: string, name: string, error: Error): Action {
export function fetchRepoFailure(
namespace: string,
name: string,
error: Error
): Action {
return {
type: FETCH_REPO_FAILURE,
payload: {
@@ -126,6 +139,50 @@ export function fetchRepoFailure(namespace: string, name: string, error: Error):
};
}
// create repo
export function createRepo(repository: Repository, callback?: () => void) {
return function(dispatch: any) {
dispatch(createRepoPending());
return apiClient
.post(REPOS_URL, repository, CONTENT_TYPE)
.then(() => {
dispatch(createRepoSuccess());
if (callback) {
callback();
}
})
.catch(err => {
dispatch(createRepoFailure(err));
});
};
}
export function createRepoPending(): Action {
return {
type: CREATE_REPO_PENDING
};
}
export function createRepoSuccess(): Action {
return {
type: CREATE_REPO_SUCCESS
};
}
export function createRepoFailure(err: Error): Action {
return {
type: CREATE_REPO_FAILURE,
payload: err
};
}
export function createRepoReset(): Action {
return {
type: CREATE_REPO_RESET
};
}
// reducer
function createIdentifier(repository: Repository) {
@@ -180,7 +237,7 @@ export default function reducer(
case FETCH_REPO_SUCCESS:
return reducerByNames(state, action.payload);
default:
return state;
return state;
}
}
@@ -211,14 +268,42 @@ export function getFetchReposFailure(state: Object) {
export function getRepository(state: Object, namespace: string, name: string) {
if (state.repos && state.repos.byNames) {
return state.repos.byNames[ namespace + "/" + name];
return state.repos.byNames[namespace + "/" + name];
}
}
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);
}
export function getFetchRepoFailure(state: Object, namespace: string, name: string) {
export function getFetchRepoFailure(
state: Object,
namespace: string,
name: string
) {
return getFailure(state, FETCH_REPO, namespace + "/" + name);
}
export function isAbleToCreateRepos(state: Object) {
if (
state.repos &&
state.repos.list &&
state.repos.list._links &&
state.repos.list._links.create
) {
return true;
}
return false;
}
export function isCreateRepoPending(state: Object) {
return isPending(state, CREATE_REPO);
}
export function getCreateRepoFailure(state: Object) {
return getFailure(state, CREATE_REPO);
}

View File

@@ -22,7 +22,15 @@ import reducer, {
fetchRepoSuccess,
getRepository,
isFetchRepoPending,
getFetchRepoFailure
getFetchRepoFailure,
CREATE_REPO_PENDING,
CREATE_REPO_SUCCESS,
createRepo,
CREATE_REPO_FAILURE,
isCreateRepoPending,
CREATE_REPO,
getCreateRepoFailure,
isAbleToCreateRepos
} from "./repos";
import type { Repository, RepositoryCollection } from "../types/Repositories";
@@ -354,6 +362,56 @@ describe("repos fetch", () => {
expect(actions[1].itemId).toBe("slarti/fjords");
});
});
it("should successfully create repo slarti/fjords", () => {
fetchMock.postOnce(REPOS_URL, slartiFjords);
const expectedActions = [
{
type: CREATE_REPO_PENDING
},
{
type: CREATE_REPO_SUCCESS
}
];
const store = mockStore({});
return store.dispatch(createRepo(slartiFjords)).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
it("should successfully create repo slarti/fjords and call the callback", () => {
// unmatched
fetchMock.postOnce(REPOS_URL, {
status: 201
});
let callMe = "not yet";
const callback = () => {
callMe = "yeah";
};
const store = mockStore({});
return store.dispatch(createRepo(slartiFjords, callback)).then(() => {
expect(callMe).toBe("yeah");
});
});
it("should disapatch failure if server returns status code 500", () => {
fetchMock.postOnce(REPOS_URL, {
status: 500
});
const store = mockStore({});
return store.dispatch(createRepo(slartiFjords)).then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(CREATE_REPO_PENDING);
expect(actions[1].type).toEqual(CREATE_REPO_FAILURE);
expect(actions[1].payload).toBeDefined();
});
});
});
describe("repos reducer", () => {
@@ -471,4 +529,58 @@ describe("repos selectors", () => {
};
expect(getFetchRepoFailure(state, "slarti", "fjords")).toEqual(error);
});
it("should return undefined when fetch repo did not fail", () => {
expect(getFetchRepoFailure({}, "slarti", "fjords")).toBe(undefined);
});
// create
it("should return true, when create repo is pending", () => {
const state = {
pending: {
[CREATE_REPO]: true
}
};
expect(isCreateRepoPending(state)).toEqual(true);
});
it("should return false, when create repo is not pending", () => {
expect(isCreateRepoPending({})).toEqual(false);
});
it("should return error when create repo did fail", () => {
const state = {
failure: {
[CREATE_REPO]: error
}
};
expect(getCreateRepoFailure(state)).toEqual(error);
});
it("should return undefined when create repo did not fail", () => {
expect(getCreateRepoFailure({})).toBe(undefined);
});
it("should return true if the list contains the create link", () => {
const state = {
repos: {
list: repositoryCollection
}
};
expect(isAbleToCreateRepos(state)).toBe(true);
});
it("should return false, if create link is unavailable", () => {
const state = {
repos: {
list: {
_links: {}
}
}
};
expect(isAbleToCreateRepos(state)).toBe(false);
});
});