Skip to content

Commit

Permalink
Merge pull request #610 from Amsterdam/release/2020-02-06
Browse files Browse the repository at this point in the history
Release/2020-02-06
  • Loading branch information
janjaap authored Feb 6, 2020
2 parents 64830fb + 641ec92 commit 5872570
Show file tree
Hide file tree
Showing 36 changed files with 4,825 additions and 773 deletions.
1 change: 0 additions & 1 deletion src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

<link rel="icon" href="/favicon.png" />
<title>SIA - Signalen Informatievoorziening Amsterdam</title>
<script src="//rum-static.pingdom.net/pa-5de788aa3a7031000800098c.js" async></script>
</head>

<body class="with-sidebars">
Expand Down
29 changes: 29 additions & 0 deletions src/models/categories/__tests__/actions.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import categoriesJson from 'utils/__tests__/fixtures/categories_private.json';

import * as actions from '../actions';
import * as constants from '../constants';

describe('models/categories/actions', () => {
test('fetchCategories', () => {
expect(actions.fetchCategories()).toEqual({
type: constants.FETCH_CATEGORIES,
});
});

test('fetchCategoriesSuccess', () => {
const payload = categoriesJson;
expect(actions.fetchCategoriesSuccess(payload)).toEqual({
type: constants.FETCH_CATEGORIES_SUCCESS,
payload,
});
});

test('fetchCategoriesFailed', () => {
const payload = new Error('Wrong!!!1!');

expect(actions.fetchCategoriesFailed(payload)).toEqual({
type: constants.FETCH_CATEGORIES_FAILED,
payload,
});
});
});
40 changes: 40 additions & 0 deletions src/models/categories/__tests__/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import injectReducerModel from 'utils/injectReducerModel';
import injectSagaModel from 'utils/injectSagaModel';

import reducer from '../reducer';
import saga from '../saga';

import loadModel from '../index';

jest.mock('utils/injectReducerModel');
jest.mock('utils/injectSagaModel');

jest.mock('../reducer');
jest.mock('../saga');

describe('models/categories', () => {
const store = { foo: 'bar' };
let spy;

beforeEach(() => {
spy = jest.fn();
});

afterEach(() => {
jest.resetAllMocks();
});

it('should inject reducer', () => {
injectReducerModel.mockImplementation(spy);
loadModel(store);

expect(spy).toHaveBeenCalledWith('categories', reducer, store);
});

it('should inject saga', () => {
injectSagaModel.mockImplementation(spy);
loadModel(store);

expect(spy).toHaveBeenCalledWith('categories', saga, store);
});
});
63 changes: 63 additions & 0 deletions src/models/categories/__tests__/reducer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { fromJS } from 'immutable';
import categoriesJson from 'utils/__tests__/fixtures/categories_private.json';

import reducer, { initialState } from '../reducer';
import * as constants from '../constants';

const catCount = 9;

const intermediateState = fromJS({
error: null,
categories: {
...categoriesJson,
results: categoriesJson.results.slice(0, catCount),
},
loading: false,
});

describe('models/categories/reducer', () => {
test('default', () => {
expect(reducer(undefined, {})).toEqual(initialState);
});

test('FETCH_CATEGORIES', () => {
const action = { type: constants.FETCH_CATEGORIES };

expect(reducer(initialState, action)).toEqual(
initialState.set('loading', true)
);

expect(reducer(intermediateState, action)).toEqual(
intermediateState.set('loading', true)
);
});

test('FETCH_CATEGORIES_SUCCESS', () => {
const type = constants.FETCH_CATEGORIES_SUCCESS;
const action = { type, payload: categoriesJson };

const result = fromJS({
error: null,
categories: action.payload,
loading: false,
});

expect(reducer(initialState, action)).toEqual(result);
expect(reducer(intermediateState, action)).toEqual(result);
});

test('FETCH_CATEGORIES_FAILED', () => {
const type = constants.FETCH_CATEGORIES_FAILED;
const error = new Error('Wrong!!!1!');
const payload = error;
const action = { type, payload };

expect(reducer(initialState, action)).toEqual(
initialState.set('error', error).set('loading', false)
);

expect(reducer(intermediateState, action)).toEqual(
intermediateState.set('error', error).set('loading', false)
);
});
});
60 changes: 60 additions & 0 deletions src/models/categories/__tests__/saga.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as Sentry from '@sentry/browser';
import { authCall, getErrorMessage } from 'shared/services/api/api';
import { testSaga } from 'redux-saga-test-plan';

import * as actions from 'containers/App/actions';
import categoriesJson from 'utils/__tests__/fixtures/categories_private.json';
import CONFIGURATION from 'shared/services/configuration/configuration';
import { VARIANT_ERROR, TYPE_LOCAL } from 'containers/Notification/constants';

import { fetchCategoriesSuccess, fetchCategoriesFailed } from '../actions';
import watchCategoriesSaga, { fetchCategories } from '../saga';
import { FETCH_CATEGORIES } from '../constants';

jest.mock('@sentry/browser');

