Skip to content

Commit

Permalink
Clean up implementation of ducks (#572)
Browse files Browse the repository at this point in the history
  • Loading branch information
ashmaroli authored Nov 12, 2020
1 parent 10a2934 commit 95493f9
Show file tree
Hide file tree
Showing 16 changed files with 245 additions and 175 deletions.
2 changes: 1 addition & 1 deletion src/containers/views/DraftEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export class DraftEdit extends Component {
if (fieldChanged) {
const [directory, ...rest] = params.splat;
const filename = rest.join('.');
putDraft('edit', directory, filename);
putDraft(directory, filename);
}
};

Expand Down
10 changes: 5 additions & 5 deletions src/containers/views/DraftNew.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { browserHistory, withRouter } from 'react-router';
import DocumentTitle from 'react-document-title';
import CreateMarkdownPage from '../../components/CreateMarkdownPage';
import { updateTitle, updateBody, updatePath } from '../../ducks/metadata';
import { putDraft } from '../../ducks/drafts';
import { createDraft } from '../../ducks/drafts';
import { clearErrors } from '../../ducks/utils';
import { preventDefault, getDocumentTitle } from '../../utils/helpers';
import { ADMIN_PREFIX } from '../../constants';
Expand Down Expand Up @@ -41,8 +41,8 @@ export class DraftNew extends Component {

handleClickSave = e => {
preventDefault(e);
const { fieldChanged, putDraft, params } = this.props;
fieldChanged && putDraft('create', params.splat);
const { fieldChanged, createDraft, params } = this.props;
fieldChanged && createDraft(params.splat);
};

render() {
Expand Down Expand Up @@ -79,7 +79,7 @@ export class DraftNew extends Component {
}

DraftNew.propTypes = {
putDraft: PropTypes.func.isRequired,
createDraft: PropTypes.func.isRequired,
updateTitle: PropTypes.func.isRequired,
updateBody: PropTypes.func.isRequired,
updatePath: PropTypes.func.isRequired,
Expand Down Expand Up @@ -108,7 +108,7 @@ const mapDispatchToProps = dispatch =>
updateTitle,
updateBody,
updatePath,
putDraft,
createDraft,
clearErrors,
},
dispatch
Expand Down
50 changes: 43 additions & 7 deletions src/ducks/action_tests/drafts.spec.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import moment from 'moment';
import * as draftsDuck from '../drafts';
import * as collectionsDuck from '../collections';
import * as utilsDuck from '../utils';
import { API } from '../../constants/api';
import nock from 'nock';

import { draft, new_draft } from './fixtures';
import { draft, new_draft, publishedDraft } from './fixtures';

const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
Expand Down Expand Up @@ -109,7 +111,7 @@ describe('Actions::Drafts', () => {
const store = mockStore({ metadata: { metadata: draft } });

return store
.dispatch(draftsDuck.putDraft('edit', null, 'draft-post.md'))
.dispatch(draftsDuck.putDraft(null, 'draft-post.md'))
.then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
Expand All @@ -127,14 +129,14 @@ describe('Actions::Drafts', () => {

const store = mockStore({ metadata: { metadata: new_draft } });

return store.dispatch(draftsDuck.putDraft('create')).then(() => {
return store.dispatch(draftsDuck.createDraft('')).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});

it('creates the draft with autogenerated filename', () => {
nock(API)
.put('/drafts/draft-post.md')
.put(`/drafts/${new_draft.relative_path}`)
.reply(200, draft);

const expectedActions = [
Expand All @@ -146,7 +148,7 @@ describe('Actions::Drafts', () => {
metadata: { metadata: { ...new_draft, path: '' } },
});

return store.dispatch(draftsDuck.putDraft('create')).then(() => {
return store.dispatch(draftsDuck.createDraft('')).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
Expand All @@ -163,11 +165,37 @@ describe('Actions::Drafts', () => {

const store = mockStore({ metadata: { metadata: draft } });

return store.dispatch(draftsDuck.putDraft('edit')).then(() => {
return store.dispatch(draftsDuck.putDraft(draft.relative_path)).then(() => {
expect(store.getActions()[1].type).toEqual(expectedActions[1].type);
});
});

it('publishes the draft as a post successfully', () => {
const today = moment().format('YYYY-MM-DD');
const datedfilename = `${today}-draft-post.md`;
const doc = {
...publishedDraft,
date: `${today} 00:00:00 +0200`,
};

nock(API)
.put(`/collections/posts/${datedfilename}`)
.reply(200, doc);

const expectedActions = [
{ type: utilsDuck.CLEAR_ERRORS },
{ type: collectionsDuck.PUT_DOCUMENT_SUCCESS, doc },
];

const store = mockStore({ metadata: { metadata: publishedDraft } });

return store
.dispatch(draftsDuck.publishDraft(null, datedfilename))
.then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});

it('creates VALIDATION_ERROR if required fields are not provided.', () => {
const expectedActions = [
{
Expand All @@ -180,7 +208,15 @@ describe('Actions::Drafts', () => {
metadata: { metadata: { path: '', title: '' } },
});

store.dispatch(draftsDuck.putDraft('create'));
store.dispatch(draftsDuck.createDraft(''));
expect(store.getActions()).toEqual(expectedActions);

store.dispatch(draftsDuck.putDraft(null, 'draft-post.md'));
expect(store.getActions()).toEqual(expectedActions.concat(expectedActions));

store.dispatch(draftsDuck.publishDraft(null, 'draft-post.md'));
expect(store.getActions()).toEqual(
expectedActions.concat(expectedActions).concat(expectedActions)
);
});
});
13 changes: 13 additions & 0 deletions src/ducks/action_tests/fixtures/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,19 @@ export const draft = {
}
};

export const publishedDraft = {
raw_content: "# Test Draft\n",
name: "draft-post.md",
relative_path: "draft-post.md",
title: "Draft Post",
slug: "draft-post",
collection: "posts",
draft: true,
front_matter: {
title: "Draft Post"
}
};

export const new_draft = {
raw_content: "# Test Draft\n",
name: "draft-post.md",
Expand Down
93 changes: 38 additions & 55 deletions src/ducks/collections.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import _ from 'underscore';
import moment from 'moment';
import { CLEAR_ERRORS, validationError, filterDeleted } from './utils';
import { clearErrors, validationError, filterDeleted } from './utils';
import { get, put, del } from '../utils/fetch';
import { validator } from '../utils/validation';
import { slugify, trimObject, computeRelativePath } from '../utils/helpers';
import {
slugify,
preparePayload,
sanitizeFrontMatter,
computeRelativePath,
} from '../utils/helpers';
import {
collectionsAPIUrl,
collectionAPIUrl,
Expand Down Expand Up @@ -45,27 +49,20 @@ export const fetchCollections = () => dispatch => {
);
};

export const fetchCollection = (
collection_name,
directory = ''
) => dispatch => {
export const fetchCollection = (collection, directory = '') => dispatch => {
dispatch({ type: FETCH_COLLECTION_REQUEST });
return get(
collectionAPIUrl(collection_name, directory),
collectionAPIUrl(collection, directory),
{ type: FETCH_COLLECTION_SUCCESS, name: 'entries' },
{ type: FETCH_COLLECTION_FAILURE, name: 'error' },
dispatch
);
};

export const fetchDocument = (
collection_name,
directory,
filename
) => dispatch => {
export const fetchDocument = (collection, directory, filename) => dispatch => {
dispatch({ type: FETCH_DOCUMENT_REQUEST });
return get(
documentAPIUrl(collection_name, directory, filename),
documentAPIUrl(collection, directory, filename),
{ type: FETCH_DOCUMENT_SUCCESS, name: 'doc' },
{ type: FETCH_DOCUMENT_FAILURE, name: 'error' },
dispatch
Expand All @@ -78,10 +75,11 @@ export const createDocument = (collection, directory) => (
) => {
// get edited fields from metadata state
const metadata = getState().metadata.metadata;
let { path, raw_content, title } = metadata;
// if path is not given or equals to directory, generate filename from the title
let { path, raw_content, title, date } = metadata;
// if `path` is a falsy value or if appending a slash to it equals to
// `directory`, generate filename from `title`.
if ((!path || `${path}/` === directory) && title) {
path = generateFilenameFromTitle(metadata, collection); // override empty path
path = generateFilenameFromTitle(title, collection, date);
} else {
// validate otherwise
const errors = validateDocument(metadata, collection);
Expand All @@ -90,14 +88,12 @@ export const createDocument = (collection, directory) => (
}
}
// clear errors
dispatch({ type: CLEAR_ERRORS });
// omit raw_content, path and empty-value keys in metadata state from front_matter
const front_matter = _.omit(metadata, (value, key, object) => {
return key === 'raw_content' || key === 'path' || value === '';
});
dispatch(clearErrors());

const front_matter = sanitizeFrontMatter(metadata);

// send the put request
return put(
// create or update document according to filename existence
documentAPIUrl(collection, directory, path),
preparePayload({ raw_content, front_matter }),
{ type: PUT_DOCUMENT_SUCCESS, name: 'doc' },
Expand All @@ -112,10 +108,11 @@ export const putDocument = (collection, directory, filename) => (
) => {
// get edited fields from metadata state
const metadata = getState().metadata.metadata;
let { path, raw_content, title } = metadata;
// if path is not given or equals to directory, generate filename from the title
let { path, raw_content, title, date } = metadata;
// if `path` is a falsy value or if appending a slash to it equals to
// `directory`, generate filename from `title`.
if ((!path || `${path}/` === directory) && title) {
path = generateFilenameFromTitle(metadata, collection); // override empty path
path = generateFilenameFromTitle(title, collection, date);
} else {
// validate otherwise
const errors = validateDocument(metadata, collection);
Expand All @@ -124,18 +121,17 @@ export const putDocument = (collection, directory, filename) => (
}
}
// clear errors
dispatch({ type: CLEAR_ERRORS });
// omit raw_content, path and empty-value keys in metadata state from front_matter
const front_matter = _.omit(metadata, (value, key, object) => {
return key === 'raw_content' || key === 'path' || value === '';
});
dispatch(clearErrors());

const front_matter = sanitizeFrontMatter(metadata);

// add collection type prefix to relative path
const relative_path = directory
? `_${collection}/${directory}/${path}`
: `_${collection}/${path}`;

// send the put request
return put(
// create or update document according to filename existence
documentAPIUrl(collection, directory, filename),
preparePayload({ path: relative_path, raw_content, front_matter }),
{ type: PUT_DOCUMENT_SUCCESS, name: 'doc' },
Expand All @@ -154,18 +150,14 @@ export const deleteDocument = (collection, directory, filename) => dispatch => {
);
};

const generateFilenameFromTitle = (metadata, collection) => {
const generateFilenameFromTitle = (title, collection, date) => {
const slugifiedTitle = slugify(title);
if (collection === 'posts') {
// if date is provided, use it, otherwise generate it with today's date
let date;
if (metadata.date) {
date = metadata.date.split(' ')[0];
} else {
date = moment().format('YYYY-MM-DD');
}
return `${date}-${slugify(metadata.title)}.md`;
const docDate = date ? date.split(' ')[0] : moment().format('YYYY-MM-DD');
return `${docDate}-${slugifiedTitle}.md`;
}
return `${slugify(metadata.title)}.md`;
return `${slugifiedTitle}.md`;
};

const validateDocument = (metadata, collection) => {
Expand All @@ -186,8 +178,6 @@ const validateDocument = (metadata, collection) => {
return validator(metadata, validations, messages);
};

const preparePayload = obj => JSON.stringify(trimObject(obj));

// Reducer
export default function collections(
state = {
Expand Down Expand Up @@ -264,17 +254,10 @@ export default function collections(

// Selectors
export const filterBySearchInput = (list, input) => {
if (!list) {
return [];
if (!input) {
return list;
}
if (input) {
return _.filter(list, item => {
if (item.type) {
return item.name.toLowerCase().includes(input.toLowerCase());
} else {
return item.title.toLowerCase().includes(input.toLowerCase());
}
});
}
return list;
return list.filter(f =>
(f.title || f.name).toLowerCase().includes(input.toLowerCase())
);
};
4 changes: 2 additions & 2 deletions src/ducks/config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getConfigurationUrl, putConfigurationUrl } from '../constants/api';
import { get, put } from '../utils/fetch';
import { validator } from '../utils/validation';
import { CLEAR_ERRORS, validationError } from './utils';
import { clearErrors, validationError } from './utils';
import { toYAML } from '../utils/helpers';

import translations from '../translations';
Expand Down Expand Up @@ -45,7 +45,7 @@ export const putConfig = (config, source = 'editor') => (
if (errors.length) {
return dispatch(validationError(errors));
}
dispatch({ type: CLEAR_ERRORS });
dispatch(clearErrors());

return put(
putConfigurationUrl(),
Expand Down
4 changes: 2 additions & 2 deletions src/ducks/datafiles.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CLEAR_ERRORS, validationError, filterDeleted } from './utils';
import { clearErrors, validationError, filterDeleted } from './utils';
import { get, put, del } from '../utils/fetch';
import { datafilesAPIUrl, datafileAPIUrl } from '../constants/api';
import {
Expand Down Expand Up @@ -82,7 +82,7 @@ export const putDataFile = (
if (errors.length) {
return dispatch(validationError(errors));
}
dispatch({ type: CLEAR_ERRORS });
dispatch(clearErrors());

return put(
datafileAPIUrl(directory, filename),
Expand Down
Loading

0 comments on commit 95493f9

Please sign in to comment.