describe('models/categories/saga', () => {
it('should watchCategoriesSaga', () => {
testSaga(watchCategoriesSaga)
.next()
.takeLatest(FETCH_CATEGORIES, fetchCategories)
.next()
.isDone();
});

describe('fetchCategories', () => {
it('should call endpoint and dispatch success', () => {
testSaga(fetchCategories)
.next()
.call(authCall, CONFIGURATION.CATEGORIES_PRIVATE_ENDPOINT)
.next(categoriesJson)
.put(fetchCategoriesSuccess(categoriesJson))
.next()
.isDone();
});

it('should dispatch error', () => {
const message = '404 not found';
const error = new Error(message);

testSaga(fetchCategories)
.next()
.call(authCall, CONFIGURATION.CATEGORIES_PRIVATE_ENDPOINT)
.throw(error)
.put(fetchCategoriesFailed(error))
.next()
.put(
actions.showGlobalNotification({
title: getErrorMessage(error),
message: 'Het categorieën overzicht kon niet opgehaald worden',
variant: VARIANT_ERROR,
type: TYPE_LOCAL,
})
)
.next()
.call([Sentry, 'captureException'], error)
.next()
.isDone();
});
});
});
19 changes: 19 additions & 0 deletions src/models/categories/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {
FETCH_CATEGORIES_FAILED,
FETCH_CATEGORIES_SUCCESS,
FETCH_CATEGORIES,
} from './constants';

export const fetchCategories = () => ({
type: FETCH_CATEGORIES,
});

export const fetchCategoriesSuccess = payload => ({
type: FETCH_CATEGORIES_SUCCESS,
payload,
});

export const fetchCategoriesFailed = payload => ({
type: FETCH_CATEGORIES_FAILED,
payload,
});
3 changes: 3 additions & 0 deletions src/models/categories/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const FETCH_CATEGORIES = 'sia/models/categories/FETCH_CATEGORIES';
export const FETCH_CATEGORIES_SUCCESS = 'sia/models/categories/FETCH_CATEGORIES_SUCCESS';
export const FETCH_CATEGORIES_FAILED = 'sia/models/categories/FETCH_CATEGORIES_FAILED';
12 changes: 12 additions & 0 deletions src/models/categories/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import injectReducerModel from 'utils/injectReducerModel';
import injectSagaModel from 'utils/injectSagaModel';

import reducer from './reducer';
import saga from './saga';

const loadModel = store => {
injectReducerModel('categories', reducer, store);
injectSagaModel('categories', saga, store);
};

export default loadModel;
32 changes: 32 additions & 0 deletions src/models/categories/reducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { fromJS } from 'immutable';

import {
FETCH_CATEGORIES_FAILED,
FETCH_CATEGORIES_SUCCESS,
FETCH_CATEGORIES,
} from './constants';

export const initialState = fromJS({
loading: false,
error: null,
categories: [],
});

export default (state = initialState, action) => {
switch (action.type) {
case FETCH_CATEGORIES:
return state.set('loading', true);

case FETCH_CATEGORIES_FAILED:
return state.set('loading', false).set('error', fromJS(action.payload));

case FETCH_CATEGORIES_SUCCESS:
return state
.set('loading', false)
.set('error', null)
.set('categories', fromJS(action.payload));

default:
return state;
}
};
37 changes: 37 additions & 0 deletions src/models/categories/saga.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { call, put, takeLatest } from 'redux-saga/effects';
import * as Sentry from '@sentry/browser';

import CONFIGURATION from 'shared/services/configuration/configuration';
import { authCall, getErrorMessage } from 'shared/services/api/api';
import { showGlobalNotification } from 'containers/App/actions';
import { VARIANT_ERROR, TYPE_LOCAL } from 'containers/Notification/constants';

import { FETCH_CATEGORIES } from './constants';
import { fetchCategoriesSuccess, fetchCategoriesFailed } from './actions';

export function* fetchCategories() {
const requestURL = CONFIGURATION.CATEGORIES_PRIVATE_ENDPOINT;

try {
const categories = yield call(authCall, requestURL);

yield put(fetchCategoriesSuccess(categories));
} catch (error) {
yield put(fetchCategoriesFailed(error));

yield put(
showGlobalNotification({
title: getErrorMessage(error),
message: 'Het categorieën overzicht kon niet opgehaald worden',
variant: VARIANT_ERROR,
type: TYPE_LOCAL,
})
);

yield call([Sentry, 'captureException'], error);
}
}

export default function* watchCategoriesSaga() {
yield takeLatest(FETCH_CATEGORIES, fetchCategories);
}
2 changes: 2 additions & 0 deletions src/models/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import loadIncidentModel from './incident';
import loadHistoryModel from './history';
import loadRolesModel from './roles';
import loadDepartmentsModel from './departments';
import loadCategoriesModel from './categories';

const loadModels = store => {
loadIncidentModel(store);
loadHistoryModel(store);
loadRolesModel(store);
loadDepartmentsModel(store);
loadCategoriesModel(store);
};

export default loadModels;
4 changes: 4 additions & 0 deletions src/shared/services/configuration/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ export class Configuration {
return `${this.API_ROOT}signals/v1/private/me/`;
}

get CATEGORIES_PRIVATE_ENDPOINT() {
return `${this.API_ROOT}signals/v1/private/categories/`;
}

get CATEGORIES_ENDPOINT() {
return `${this.API_ROOT}signals/v1/public/terms/categories/`;
}
Expand Down
13 changes: 12 additions & 1 deletion src/shared/types/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,22 @@ const dateTypeFactory = isRequired =>
export const dateType = dateTypeFactory(false);
dateType.isRequired = dateTypeFactory(true);

const idOrKeyPropRequired = (props, propName, componentName) => {
const { id, key } = props;

if (id === undefined && key === undefined) {
return new Error( `Either prop \`key\` or \`id\` is marked as required in \`${componentName}\`, but neither has been set`);
}

return null;
};

/**
* Generic data item type
*/
const dataItemType = PropTypes.shape({
key: PropTypes.string.isRequired,
id: idOrKeyPropRequired,
key: idOrKeyPropRequired,
slug: PropTypes.string,
value: PropTypes.string.isRequired,
});
Expand Down
Loading

0 comments on commit 5872570

Please sign in to comment.