29 | );
30 | }
31 |
32 | export default {{ properCase name }};
33 |
--------------------------------------------------------------------------------
/client/internals/generators/component/styles.css.hbs:
--------------------------------------------------------------------------------
1 | .{{ camelCase name }} { /* stylelint-disable */
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/client/internals/generators/component/test.js.hbs:
--------------------------------------------------------------------------------
1 | // import {{ properCase name }} from '../index';
2 |
3 | import expect from 'expect';
4 | // import { shallow } from 'enzyme';
5 | // import React from 'react';
6 |
7 | describe('<{{ properCase name }} />', () => {
8 | it('Expect to have unit tests specified', () => {
9 | expect(true).toEqual(false);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/client/internals/generators/container/actions.js.hbs:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * {{ properCase name }} actions
4 | *
5 | */
6 |
7 | import {
8 | DEFAULT_ACTION,
9 | } from './constants';
10 |
11 | export function defaultAction() {
12 | return {
13 | type: DEFAULT_ACTION,
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/client/internals/generators/container/actions.test.js.hbs:
--------------------------------------------------------------------------------
1 | import expect from 'expect';
2 | import {
3 | defaultAction,
4 | } from '../actions';
5 | import {
6 | DEFAULT_ACTION,
7 | } from '../constants';
8 |
9 | describe('{{ properCase name }} actions', () => {
10 | describe('Default Action', () => {
11 | it('has a type of DEFAULT_ACTION', () => {
12 | const expected = {
13 | type: DEFAULT_ACTION,
14 | };
15 | expect(defaultAction()).toEqual(expected);
16 | });
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/client/internals/generators/container/constants.js.hbs:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * {{ properCase name }} constants
4 | *
5 | */
6 |
7 | export const DEFAULT_ACTION = 'app/{{ properCase name }}/DEFAULT_ACTION';
8 |
--------------------------------------------------------------------------------
/client/internals/generators/container/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Container Generator
3 | */
4 |
5 | const componentExists = require('../utils/componentExists');
6 |
7 | module.exports = {
8 | description: 'Add a container component',
9 | prompts: [{
10 | type: 'input',
11 | name: 'name',
12 | message: 'What should it be called?',
13 | default: 'Form',
14 | validate: value => {
15 | if ((/.+/).test(value)) {
16 | return componentExists(value) ? 'A component or container with this name already exists' : true;
17 | }
18 |
19 | return 'The name is required';
20 | },
21 | }, {
22 | type: 'confirm',
23 | name: 'wantHeaders',
24 | default: false,
25 | message: 'Do you want headers?',
26 | }, {
27 | type: 'confirm',
28 | name: 'wantCSS',
29 | default: false,
30 | message: 'Does it have styling?',
31 | }, {
32 | type: 'confirm',
33 | name: 'wantActionsAndReducer',
34 | default: true,
35 | message: 'Do you want an actions/constants/selectors/reducer tupel for this container?',
36 | }, {
37 | type: 'confirm',
38 | name: 'wantSagas',
39 | default: true,
40 | message: 'Do you want sagas for asynchronous flows? (e.g. fetching data)',
41 | }, {
42 | type: 'confirm',
43 | name: 'wantMessages',
44 | default: true,
45 | message: 'Do you want i18n messages (i.e. will this component use text)?',
46 | }],
47 | actions: data => {
48 | // Generate index.js and index.test.js
49 | const actions = [{
50 | type: 'add',
51 | path: '../../app/containers/{{properCase name}}/index.js',
52 | templateFile: './container/index.js.hbs',
53 | abortOnFail: true,
54 | }, {
55 | type: 'add',
56 | path: '../../app/containers/{{properCase name}}/tests/index.test.js',
57 | templateFile: './container/test.js.hbs',
58 | abortOnFail: true,
59 | }];
60 |
61 | // If they want a CSS file, add styles.css
62 | if (data.wantCSS) {
63 | actions.push({
64 | type: 'add',
65 | path: '../../app/containers/{{properCase name}}/styles.css',
66 | templateFile: './container/styles.css.hbs',
67 | abortOnFail: true,
68 | });
69 | }
70 |
71 | // If component wants messages
72 | if (data.wantMessages) {
73 | actions.push({
74 | type: 'add',
75 | path: '../../app/containers/{{properCase name}}/messages.js',
76 | templateFile: './container/messages.js.hbs',
77 | abortOnFail: true,
78 | });
79 | }
80 |
81 | // If they want actions and a reducer, generate actions.js, constants.js,
82 | // reducer.js and the corresponding tests for actions and the reducer
83 | if (data.wantActionsAndReducer) {
84 | // Actions
85 | actions.push({
86 | type: 'add',
87 | path: '../../app/containers/{{properCase name}}/actions.js',
88 | templateFile: './container/actions.js.hbs',
89 | abortOnFail: true,
90 | });
91 | actions.push({
92 | type: 'add',
93 | path: '../../app/containers/{{properCase name}}/tests/actions.test.js',
94 | templateFile: './container/actions.test.js.hbs',
95 | abortOnFail: true,
96 | });
97 |
98 | // Constants
99 | actions.push({
100 | type: 'add',
101 | path: '../../app/containers/{{properCase name}}/constants.js',
102 | templateFile: './container/constants.js.hbs',
103 | abortOnFail: true,
104 | });
105 |
106 | // Selectors
107 | actions.push({
108 | type: 'add',
109 | path: '../../app/containers/{{properCase name}}/selectors.js',
110 | templateFile: './container/selectors.js.hbs',
111 | abortOnFail: true,
112 | });
113 | actions.push({
114 | type: 'add',
115 | path: '../../app/containers/{{properCase name}}/tests/selectors.test.js',
116 | templateFile: './container/selectors.test.js.hbs',
117 | abortOnFail: true,
118 | });
119 |
120 | // Reducer
121 | actions.push({
122 | type: 'add',
123 | path: '../../app/containers/{{properCase name}}/reducer.js',
124 | templateFile: './container/reducer.js.hbs',
125 | abortOnFail: true,
126 | });
127 | actions.push({
128 | type: 'add',
129 | path: '../../app/containers/{{properCase name}}/tests/reducer.test.js',
130 | templateFile: './container/reducer.test.js.hbs',
131 | abortOnFail: true,
132 | });
133 | }
134 |
135 | // Sagas
136 | if (data.wantSagas) {
137 | actions.push({
138 | type: 'add',
139 | path: '../../app/containers/{{properCase name}}/sagas.js',
140 | templateFile: './container/sagas.js.hbs',
141 | abortOnFail: true,
142 | });
143 | actions.push({
144 | type: 'add',
145 | path: '../../app/containers/{{properCase name}}/tests/sagas.test.js',
146 | templateFile: './container/sagas.test.js.hbs',
147 | abortOnFail: true,
148 | });
149 | }
150 |
151 | return actions;
152 | },
153 | };
154 |
--------------------------------------------------------------------------------
/client/internals/generators/container/index.js.hbs:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * {{properCase name }}
4 | *
5 | */
6 |
7 | import React from 'react';
8 | import { connect } from 'react-redux';
9 | {{#if wantHeaders}}
10 | import Helmet from 'react-helmet';
11 | {{/if}}
12 | {{#if wantActionsAndReducer}}
13 | import select{{properCase name}} from './selectors';
14 | {{/if}}
15 | {{#if wantMessages}}
16 | import { FormattedMessage } from 'react-intl';
17 | import messages from './messages';
18 | {{/if}}
19 | {{#if wantCSS}}
20 | import styles from './styles.css';
21 | {{/if}}
22 |
23 | export class {{ properCase name }} extends React.Component { // eslint-disable-line react/prefer-stateless-function
24 | render() {
25 | return (
26 | {{#if wantCSS}}
27 |
28 | {{else}}
29 |
30 | {{/if}}
31 | {{#if wantHeaders}}
32 |
38 | {{/if}}
39 | {{#if wantMessages}}
40 |
41 | {{/if}}
42 |
43 | );
44 | }
45 | }
46 |
47 | {{#if wantActionsAndReducer}}
48 | const mapStateToProps = select{{properCase name}}();
49 | {{/if}}
50 |
51 | function mapDispatchToProps(dispatch) {
52 | return {
53 | dispatch,
54 | };
55 | }
56 |
57 | {{#if wantActionsAndReducer}}
58 | export default connect(mapStateToProps, mapDispatchToProps)({{ properCase name }});
59 | {{else}}
60 | export default connect(mapDispatchToProps)({{ properCase name }});
61 | {{/if}}
62 |
--------------------------------------------------------------------------------
/client/internals/generators/container/messages.js.hbs:
--------------------------------------------------------------------------------
1 | /*
2 | * {{properCase name }} Messages
3 | *
4 | * This contains all the text for the {{properCase name }} component.
5 | */
6 | import { defineMessages } from 'react-intl';
7 |
8 | export default defineMessages({
9 | header: {
10 | id: 'app.containers.{{properCase name }}.header',
11 | defaultMessage: 'This is {{properCase name}} container !',
12 | },
13 | });
14 |
--------------------------------------------------------------------------------
/client/internals/generators/container/reducer.js.hbs:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * {{ properCase name }} reducer
4 | *
5 | */
6 |
7 | import { fromJS } from 'immutable';
8 | import {
9 | DEFAULT_ACTION,
10 | } from './constants';
11 |
12 | const initialState = fromJS({});
13 |
14 | function {{ camelCase name }}Reducer(state = initialState, action) {
15 | switch (action.type) {
16 | case DEFAULT_ACTION:
17 | return state;
18 | default:
19 | return state;
20 | }
21 | }
22 |
23 | export default {{ camelCase name }}Reducer;
24 |
--------------------------------------------------------------------------------
/client/internals/generators/container/reducer.test.js.hbs:
--------------------------------------------------------------------------------
1 | import expect from 'expect';
2 | import {{ camelCase name }}Reducer from '../reducer';
3 | import { fromJS } from 'immutable';
4 |
5 | describe('{{ camelCase name }}Reducer', () => {
6 | it('returns the initial state', () => {
7 | expect({{ camelCase name }}Reducer(undefined, {})).toEqual(fromJS({}));
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/client/internals/generators/container/sagas.js.hbs:
--------------------------------------------------------------------------------
1 | // import { take, call, put, select } from 'redux-saga/effects';
2 |
3 | // Individual exports for testing
4 | export function* defaultSaga() {
5 | return;
6 | }
7 |
8 | // All sagas to be loaded
9 | export default [
10 | defaultSaga,
11 | ];
12 |
--------------------------------------------------------------------------------
/client/internals/generators/container/sagas.test.js.hbs:
--------------------------------------------------------------------------------
1 | /**
2 | * Test sagas
3 | */
4 |
5 | import expect from 'expect';
6 | // import { take, call, put, select } from 'redux-saga/effects';
7 | // import { defaultSaga } from '../sagas';
8 |
9 | // const generator = defaultSaga();
10 |
11 | describe('defaultSaga Saga', () => {
12 | it('Expect to have unit tests specified', () => {
13 | expect(true).toEqual(false);
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/client/internals/generators/container/selectors.js.hbs:
--------------------------------------------------------------------------------
1 | import { createSelector } from 'reselect';
2 |
3 | /**
4 | * Direct selector to the {{ camelCase name }} state domain
5 | */
6 | const select{{ properCase name }}Domain = () => state => state.get('{{ camelCase name }}');
7 |
8 | /**
9 | * Other specific selectors
10 | */
11 |
12 |
13 | /**
14 | * Default selector used by {{ properCase name }}
15 | */
16 |
17 | const select{{ properCase name }} = () => createSelector(
18 | select{{ properCase name }}Domain(),
19 | (substate) => substate.toJS()
20 | );
21 |
22 | export default select{{ properCase name }};
23 | export {
24 | select{{ properCase name }}Domain,
25 | };
26 |
--------------------------------------------------------------------------------
/client/internals/generators/container/selectors.test.js.hbs:
--------------------------------------------------------------------------------
1 | // import { select{{ properCase name }}Domain } from '../selectors';
2 | // import { fromJS } from 'immutable';
3 | import expect from 'expect';
4 |
5 | // const selector = select{{ properCase name}}Domain();
6 |
7 | describe('select{{ properCase name }}Domain', () => {
8 | it('Expect to have unit tests specified', () => {
9 | expect('Test case').toEqual(false);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/client/internals/generators/container/styles.css.hbs:
--------------------------------------------------------------------------------
1 | .{{ camelCase name }} { /* stylelint-disable */
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/client/internals/generators/container/test.js.hbs:
--------------------------------------------------------------------------------
1 | // import {{ properCase name }} from '../index';
2 |
3 | import expect from 'expect';
4 | // import { shallow } from 'enzyme';
5 | // import React from 'react';
6 |
7 | describe('<{{ properCase name }} />', () => {
8 | it('Expect to have unit tests specified', () => {
9 | expect(true).toEqual(false);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/client/internals/generators/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * generator/index.js
3 | *
4 | * Exports the generators so plop knows them
5 | */
6 |
7 | const fs = require('fs');
8 | const componentGenerator = require('./component/index.js');
9 | const containerGenerator = require('./container/index.js');
10 | const routeGenerator = require('./route/index.js');
11 | const languageGenerator = require('./language/index.js');
12 |
13 | module.exports = (plop) => {
14 | plop.setGenerator('component', componentGenerator);
15 | plop.setGenerator('container', containerGenerator);
16 | plop.setGenerator('route', routeGenerator);
17 | plop.setGenerator('language', languageGenerator);
18 | plop.addHelper('directory', (comp) => {
19 | try {
20 | fs.accessSync(`app/containers/${comp}`, fs.F_OK);
21 | return `containers/${comp}`;
22 | } catch (e) {
23 | return `components/${comp}`;
24 | }
25 | });
26 | plop.addHelper('curly', (object, open) => (open ? '{' : '}'));
27 | };
28 |
--------------------------------------------------------------------------------
/client/internals/generators/language/add-locale-data.hbs:
--------------------------------------------------------------------------------
1 | $1addLocaleData({{language}}LocaleData);
2 |
--------------------------------------------------------------------------------
/client/internals/generators/language/app-locale.hbs:
--------------------------------------------------------------------------------
1 | $1
2 | '{{language}}',
3 |
--------------------------------------------------------------------------------
/client/internals/generators/language/format-translation-messages.hbs:
--------------------------------------------------------------------------------
1 | $1 {{language}}: formatTranslationMessages({{language}}TranslationMessages),
2 |
--------------------------------------------------------------------------------
/client/internals/generators/language/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Language Generator
3 | */
4 | const exec = require('child_process').exec;
5 |
6 | module.exports = {
7 | description: 'Add a langauge',
8 | prompts: [{
9 | type: 'input',
10 | name: 'language',
11 | message: 'What is the language you want to add i18n support for (e.g. "fr", "de")?',
12 | default: 'fr',
13 | validate: value => {
14 | if ((/.+/).test(value) && value.length === 2) {
15 | return true;
16 | }
17 |
18 | return '2 character language specifier is required';
19 | },
20 | }],
21 |
22 | actions: () => {
23 | const actions = [];
24 | actions.push({
25 | type: 'modify',
26 | path: '../../app/i18n.js',
27 | pattern: /('react-intl\/locale-data\/[a-z]+';\n)(?!.*'react-intl\/locale-data\/[a-z]+';)/g,
28 | templateFile: './language/intl-locale-data.hbs',
29 | });
30 | actions.push({
31 | type: 'modify',
32 | path: '../../app/i18n.js',
33 | pattern: /([\n\s'[a-z]+',)(?!.*[\n\s'[a-z]+',)/g,
34 | templateFile: './language/app-locale.hbs',
35 | });
36 | actions.push({
37 | type: 'modify',
38 | path: '../../app/i18n.js',
39 | pattern: /(from\s'.\/translations\/[a-z]+.json';\n)(?!.*from\s'.\/translations\/[a-z]+.json';)/g,
40 | templateFile: './language/translation-messages.hbs',
41 | });
42 | actions.push({
43 | type: 'modify',
44 | path: '../../app/i18n.js',
45 | pattern: /(addLocaleData\([a-z]+LocaleData\);\n)(?!.*addLocaleData\([a-z]+LocaleData\);)/g,
46 | templateFile: './language/add-locale-data.hbs',
47 | });
48 | actions.push({
49 | type: 'modify',
50 | path: '../../app/i18n.js',
51 | pattern: /([a-z]+:\sformatTranslationMessages\([a-z]+TranslationMessages\),\n)(?!.*[a-z]+:\sformatTranslationMessages\([a-z]+TranslationMessages\),)/g,
52 | templateFile: './language/format-translation-messages.hbs',
53 | });
54 | actions.push({
55 | type: 'add',
56 | path: '../../app/translations/{{language}}.json',
57 | templateFile: './language/translations-json.hbs',
58 | abortOnFail: true,
59 | });
60 | actions.push({
61 | type: 'modify',
62 | path: '../../app/app.js',
63 | pattern: /(System\.import\('intl\/locale-data\/jsonp\/[a-z]+\.js'\),\n)(?!.*System\.import\('intl\/locale-data\/jsonp\/[a-z]+\.js'\),)/g,
64 | templateFile: './language/polyfill-intl-locale.hbs',
65 | });
66 | actions.push(
67 | () => {
68 | const cmd = 'npm run extract-intl';
69 | exec(cmd, (err, result, stderr) => {
70 | if (err || stderr) {
71 | throw err || stderr;
72 | }
73 | process.stdout.write(result);
74 | });
75 | }
76 | );
77 |
78 | return actions;
79 | },
80 | };
81 |
--------------------------------------------------------------------------------
/client/internals/generators/language/intl-locale-data.hbs:
--------------------------------------------------------------------------------
1 | $1import {{language}}LocaleData from 'react-intl/locale-data/{{language}}';
2 |
--------------------------------------------------------------------------------
/client/internals/generators/language/polyfill-intl-locale.hbs:
--------------------------------------------------------------------------------
1 | $1 System.import('intl/locale-data/jsonp/{{language}}.js'),
2 |
--------------------------------------------------------------------------------
/client/internals/generators/language/translation-messages.hbs:
--------------------------------------------------------------------------------
1 | $1import {{language}}TranslationMessages from './translations/{{language}}.json';
2 |
--------------------------------------------------------------------------------
/client/internals/generators/language/translations-json.hbs:
--------------------------------------------------------------------------------
1 | []
2 |
--------------------------------------------------------------------------------
/client/internals/generators/route/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Route Generator
3 | */
4 | const fs = require('fs');
5 | const componentExists = require('../utils/componentExists');
6 |
7 | function reducerExists(comp) {
8 | try {
9 | fs.accessSync(`app/containers/${comp}/reducer.js`, fs.F_OK);
10 | return true;
11 | } catch (e) {
12 | return false;
13 | }
14 | }
15 |
16 | function sagasExists(comp) {
17 | try {
18 | fs.accessSync(`app/containers/${comp}/sagas.js`, fs.F_OK);
19 | return true;
20 | } catch (e) {
21 | return false;
22 | }
23 | }
24 |
25 | function trimTemplateFile(template) {
26 | // Loads the template file and trims the whitespace and then returns the content as a string.
27 | return fs.readFileSync(`internals/generators/route/${template}`, 'utf8').replace(/\s*$/, '');
28 | }
29 |
30 | module.exports = {
31 | description: 'Add a route',
32 | prompts: [{
33 | type: 'input',
34 | name: 'component',
35 | message: 'Which component should the route show?',
36 | validate: value => {
37 | if ((/.+/).test(value)) {
38 | return componentExists(value) ? true : `"${value}" doesn't exist.`;
39 | }
40 |
41 | return 'The path is required';
42 | },
43 | }, {
44 | type: 'input',
45 | name: 'path',
46 | message: 'Enter the path of the route.',
47 | default: '/about',
48 | validate: value => {
49 | if ((/.+/).test(value)) {
50 | return true;
51 | }
52 |
53 | return 'path is required';
54 | },
55 | }],
56 |
57 | // Add the route to the routes.js file above the error route
58 | // TODO smarter route adding
59 | actions: data => {
60 | const actions = [];
61 | if (reducerExists(data.component)) {
62 | data.useSagas = sagasExists(data.component); // eslint-disable-line no-param-reassign
63 | actions.push({
64 | type: 'modify',
65 | path: '../../app/routes.js',
66 | pattern: /(\s{\n\s{0,}path: '\*',)/g,
67 | template: trimTemplateFile('routeWithReducer.hbs'),
68 | });
69 | } else {
70 | actions.push({
71 | type: 'modify',
72 | path: '../../app/routes.js',
73 | pattern: /(\s{\n\s{0,}path: '\*',)/g,
74 | template: trimTemplateFile('route.hbs'),
75 | });
76 | }
77 |
78 | return actions;
79 | },
80 | };
81 |
--------------------------------------------------------------------------------
/client/internals/generators/route/route.hbs:
--------------------------------------------------------------------------------
1 | {
2 | path: '{{ path }}',
3 | name: '{{ camelCase component }}',
4 | getComponent(location, cb) {
5 | System.import('{{{directory (properCase component)}}}')
6 | .then(loadModule(cb))
7 | .catch(errorLoading);
8 | },
9 | },$1
10 |
--------------------------------------------------------------------------------
/client/internals/generators/route/routeWithReducer.hbs:
--------------------------------------------------------------------------------
1 | {
2 | path: '{{ path }}',
3 | name: '{{ camelCase component }}',
4 | getComponent(nextState, cb) {
5 | const importModules = Promise.all([
6 | System.import('containers/{{ properCase component }}/reducer'),
7 | {{#if useSagas}}
8 | System.import('containers/{{ properCase component }}/sagas'),
9 | {{/if}}
10 | System.import('containers/{{ properCase component }}'),
11 | ]);
12 |
13 | const renderRoute = loadModule(cb);
14 |
15 | importModules.then(([reducer,{{#if useSagas}} sagas,{{/if}} component]) => {
16 | injectReducer('{{ camelCase component }}', reducer.default);
17 | {{#if useSagas}}
18 | injectSagas(sagas.default);
19 | {{/if}}
20 | renderRoute(component);
21 | });
22 |
23 | importModules.catch(errorLoading);
24 | },
25 | },$1
26 |
--------------------------------------------------------------------------------
/client/internals/generators/utils/componentExists.js:
--------------------------------------------------------------------------------
1 | /**
2 | * componentExists
3 | *
4 | * Check whether the given component exist in either the components or containers directory
5 | */
6 |
7 | const fs = require('fs');
8 | const pageComponents = fs.readdirSync('app/components');
9 | const pageContainers = fs.readdirSync('app/containers');
10 | const components = pageComponents.concat(pageContainers);
11 |
12 | function componentExists(comp) {
13 | return components.indexOf(comp) >= 0;
14 | }
15 |
16 | module.exports = componentExists;
17 |
--------------------------------------------------------------------------------
/client/internals/scripts/analyze.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var shelljs = require('shelljs');
4 | var animateProgress = require('./helpers/progress');
5 | var chalk = require('chalk');
6 | var addCheckMark = require('./helpers/checkmark');
7 |
8 | var progress = animateProgress('Generating stats');
9 |
10 | // Generate stats.json file with webpack
11 | shelljs.exec(
12 | 'webpack --config internals/webpack/webpack.prod.babel.js --profile --json > stats.json',
13 | addCheckMark.bind(null, callback) // Output a checkmark on completion
14 | );
15 |
16 | // Called after webpack has finished generating the stats.json file
17 | function callback() {
18 | clearInterval(progress);
19 | process.stdout.write(
20 | '\n\nOpen ' + chalk.magenta('http://webpack.github.io/analyse/') + ' in your browser and upload the stats.json file!' +
21 | chalk.blue('\n(Tip: ' + chalk.italic('CMD + double-click') + ' the link!)\n\n')
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/client/internals/scripts/clean.js:
--------------------------------------------------------------------------------
1 | require('shelljs/global');
2 |
3 | /**
4 | * Adds mark check symbol
5 | */
6 | function addCheckMark(callback) {
7 | process.stdout.write(' ✓');
8 | callback();
9 | }
10 |
11 | if (!which('git')) {
12 | echo('Sorry, this script requires git');
13 | exit(1);
14 | }
15 |
16 | if (!test('-e', 'internals/templates')) {
17 | echo('The example is deleted already.');
18 | exit(1);
19 | }
20 |
21 | process.stdout.write('Cleanup started...');
22 |
23 | // Cleanup components folder
24 | rm('-rf', 'app/components/*');
25 |
26 | // Cleanup containers folder
27 | rm('-rf', 'app/containers/*');
28 | mkdir('-p', 'app/containers/App');
29 | mkdir('-p', 'app/containers/NotFoundPage');
30 | mkdir('-p', 'app/containers/HomePage');
31 | cp('internals/templates/appContainer.js', 'app/containers/App/index.js');
32 | cp('internals/templates/notFoundPage/notFoundPage.js', 'app/containers/NotFoundPage/index.js');
33 | cp('internals/templates/notFoundPage/messages.js', 'app/containers/NotFoundPage/messages.js');
34 | cp('internals/templates/homePage/homePage.js', 'app/containers/HomePage/index.js');
35 | cp('internals/templates/homePage/messages.js', 'app/containers/HomePage/messages.js');
36 |
37 | // Handle Translations
38 | mkdir('-p', 'app/translations');
39 | cp('internals/templates/translations/en.json',
40 | 'app/translations/en.json');
41 |
42 | // move i18n file
43 | cp('internals/templates/i18n.js',
44 | 'app/i18n.js');
45 |
46 | // Copy LanguageProvider
47 | mkdir('-p', 'app/containers/LanguageProvider');
48 | mkdir('-p', 'app/containers/LanguageProvider/tests');
49 | cp('internals/templates/languageProvider/actions.js',
50 | 'app/containers/LanguageProvider/actions.js');
51 | cp('internals/templates/languageProvider/constants.js',
52 | 'app/containers/LanguageProvider/constants.js');
53 | cp('internals/templates/languageProvider/languageProvider.js',
54 | 'app/containers/LanguageProvider/index.js');
55 | cp('internals/templates/languageProvider/reducer.js',
56 | 'app/containers/LanguageProvider/reducer.js');
57 | cp('internals/templates/languageProvider/selectors.js',
58 | 'app/containers/LanguageProvider/selectors.js');
59 | cp('internals/templates/styles.css', 'app/containers/App/styles.css');
60 |
61 | // Copy selectors
62 | mkdir('app/containers/App/tests');
63 | cp('internals/templates/selectors.js',
64 | 'app/containers/App/selectors.js');
65 | cp('internals/templates/selectors.test.js',
66 | 'app/containers/App/tests/selectors.test.js');
67 |
68 | // Utils
69 | rm('-rf', 'app/utils');
70 | mkdir('app/utils');
71 | mkdir('app/utils/tests');
72 | cp('internals/templates/asyncInjectors.js',
73 | 'app/utils/asyncInjectors.js');
74 | cp('internals/templates/asyncInjectors.test.js',
75 | 'app/utils/tests/asyncInjectors.test.js');
76 |
77 | // Replace the files in the root app/ folder
78 | cp('internals/templates/app.js', 'app/app.js');
79 | cp('internals/templates/index.html', 'app/index.html');
80 | cp('internals/templates/reducers.js', 'app/reducers.js');
81 | cp('internals/templates/routes.js', 'app/routes.js');
82 | cp('internals/templates/store.js', 'app/store.js');
83 | cp('internals/templates/store.test.js', 'app/tests/store.test.js');
84 |
85 | // Remove the templates folder
86 | rm('-rf', 'internals/templates');
87 |
88 | process.stdout.write(' ✓');
89 |
90 | // Commit the changes
91 | if (exec('git add . --all && git commit -qm "Remove default example"').code !== 0) {
92 | echo('\nError: Git commit failed');
93 | exit(1);
94 | }
95 |
96 | echo('\nCleanup done. Happy Coding!!!');
97 |
--------------------------------------------------------------------------------
/client/internals/scripts/dependencies.js:
--------------------------------------------------------------------------------
1 | /*eslint-disable*/
2 |
3 | // No need to build the DLL in production
4 | if (process.env.NODE_ENV === 'production') {
5 | process.exit(0)
6 | }
7 |
8 | require('shelljs/global')
9 |
10 | const path = require('path')
11 | const fs = require('fs')
12 | const exists = fs.existsSync
13 | const writeFile = fs.writeFileSync
14 |
15 | const defaults = require('lodash/defaultsDeep')
16 | const pkg = require(path.join(process.cwd(), 'package.json'))
17 | const config = require('../config')
18 | const dllConfig = defaults(pkg.dllPlugin, config.dllPlugin.defaults)
19 | const outputPath = path.join(process.cwd(), dllConfig.path)
20 | const dllManifestPath = path.join(outputPath, 'package.json')
21 |
22 | /**
23 | * I use node_modules/react-boilerplate-dlls by default just because
24 | * it isn't going to be version controlled and babel wont try to parse it.
25 | */
26 | mkdir('-p', outputPath)
27 |
28 | echo('Building the Webpack DLL...')
29 |
30 | /**
31 | * Create a manifest so npm install doesnt warn us
32 | */
33 | if (!exists(dllManifestPath)) {
34 | writeFile(
35 | dllManifestPath,
36 | JSON.stringify(defaults({
37 | name: 'react-boilerplate-dlls',
38 | private: true,
39 | author: pkg.author,
40 | repository: pkg.repository,
41 | version: pkg.version
42 | }), null, 2),
43 |
44 | 'utf8'
45 | )
46 | }
47 |
48 | // the BUILDING_DLL env var is set to avoid confusing the development environment
49 | exec('cross-env BUILDING_DLL=true webpack --display-chunks --color --config internals/webpack/webpack.dll.babel.js')
50 |
--------------------------------------------------------------------------------
/client/internals/scripts/extract-intl.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /**
3 | * This script will extract the internationalization messages from all components
4 | and package them in the transalation json files in the translations file.
5 | */
6 | const fs = require('fs');
7 | const nodeGlob = require('glob');
8 | const transform = require('babel-core').transform;
9 |
10 | const animateProgress = require('./helpers/progress');
11 | const addCheckmark = require('./helpers/checkmark');
12 |
13 | const pkg = require('../../package.json');
14 | const i18n = require('../../app/i18n');
15 |
16 | require('shelljs/global');
17 |
18 | // Glob to match all js files except test files
19 | const FILES_TO_PARSE = 'app/**/!(*.test).js';
20 | const locales = i18n.appLocales;
21 |
22 | const newLine = () => process.stdout.write('\n');
23 |
24 | // Progress Logger
25 | let progress;
26 | const task = (message) => {
27 | progress = animateProgress(message);
28 | process.stdout.write(message);
29 |
30 | return (error) => {
31 | if (error) {
32 | process.stderr.write(error);
33 | }
34 | clearTimeout(progress);
35 | return addCheckmark(() => newLine());
36 | }
37 | }
38 |
39 | // Wrap async functions below into a promise
40 | const glob = (pattern) => new Promise((resolve, reject) => {
41 | nodeGlob(pattern, (error, value) => (error ? reject(error) : resolve(value)));
42 | });
43 |
44 | const readFile = (fileName) => new Promise((resolve, reject) => {
45 | fs.readFile(fileName, (error, value) => (error ? reject(error) : resolve(value)));
46 | });
47 |
48 | const writeFile = (fileName, data) => new Promise((resolve, reject) => {
49 | fs.writeFile(fileName, data, (error, value) => (error ? reject(error) : resolve(value)));
50 | });
51 |
52 | // Store existing translations into memory
53 | const oldLocaleMappings = [];
54 | const localeMappings = [];
55 | // Loop to run once per locale
56 | for (const locale of locales) {
57 | oldLocaleMappings[locale] = {};
58 | localeMappings[locale] = {};
59 | // File to store translation messages into
60 | const translationFileName = `app/translations/${locale}.json`;
61 | try {
62 | // Parse the old translation message JSON files
63 | const messages = JSON.parse(fs.readFileSync(translationFileName));
64 | for (const message of messages) {
65 | oldLocaleMappings[locale][message.id] = message;
66 | }
67 | } catch (error) {
68 | if (error.code !== 'ENOENT') {
69 | process.stderr.write(
70 | `There was an error loading this translation file: ${translationFileName}
71 | \n${error}`
72 | );
73 | }
74 | }
75 | }
76 |
77 | const extractFromFile = async (fileName) => {
78 | try {
79 | const code = await readFile(fileName);
80 | // Use babel plugin to extract instances where react-intl is used
81 | const { metadata: result } = await transform(code, {
82 | presets: pkg.babel.presets,
83 | plugins: [
84 | ['react-intl'],
85 | ],
86 | });
87 | for (const message of result['react-intl'].messages) {
88 | for (const locale of locales) {
89 | const oldLocaleMapping = oldLocaleMappings[locale][message.id];
90 | // Merge old translations into the babel extracted instances where react-intl is used
91 | localeMappings[locale][message.id] = {
92 | id: message.id,
93 | description: message.description,
94 | defaultMessage: message.defaultMessage,
95 | message: (oldLocaleMapping && oldLocaleMapping.message)
96 | ? oldLocaleMapping.message
97 | : '',
98 | };
99 | }
100 | }
101 | } catch (error) {
102 | process.stderr.write(`Error transforming file: ${fileName}\n${error}`);
103 | }
104 | };
105 |
106 | (async function main() {
107 | const memoryTaskDone = task('Storing language files in memory');
108 | const files = await glob(FILES_TO_PARSE);
109 | memoryTaskDone()
110 |
111 | const extractTaskDone = task('Run extraction on all files');
112 | // Run extraction on all files that match the glob on line 16
113 | await Promise.all(files.map((fileName) => extractFromFile(fileName)));
114 | extractTaskDone()
115 |
116 | // Make the directory if it doesn't exist, especially for first run
117 | mkdir('-p', 'app/translations');
118 | for (const locale of locales) {
119 | const translationFileName = `app/translations/${locale}.json`;
120 |
121 | try {
122 | const localeTaskDone = task(
123 | `Writing translation messages for ${locale} to: ${translationFileName}`
124 | );
125 |
126 | // Sort the translation JSON file so that git diffing is easier
127 | // Otherwise the translation messages will jump around every time we extract
128 | let messages = Object.values(localeMappings[locale]).sort((a, b) => {
129 | a = a.id.toUpperCase();
130 | b = b.id.toUpperCase();
131 | return do {
132 | if (a < b) -1;
133 | else if (a > b) 1;
134 | else 0;
135 | };
136 | });
137 |
138 | // Write to file the JSON representation of the translation messages
139 | const prettified = `${JSON.stringify(messages, null, 2)}\n`;
140 |
141 | await writeFile(translationFileName, prettified);
142 |
143 | localeTaskDone();
144 | } catch (error) {
145 | localeTaskDone(
146 | `There was an error saving this translation file: ${translationFileName}
147 | \n${error}`
148 | );
149 | }
150 | }
151 |
152 | process.exit()
153 | }());
154 |
--------------------------------------------------------------------------------
/client/internals/scripts/helpers/checkmark.js:
--------------------------------------------------------------------------------
1 | var chalk = require('chalk');
2 |
3 | /**
4 | * Adds mark check symbol
5 | */
6 | function addCheckMark(callback) {
7 | process.stdout.write(chalk.green(' ✓'));
8 | callback();
9 | }
10 |
11 | module.exports = addCheckMark;
12 |
--------------------------------------------------------------------------------
/client/internals/scripts/helpers/progress.js:
--------------------------------------------------------------------------------
1 | var readline = require('readline');
2 |
3 | /**
4 | * Adds an animated progress indicator
5 | *
6 | * @param {string} message The message to write next to the indicator
7 | * @param {number} amountOfDots The amount of dots you want to animate
8 | */
9 | function animateProgress(message, amountOfDots) {
10 | if (typeof amountOfDots !== 'number') {
11 | amountOfDots = 3;
12 | }
13 |
14 | var i = 0;
15 | return setInterval(function () {
16 | readline.cursorTo(process.stdout, 0);
17 | i = (i + 1) % (amountOfDots + 1);
18 | var dots = new Array(i + 1).join('.');
19 | process.stdout.write(message + dots);
20 | }, 500);
21 | }
22 |
23 | module.exports = animateProgress;
24 |
--------------------------------------------------------------------------------
/client/internals/scripts/npmcheckversion.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | var exec = require('child_process').exec;
3 | exec('npm -v', function (err, stdout, stderr) {
4 | if (err) throw err;
5 | if (parseFloat(stdout) < 3) {
6 | throw new Error('[ERROR: React Boilerplate] You need npm version @>=3');
7 | process.exit(1);
8 | }
9 | });
10 |
--------------------------------------------------------------------------------
/client/internals/scripts/pagespeed.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | process.stdin.resume();
4 | process.stdin.setEncoding('utf8');
5 |
6 | var ngrok = require('ngrok');
7 | var psi = require('psi');
8 | var chalk = require('chalk');
9 |
10 | log('\nStarting ngrok tunnel');
11 |
12 | startTunnel(runPsi);
13 |
14 | function runPsi(url) {
15 | log('\nStarting PageSpeed Insights');
16 | psi.output(url).then(function (err) {
17 | process.exit(0);
18 | });
19 | }
20 |
21 | function startTunnel(cb) {
22 | ngrok.connect(3000, function (err, url) {
23 | if (err) {
24 | log(chalk.red('\nERROR\n' + err));
25 | process.exit(0);
26 | }
27 |
28 | log('\nServing tunnel from: ' + chalk.magenta(url));
29 | cb(url);
30 | });
31 | }
32 |
33 | function log(string) {
34 | process.stdout.write(string);
35 | }
36 |
--------------------------------------------------------------------------------
/client/internals/testing/karma.conf.js:
--------------------------------------------------------------------------------
1 | const webpackConfig = require('../webpack/webpack.test.babel');
2 | const argv = require('minimist')(process.argv.slice(2));
3 | const path = require('path');
4 |
5 | module.exports = (config) => {
6 | config.set({
7 | frameworks: ['mocha'],
8 | reporters: ['coverage', 'mocha'],
9 | browsers: process.env.TRAVIS // eslint-disable-line no-nested-ternary
10 | ? ['ChromeTravis']
11 | : process.env.APPVEYOR
12 | ? ['IE'] : ['Chrome'],
13 |
14 | autoWatch: false,
15 | singleRun: true,
16 |
17 | client: {
18 | mocha: {
19 | grep: argv.grep,
20 | },
21 | },
22 |
23 | files: [
24 | {
25 | pattern: './test-bundler.js',
26 | watched: false,
27 | served: true,
28 | included: true,
29 | },
30 | ],
31 |
32 | preprocessors: {
33 | ['./test-bundler.js']: ['webpack', 'sourcemap'], // eslint-disable-line no-useless-computed-key
34 | },
35 |
36 | webpack: webpackConfig,
37 |
38 | // make Webpack bundle generation quiet
39 | webpackMiddleware: {
40 | noInfo: true,
41 | stats: 'errors-only',
42 | },
43 |
44 | customLaunchers: {
45 | ChromeTravis: {
46 | base: 'Chrome',
47 | flags: ['--no-sandbox'],
48 | },
49 | },
50 |
51 | coverageReporter: {
52 | dir: path.join(process.cwd(), 'coverage'),
53 | reporters: [
54 | { type: 'lcov', subdir: 'lcov' },
55 | { type: 'html', subdir: 'html' },
56 | { type: 'text-summary' },
57 | ],
58 | },
59 |
60 | });
61 | };
62 |
--------------------------------------------------------------------------------
/client/internals/testing/test-bundler.js:
--------------------------------------------------------------------------------
1 | // needed for regenerator-runtime
2 | // (ES7 generator support is required by redux-saga)
3 | import 'babel-polyfill';
4 |
5 | // If we need to use Chai, we'll have already chaiEnzyme loaded
6 | import chai from 'chai';
7 | import chaiEnzyme from 'chai-enzyme';
8 | chai.use(chaiEnzyme());
9 |
10 | // Include all .js files under `app`, except app.js, reducers.js, and routes.js.
11 | // This is for isparta code coverage
12 | const context = require.context('../../app', true, /^^((?!(app|reducers|routes)).)*\.js$/);
13 | context.keys().forEach(context);
14 |
--------------------------------------------------------------------------------
/client/internals/webpack/webpack.base.babel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * COMMON WEBPACK CONFIGURATION
3 | */
4 |
5 | const path = require('path');
6 | const webpack = require('webpack');
7 |
8 | module.exports = (options) => ({
9 | entry: options.entry,
10 | output: Object.assign({ // Compile into js/build.js
11 | path: path.resolve(process.cwd(), 'build'),
12 | publicPath: '/',
13 | }, options.output), // Merge with env dependent settings
14 | module: {
15 | loaders: [{
16 | test: /\.js$/, // Transform all .js files required somewhere with Babel
17 | loader: 'babel',
18 | exclude: /node_modules/,
19 | query: options.babelQuery,
20 | }, {
21 | // Transform our own .css files with PostCSS and CSS-modules
22 | test: /\.css$/,
23 | exclude: /node_modules/,
24 | loader: options.cssLoaders,
25 | }, {
26 | // Do not transform vendor's CSS with CSS-modules
27 | // The point is that they remain in global scope.
28 | // Since we require these CSS files in our JS or CSS files,
29 | // they will be a part of our compilation either way.
30 | // So, no need for ExtractTextPlugin here.
31 | test: /\.css$/,
32 | include: /node_modules/,
33 | loaders: ['style-loader', 'css-loader'],
34 | }, {
35 | test: /\.(eot|svg|ttf|woff|woff2)$/,
36 | loader: 'file-loader',
37 | }, {
38 | test: /\.(jpg|png|gif)$/,
39 | loaders: [
40 | 'file-loader',
41 | 'image-webpack?{progressive:true, optimizationLevel: 7, interlaced: false, pngquant:{quality: "65-90", speed: 4}}',
42 | ],
43 | }, {
44 | test: /\.html$/,
45 | loader: 'html-loader',
46 | }, {
47 | test: /\.json$/,
48 | loader: 'json-loader',
49 | }, {
50 | test: /\.(mp4|webm)$/,
51 | loader: 'url-loader?limit=10000',
52 | }],
53 | },
54 | plugins: options.plugins.concat([
55 | new webpack.ProvidePlugin({
56 | // make fetch available
57 | fetch: 'exports?self.fetch!whatwg-fetch',
58 | }),
59 |
60 | // Always expose NODE_ENV to webpack, in order to use `process.env.NODE_ENV`
61 | // inside your code for any environment checks; UglifyJS will automatically
62 | // drop any unreachable code.
63 | new webpack.DefinePlugin({
64 | 'process.env': {
65 | NODE_ENV: JSON.stringify(process.env.NODE_ENV),
66 | },
67 | }),
68 | ]),
69 | postcss: () => options.postcssPlugins,
70 | resolve: {
71 | modules: ['app', 'node_modules'],
72 | extensions: [
73 | '',
74 | '.js',
75 | '.jsx',
76 | '.react.js',
77 | ],
78 | mainFields: [
79 | 'jsnext:main',
80 | 'main',
81 | ],
82 | },
83 | devtool: options.devtool,
84 | target: 'web', // Make web variables accessible to webpack, e.g. window
85 | stats: false, // Don't show stats in the console
86 | progress: true,
87 | });
88 |
--------------------------------------------------------------------------------
/client/internals/webpack/webpack.dev.babel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * DEVELOPMENT WEBPACK CONFIGURATION
3 | */
4 |
5 | const path = require('path');
6 | const fs = require('fs');
7 | const webpack = require('webpack');
8 | const HtmlWebpackPlugin = require('html-webpack-plugin');
9 | const logger = require('../../server/logger');
10 | const cheerio = require('cheerio');
11 | const pkg = require(path.resolve(process.cwd(), 'package.json'));
12 | const dllPlugin = pkg.dllPlugin;
13 |
14 | // PostCSS plugins
15 | const cssnext = require('postcss-cssnext');
16 | const postcssFocus = require('postcss-focus');
17 | const postcssReporter = require('postcss-reporter');
18 |
19 | const plugins = [
20 | new webpack.HotModuleReplacementPlugin(), // Tell webpack we want hot reloading
21 | new webpack.NoErrorsPlugin(),
22 | new HtmlWebpackPlugin({
23 | inject: true, // Inject all files that are generated by webpack, e.g. bundle.js
24 | templateContent: templateContent(), // eslint-disable-line no-use-before-define
25 | }),
26 | ];
27 |
28 | module.exports = require('./webpack.base.babel')({
29 | // Add hot reloading in development
30 | entry: [
31 | 'eventsource-polyfill', // Necessary for hot reloading with IE
32 | 'webpack-hot-middleware/client',
33 | path.join(process.cwd(), 'app/app.js'), // Start with js/app.js
34 | ],
35 |
36 | // Don't use hashes in dev mode for better performance
37 | output: {
38 | filename: '[name].js',
39 | chunkFilename: '[name].chunk.js',
40 | },
41 |
42 | // Add development plugins
43 | plugins: dependencyHandlers().concat(plugins), // eslint-disable-line no-use-before-define
44 |
45 | // Load the CSS in a style tag in development
46 | cssLoaders: 'style-loader!css-loader?localIdentName=[local]__[path][name]__[hash:base64:5]&modules&importLoaders=1&sourceMap!postcss-loader',
47 |
48 | // Process the CSS with PostCSS
49 | postcssPlugins: [
50 | postcssFocus(), // Add a :focus to every :hover
51 | cssnext({ // Allow future CSS features to be used, also auto-prefixes the CSS...
52 | browsers: ['last 2 versions', 'IE > 10'], // ...based on this browser list
53 | }),
54 | postcssReporter({ // Posts messages from plugins to the terminal
55 | clearMessages: true,
56 | }),
57 | ],
58 |
59 | // Tell babel that we want to hot-reload
60 | babelQuery: {
61 | presets: ['react-hmre'],
62 | },
63 |
64 | // Emit a source map for easier debugging
65 | devtool: 'cheap-module-eval-source-map',
66 | });
67 |
68 | /**
69 | * Select which plugins to use to optimize the bundle's handling of
70 | * third party dependencies.
71 | *
72 | * If there is a dllPlugin key on the project's package.json, the
73 | * Webpack DLL Plugin will be used. Otherwise the CommonsChunkPlugin
74 | * will be used.
75 | *
76 | */
77 | function dependencyHandlers() {
78 | // Don't do anything during the DLL Build step
79 | if (process.env.BUILDING_DLL) { return []; }
80 |
81 | // If the package.json does not have a dllPlugin property, use the CommonsChunkPlugin
82 | if (!dllPlugin) {
83 | return [
84 | new webpack.optimize.CommonsChunkPlugin({
85 | name: 'vendor',
86 | children: true,
87 | minChunks: 2,
88 | async: true,
89 | }),
90 | ];
91 | }
92 |
93 | const dllPath = path.resolve(process.cwd(), dllPlugin.path || 'node_modules/react-boilerplate-dlls');
94 |
95 | /**
96 | * If DLLs aren't explicitly defined, we assume all production dependencies listed in package.json
97 | * Reminder: You need to exclude any server side dependencies by listing them in dllConfig.exclude
98 | *
99 | * @see https://github.com/mxstbr/react-boilerplate/tree/master/docs/general/webpack.md
100 | */
101 | if (!dllPlugin.dlls) {
102 | const manifestPath = path.resolve(dllPath, 'reactBoilerplateDeps.json');
103 |
104 | if (!fs.existsSync(manifestPath)) {
105 | logger.error('The DLL manifest is missing. Please run `npm run build:dll`');
106 | process.exit(0);
107 | }
108 |
109 | return [
110 | new webpack.DllReferencePlugin({
111 | context: process.cwd(),
112 | manifest: require(manifestPath), // eslint-disable-line global-require
113 | }),
114 | ];
115 | }
116 |
117 | // If DLLs are explicitly defined, we automatically create a DLLReferencePlugin for each of them.
118 | const dllManifests = Object.keys(dllPlugin.dlls).map((name) => path.join(dllPath, `/${name}.json`));
119 |
120 | return dllManifests.map((manifestPath) => {
121 | if (!fs.existsSync(path)) {
122 | if (!fs.existsSync(manifestPath)) {
123 | logger.error(`The following Webpack DLL manifest is missing: ${path.basename(manifestPath)}`);
124 | logger.error(`Expected to find it in ${dllPath}`);
125 | logger.error('Please run: npm run build:dll');
126 |
127 | process.exit(0);
128 | }
129 | }
130 |
131 | return new webpack.DllReferencePlugin({
132 | context: process.cwd(),
133 | manifest: require(manifestPath), // eslint-disable-line global-require
134 | });
135 | });
136 | }
137 |
138 | /**
139 | * We dynamically generate the HTML content in development so that the different
140 | * DLL Javascript files are loaded in script tags and available to our application.
141 | */
142 | function templateContent() {
143 | const html = fs.readFileSync(
144 | path.resolve(process.cwd(), 'app/index.html')
145 | ).toString();
146 |
147 | if (!dllPlugin) { return html; }
148 |
149 | const doc = cheerio(html);
150 | const body = doc.find('body');
151 | const dllNames = !dllPlugin.dlls ? ['reactBoilerplateDeps'] : Object.keys(dllPlugin.dlls);
152 |
153 | dllNames.forEach(dllName => body.append(``));
154 |
155 | return doc.toString();
156 | }
157 |
--------------------------------------------------------------------------------
/client/internals/webpack/webpack.dll.babel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * WEBPACK DLL GENERATOR
3 | *
4 | * This profile is used to cache webpack's module
5 | * contexts for external library and framework type
6 | * dependencies which will usually not change often enough
7 | * to warrant building them from scratch every time we use
8 | * the webpack process.
9 | */
10 |
11 | const { join } = require('path');
12 | const defaults = require('lodash/defaultsDeep');
13 | const webpack = require('webpack');
14 | const pkg = require(join(process.cwd(), 'package.json'));
15 | const dllPlugin = require('../config').dllPlugin;
16 |
17 | if (!pkg.dllPlugin) { process.exit(0); }
18 |
19 | const dllConfig = defaults(pkg.dllPlugin, dllPlugin.defaults);
20 | const outputPath = join(process.cwd(), dllConfig.path);
21 |
22 | module.exports = {
23 | context: process.cwd(),
24 | entry: dllConfig.dlls ? dllConfig.dlls : dllPlugin.entry(pkg),
25 | devtool: 'eval',
26 | output: {
27 | filename: '[name].dll.js',
28 | path: outputPath,
29 | library: '[name]',
30 | },
31 | plugins: [
32 | new webpack.DllPlugin({ name: '[name]', path: join(outputPath, '[name].json') }), // eslint-disable-line no-new
33 | ],
34 | };
35 |
--------------------------------------------------------------------------------
/client/internals/webpack/webpack.prod.babel.js:
--------------------------------------------------------------------------------
1 | // Important modules this config uses
2 | const path = require('path');
3 | const webpack = require('webpack');
4 | const HtmlWebpackPlugin = require('html-webpack-plugin');
5 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
6 | const OfflinePlugin = require('offline-plugin');
7 |
8 | // PostCSS plugins
9 | const cssnext = require('postcss-cssnext');
10 | const postcssFocus = require('postcss-focus');
11 | const postcssReporter = require('postcss-reporter');
12 |
13 | module.exports = require('./webpack.base.babel')({
14 | // In production, we skip all hot-reloading stuff
15 | entry: [
16 | path.join(process.cwd(), 'app/app.js'),
17 | ],
18 |
19 | // Utilize long-term caching by adding content hashes (not compilation hashes) to compiled assets
20 | output: {
21 | filename: '[name].[chunkhash].js',
22 | chunkFilename: '[name].[chunkhash].chunk.js',
23 | },
24 |
25 | // We use ExtractTextPlugin so we get a seperate CSS file instead
26 | // of the CSS being in the JS and injected as a style tag
27 | cssLoaders: ExtractTextPlugin.extract(
28 | 'style-loader',
29 | 'css-loader?modules&-autoprefixer&importLoaders=1!postcss-loader'
30 | ),
31 |
32 | // In production, we minify our CSS with cssnano
33 | postcssPlugins: [
34 | postcssFocus(),
35 | cssnext({
36 | browsers: ['last 2 versions', 'IE > 10'],
37 | }),
38 | postcssReporter({
39 | clearMessages: true,
40 | }),
41 | ],
42 | plugins: [
43 | new webpack.optimize.CommonsChunkPlugin({
44 | name: 'vendor',
45 | children: true,
46 | minChunks: 2,
47 | async: true,
48 | }),
49 |
50 | // OccurrenceOrderPlugin is needed for long-term caching to work properly.
51 | // See http://mxs.is/googmv
52 | new webpack.optimize.OccurrenceOrderPlugin(true),
53 |
54 | // Merge all duplicate modules
55 | new webpack.optimize.DedupePlugin(),
56 |
57 | // Minify and optimize the JavaScript
58 | new webpack.optimize.UglifyJsPlugin({
59 | compress: {
60 | warnings: false, // ...but do not show warnings in the console (there is a lot of them)
61 | },
62 | }),
63 |
64 | // Minify and optimize the index.html
65 | new HtmlWebpackPlugin({
66 | template: 'app/index.html',
67 | minify: {
68 | removeComments: true,
69 | collapseWhitespace: true,
70 | removeRedundantAttributes: true,
71 | useShortDoctype: true,
72 | removeEmptyAttributes: true,
73 | removeStyleLinkTypeAttributes: true,
74 | keepClosingSlash: true,
75 | minifyJS: true,
76 | minifyCSS: true,
77 | minifyURLs: true,
78 | },
79 | inject: true,
80 | }),
81 |
82 | // Extract the CSS into a seperate file
83 | new ExtractTextPlugin('[name].[contenthash].css'),
84 |
85 | // Put it in the end to capture all the HtmlWebpackPlugin's
86 | // assets manipulations and do leak its manipulations to HtmlWebpackPlugin
87 | new OfflinePlugin({
88 | relativePaths: false,
89 | publicPath: '/',
90 |
91 | // No need to cache .htaccess. See http://mxs.is/googmp,
92 | // this is applied before any match in `caches` section
93 | excludes: ['.htaccess'],
94 |
95 | caches: {
96 | main: [':rest:'],
97 |
98 | // All chunks marked as `additional`, loaded after main section
99 | // and do not prevent SW to install. Change to `optional` if
100 | // do not want them to be preloaded at all (cached only when first loaded)
101 | additional: ['*.chunk.js'],
102 | },
103 |
104 | // Removes warning for about `additional` section usage
105 | safeToUseOptionalCaches: true,
106 |
107 | AppCache: false,
108 | }),
109 | ],
110 | });
111 |
--------------------------------------------------------------------------------
/client/internals/webpack/webpack.test.babel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * TEST WEBPACK CONFIGURATION
3 | */
4 |
5 | const path = require('path');
6 | const webpack = require('webpack');
7 | const modules = [
8 | 'app',
9 | 'node_modules',
10 | ];
11 |
12 | module.exports = {
13 | devtool: 'inline-source-map',
14 | isparta: {
15 | babel: {
16 | presets: ['es2015', 'react', 'stage-0'],
17 | },
18 | },
19 | module: {
20 | // Some libraries don't like being run through babel.
21 | // If they gripe, put them here.
22 | noParse: [
23 | /node_modules(\\|\/)sinon/,
24 | /node_modules(\\|\/)acorn/,
25 | ],
26 | preLoaders: [
27 | { test: /\.js$/,
28 | loader: 'isparta',
29 | include: path.resolve('app/'),
30 | },
31 | ],
32 | loaders: [
33 | { test: /\.json$/, loader: 'json-loader' },
34 | { test: /\.css$/, loader: 'null-loader' },
35 |
36 | // sinon.js--aliased for enzyme--expects/requires global vars.
37 | // imports-loader allows for global vars to be injected into the module.
38 | // See https://github.com/webpack/webpack/issues/304
39 | { test: /sinon(\\|\/)pkg(\\|\/)sinon\.js/,
40 | loader: 'imports?define=>false,require=>false',
41 | },
42 | { test: /\.js$/,
43 | loader: 'babel',
44 | exclude: [/node_modules/],
45 | },
46 | { test: /\.jpe?g$|\.gif$|\.png$|\.svg$/i,
47 | loader: 'null-loader',
48 | },
49 | ],
50 | },
51 |
52 | plugins: [
53 |
54 | // Always expose NODE_ENV to webpack, in order to use `process.env.NODE_ENV`
55 | // inside your code for any environment checks; UglifyJS will automatically
56 | // drop any unreachable code.
57 | new webpack.DefinePlugin({
58 | 'process.env': {
59 | NODE_ENV: JSON.stringify(process.env.NODE_ENV),
60 | },
61 | })],
62 |
63 | // Some node_modules pull in Node-specific dependencies.
64 | // Since we're running in a browser we have to stub them out. See:
65 | // https://webpack.github.io/docs/configuration.html#node
66 | // https://github.com/webpack/node-libs-browser/tree/master/mock
67 | // https://github.com/webpack/jade-loader/issues/8#issuecomment-55568520
68 | node: {
69 | fs: 'empty',
70 | child_process: 'empty',
71 | net: 'empty',
72 | tls: 'empty',
73 | },
74 |
75 | // required for enzyme to work properly
76 | externals: {
77 | jsdom: 'window',
78 | 'react/addons': true,
79 | 'react/lib/ExecutionEnvironment': true,
80 | 'react/lib/ReactContext': 'window',
81 | },
82 | resolve: {
83 | modulesDirectories: modules,
84 | modules,
85 | alias: {
86 | // required for enzyme to work properly
87 | sinon: 'sinon/pkg/sinon',
88 | },
89 | },
90 | };
91 |
--------------------------------------------------------------------------------
/client/server/index.js:
--------------------------------------------------------------------------------
1 | /* eslint consistent-return:0 */
2 |
3 | const express = require('express');
4 | const logger = require('./logger');
5 |
6 | const argv = require('minimist')(process.argv.slice(2));
7 | const setup = require('./middlewares/frontendMiddleware');
8 | const isDev = process.env.NODE_ENV !== 'production';
9 | const ngrok = (isDev && process.env.ENABLE_TUNNEL) || argv.tunnel ? require('ngrok') : false;
10 | const resolve = require('path').resolve;
11 | const app = express();
12 |
13 | // If you need a backend, e.g. an API, add your custom backend-specific middleware here
14 | // app.use('/api', myApi);
15 |
16 | // In production we need to pass these values in instead of relying on webpack
17 | setup(app, {
18 | outputPath: resolve(process.cwd(), 'build'),
19 | publicPath: '/',
20 | });
21 |
22 | // get the intended port number, use port 3000 if not provided
23 | const port = argv.port || process.env.PORT || 3000;
24 |
25 | // Start your app.
26 | app.listen(port, (err) => {
27 | if (err) {
28 | return logger.error(err.message);
29 | }
30 |
31 | // Connect to ngrok in dev mode
32 | if (ngrok) {
33 | ngrok.connect(port, (innerErr, url) => {
34 | if (innerErr) {
35 | return logger.error(innerErr);
36 | }
37 |
38 | logger.appStarted(port, url);
39 | });
40 | } else {
41 | logger.appStarted(port);
42 | }
43 | });
44 |
--------------------------------------------------------------------------------
/client/server/logger.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 |
3 | const chalk = require('chalk');
4 | const ip = require('ip');
5 |
6 | const divider = chalk.gray('\n-----------------------------------');
7 |
8 | /**
9 | * Logger middleware, you can customize it to make messages more personal
10 | */
11 | const logger = {
12 |
13 | // Called whenever there's an error on the server we want to print
14 | error: err => {
15 | console.error(chalk.red(err));
16 | },
17 |
18 | // Called when express.js app starts on given port w/o errors
19 | appStarted: (port, tunnelStarted) => {
20 | console.log(`Server started ${chalk.green('✓')}`);
21 |
22 | // If the tunnel started, log that and the URL it's available at
23 | if (tunnelStarted) {
24 | console.log(`Tunnel initialised ${chalk.green('✓')}`);
25 | }
26 |
27 | console.log(`
28 | ${chalk.bold('Access URLs:')}${divider}
29 | Localhost: ${chalk.magenta(`http://localhost:${port}`)}
30 | LAN: ${chalk.magenta(`http://${ip.address()}:${port}`) +
31 | (tunnelStarted ? `\n Proxy: ${chalk.magenta(tunnelStarted)}` : '')}${divider}
32 | ${chalk.blue(`Press ${chalk.italic('CTRL-C')} to stop`)}
33 | `);
34 | },
35 | };
36 |
37 | module.exports = logger;
38 |
--------------------------------------------------------------------------------
/client/server/middlewares/frontendMiddleware.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable global-require */
2 | const express = require('express');
3 | const path = require('path');
4 | const compression = require('compression');
5 | const pkg = require(path.resolve(process.cwd(), 'package.json'));
6 |
7 | // Dev middleware
8 | const addDevMiddlewares = (app, webpackConfig) => {
9 | const webpack = require('webpack');
10 | const webpackDevMiddleware = require('webpack-dev-middleware');
11 | const webpackHotMiddleware = require('webpack-hot-middleware');
12 | const compiler = webpack(webpackConfig);
13 | const middleware = webpackDevMiddleware(compiler, {
14 | noInfo: true,
15 | publicPath: webpackConfig.output.publicPath,
16 | silent: true,
17 | stats: 'errors-only',
18 | });
19 |
20 | app.use(middleware);
21 | app.use(webpackHotMiddleware(compiler));
22 |
23 | // Since webpackDevMiddleware uses memory-fs internally to store build
24 | // artifacts, we use it instead
25 | const fs = middleware.fileSystem;
26 |
27 | if (pkg.dllPlugin) {
28 | app.get(/\.dll\.js$/, (req, res) => {
29 | const filename = req.path.replace(/^\//, '');
30 | res.sendFile(path.join(process.cwd(), pkg.dllPlugin.path, filename));
31 | });
32 | }
33 |
34 | app.get('*', (req, res) => {
35 | fs.readFile(path.join(compiler.outputPath, 'index.html'), (err, file) => {
36 | if (err) {
37 | res.sendStatus(404);
38 | } else {
39 | res.send(file.toString());
40 | }
41 | });
42 | });
43 | };
44 |
45 | // Production middlewares
46 | const addProdMiddlewares = (app, options) => {
47 | const publicPath = options.publicPath || '/';
48 | const outputPath = options.outputPath || path.resolve(process.cwd(), 'build');
49 |
50 | // compression middleware compresses your server responses which makes them
51 | // smaller (applies also to assets). You can read more about that technique
52 | // and other good practices on official Express.js docs http://mxs.is/googmy
53 | app.use(compression());
54 | app.use(publicPath, express.static(outputPath));
55 |
56 | app.get('*', (req, res) => res.sendFile(path.resolve(outputPath, 'index.html')));
57 | };
58 |
59 | /**
60 | * Front-end middleware
61 | */
62 | module.exports = (app, options) => {
63 | const isProd = process.env.NODE_ENV === 'production';
64 |
65 | if (isProd) {
66 | addProdMiddlewares(app, options);
67 | } else {
68 | const webpackConfig = require('../../internals/webpack/webpack.dev.babel');
69 | addDevMiddlewares(app, webpackConfig);
70 | }
71 |
72 | return app;
73 | };
74 |
--------------------------------------------------------------------------------
/server/CleanCo.Common/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 |
84 | # Visual Studio profiler
85 | *.psess
86 | *.vsp
87 | *.vspx
88 | *.sap
89 |
90 | # TFS 2012 Local Workspace
91 | $tf/
92 |
93 | # Guidance Automation Toolkit
94 | *.gpState
95 |
96 | # ReSharper is a .NET coding add-in
97 | _ReSharper*/
98 | *.[Rr]e[Ss]harper
99 | *.DotSettings.user
100 |
101 | # JustCode is a .NET coding add-in
102 | .JustCode
103 |
104 | # TeamCity is a build add-in
105 | _TeamCity*
106 |
107 | # DotCover is a Code Coverage Tool
108 | *.dotCover
109 |
110 | # NCrunch
111 | _NCrunch_*
112 | .*crunch*.local.xml
113 | nCrunchTemp_*
114 |
115 | # MightyMoose
116 | *.mm.*
117 | AutoTest.Net/
118 |
119 | # Web workbench (sass)
120 | .sass-cache/
121 |
122 | # Installshield output folder
123 | [Ee]xpress/
124 |
125 | # DocProject is a documentation generator add-in
126 | DocProject/buildhelp/
127 | DocProject/Help/*.HxT
128 | DocProject/Help/*.HxC
129 | DocProject/Help/*.hhc
130 | DocProject/Help/*.hhk
131 | DocProject/Help/*.hhp
132 | DocProject/Help/Html2
133 | DocProject/Help/html
134 |
135 | # Click-Once directory
136 | publish/
137 |
138 | # Publish Web Output
139 | *.[Pp]ublish.xml
140 | *.azurePubxml
141 | # TODO: Comment the next line if you want to checkin your web deploy settings
142 | # but database connection strings (with potential passwords) will be unencrypted
143 | *.pubxml
144 | *.publishproj
145 |
146 | # NuGet Packages
147 | *.nupkg
148 | # The packages folder can be ignored because of Package Restore
149 | **/packages/*
150 | # except build/, which is used as an MSBuild target.
151 | !**/packages/build/
152 | # Uncomment if necessary however generally it will be regenerated when needed
153 | #!**/packages/repositories.config
154 |
155 | # Microsoft Azure Build Output
156 | csx/
157 | *.build.csdef
158 |
159 | # Microsoft Azure Emulator
160 | ecf/
161 | rcf/
162 |
163 | # Microsoft Azure ApplicationInsights config file
164 | ApplicationInsights.config
165 |
166 | # Windows Store app package directory
167 | AppPackages/
168 | BundleArtifacts/
169 |
170 | # Visual Studio cache files
171 | # files ending in .cache can be ignored
172 | *.[Cc]ache
173 | # but keep track of directories ending in .cache
174 | !*.[Cc]ache/
175 |
176 | # Others
177 | ClientBin/
178 | ~$*
179 | *~
180 | *.dbmdl
181 | *.dbproj.schemaview
182 | *.pfx
183 | *.publishsettings
184 | node_modules/
185 | orleans.codegen.cs
186 |
187 | # RIA/Silverlight projects
188 | Generated_Code/
189 |
190 | # Backup & report files from converting an old project file
191 | # to a newer Visual Studio version. Backup files are not needed,
192 | # because we have git ;-)
193 | _UpgradeReport_Files/
194 | Backup*/
195 | UpgradeLog*.XML
196 | UpgradeLog*.htm
197 |
198 | # SQL Server files
199 | *.mdf
200 | *.ldf
201 |
202 | # Business Intelligence projects
203 | *.rdl.data
204 | *.bim.layout
205 | *.bim_*.settings
206 |
207 | # Microsoft Fakes
208 | FakesAssemblies/
209 |
210 | # GhostDoc plugin setting file
211 | *.GhostDoc.xml
212 |
213 | # Node.js Tools for Visual Studio
214 | .ntvs_analysis.dat
215 |
216 | # Visual Studio 6 build log
217 | *.plg
218 |
219 | # Visual Studio 6 workspace options file
220 | *.opt
221 |
222 | # Visual Studio LightSwitch build output
223 | **/*.HTMLClient/GeneratedArtifacts
224 | **/*.DesktopClient/GeneratedArtifacts
225 | **/*.DesktopClient/ModelManifest.xml
226 | **/*.Server/GeneratedArtifacts
227 | **/*.Server/ModelManifest.xml
228 | _Pvt_Extensions
229 |
230 | # Paket dependency manager
231 | .paket/paket.exe
232 |
233 | # FAKE - F# Make
234 | .fake/
235 |
--------------------------------------------------------------------------------
/server/CleanCo.Common/IRecord.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CleanCo.Common
4 | {
5 | public interface IRecord
6 | {
7 | Guid Id { get; set; }
8 | }
9 | }
--------------------------------------------------------------------------------
/server/CleanCo.Common/IRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 |
5 | namespace CleanCo.Common
6 | {
7 | public interface IRepository
8 | {
9 | List RetrieveAll();
10 | List Find(string filter);
11 | T RetrieveById(Guid id);
12 | void Save(T record);
13 | void Delete(Guid id);
14 | int RecordCount{ get; }
15 | }
16 | }
--------------------------------------------------------------------------------
/server/CleanCo.Common/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0-*",
3 |
4 | "dependencies": {
5 | "NETStandard.Library": "1.6.0"
6 | },
7 |
8 | "frameworks": {
9 | "netstandard1.6": {
10 | "imports": "dnxcore50"
11 | }
12 | },
13 |
14 | "tooling": {
15 | "defaultNamespace": "CleanCo.Common"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/server/CleanCo.Ecomm.Catalog/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 |
84 | # Visual Studio profiler
85 | *.psess
86 | *.vsp
87 | *.vspx
88 | *.sap
89 |
90 | # TFS 2012 Local Workspace
91 | $tf/
92 |
93 | # Guidance Automation Toolkit
94 | *.gpState
95 |
96 | # ReSharper is a .NET coding add-in
97 | _ReSharper*/
98 | *.[Rr]e[Ss]harper
99 | *.DotSettings.user
100 |
101 | # JustCode is a .NET coding add-in
102 | .JustCode
103 |
104 | # TeamCity is a build add-in
105 | _TeamCity*
106 |
107 | # DotCover is a Code Coverage Tool
108 | *.dotCover
109 |
110 | # NCrunch
111 | _NCrunch_*
112 | .*crunch*.local.xml
113 | nCrunchTemp_*
114 |
115 | # MightyMoose
116 | *.mm.*
117 | AutoTest.Net/
118 |
119 | # Web workbench (sass)
120 | .sass-cache/
121 |
122 | # Installshield output folder
123 | [Ee]xpress/
124 |
125 | # DocProject is a documentation generator add-in
126 | DocProject/buildhelp/
127 | DocProject/Help/*.HxT
128 | DocProject/Help/*.HxC
129 | DocProject/Help/*.hhc
130 | DocProject/Help/*.hhk
131 | DocProject/Help/*.hhp
132 | DocProject/Help/Html2
133 | DocProject/Help/html
134 |
135 | # Click-Once directory
136 | publish/
137 |
138 | # Publish Web Output
139 | *.[Pp]ublish.xml
140 | *.azurePubxml
141 | # TODO: Comment the next line if you want to checkin your web deploy settings
142 | # but database connection strings (with potential passwords) will be unencrypted
143 | *.pubxml
144 | *.publishproj
145 |
146 | # NuGet Packages
147 | *.nupkg
148 | # The packages folder can be ignored because of Package Restore
149 | **/packages/*
150 | # except build/, which is used as an MSBuild target.
151 | !**/packages/build/
152 | # Uncomment if necessary however generally it will be regenerated when needed
153 | #!**/packages/repositories.config
154 |
155 | # Microsoft Azure Build Output
156 | csx/
157 | *.build.csdef
158 |
159 | # Microsoft Azure Emulator
160 | ecf/
161 | rcf/
162 |
163 | # Microsoft Azure ApplicationInsights config file
164 | ApplicationInsights.config
165 |
166 | # Windows Store app package directory
167 | AppPackages/
168 | BundleArtifacts/
169 |
170 | # Visual Studio cache files
171 | # files ending in .cache can be ignored
172 | *.[Cc]ache
173 | # but keep track of directories ending in .cache
174 | !*.[Cc]ache/
175 |
176 | # Others
177 | ClientBin/
178 | ~$*
179 | *~
180 | *.dbmdl
181 | *.dbproj.schemaview
182 | *.pfx
183 | *.publishsettings
184 | node_modules/
185 | orleans.codegen.cs
186 |
187 | # RIA/Silverlight projects
188 | Generated_Code/
189 |
190 | # Backup & report files from converting an old project file
191 | # to a newer Visual Studio version. Backup files are not needed,
192 | # because we have git ;-)
193 | _UpgradeReport_Files/
194 | Backup*/
195 | UpgradeLog*.XML
196 | UpgradeLog*.htm
197 |
198 | # SQL Server files
199 | *.mdf
200 | *.ldf
201 |
202 | # Business Intelligence projects
203 | *.rdl.data
204 | *.bim.layout
205 | *.bim_*.settings
206 |
207 | # Microsoft Fakes
208 | FakesAssemblies/
209 |
210 | # GhostDoc plugin setting file
211 | *.GhostDoc.xml
212 |
213 | # Node.js Tools for Visual Studio
214 | .ntvs_analysis.dat
215 |
216 | # Visual Studio 6 build log
217 | *.plg
218 |
219 | # Visual Studio 6 workspace options file
220 | *.opt
221 |
222 | # Visual Studio LightSwitch build output
223 | **/*.HTMLClient/GeneratedArtifacts
224 | **/*.DesktopClient/GeneratedArtifacts
225 | **/*.DesktopClient/ModelManifest.xml
226 | **/*.Server/GeneratedArtifacts
227 | **/*.Server/ModelManifest.xml
228 | _Pvt_Extensions
229 |
230 | # Paket dependency manager
231 | .paket/paket.exe
232 |
233 | # FAKE - F# Make
234 | .fake/
235 |
--------------------------------------------------------------------------------
/server/CleanCo.Ecomm.Catalog/Catalog.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using CleanCo.Common;
4 | using CleanCo.Products;
5 |
6 | namespace CleanCo.Ecomm
7 | {
8 | public class Catalog {
9 |
10 | private IRepository repo;
11 |
12 | // TODO: remove this when repo.Find is implemented.
13 | private List names = new List();
14 | public Catalog(IRepository repo)
15 | {
16 | if (repo == null) throw new ArgumentNullException("repo can't be null");
17 | this.repo = repo;
18 | seedRepo();
19 | }
20 |
21 | public void AddProduct(Product product)
22 | {
23 | if(names.Contains(product.Name))
24 | {
25 | throw new ArgumentOutOfRangeException("That name already exists!");
26 | }
27 | repo.Save(product);
28 | // TODO: remove this when repo.Find is implemented.
29 | names.Add(product.Name);
30 | }
31 |
32 | public List GetProducts()
33 | {
34 | return repo.RetrieveAll();
35 | }
36 |
37 | public Product GetProductById(Guid id)
38 | {
39 | return repo.RetrieveById(id);
40 | }
41 |
42 | private void seedRepo()
43 | {
44 | if (this.repo.RecordCount == 0) {
45 | Product lumberjack = new Product("Lumberjack Lather Soap");
46 | lumberjack.Price = 6.95m;
47 | lumberjack.PrimaryImage = new Uri("http://ft.b5z.net/i/u/6131027/i/ec/lumberjack_lather1.jpg");
48 | lumberjack.AdditionalImages = new List();
49 | lumberjack.AdditionalImages.Add(new Uri("http://ft.b5z.net/zirw/805/i/u/6131027/i/ec/lumberjack_lather2.jpg"));
50 | lumberjack.Description = "Cedarwood, Fir Needle, Eucalyptus, Aloe, Black Spruce, Agave Root & Yellow Dock Root\n\n" +
51 | "An earthy woodsy soap with Cedarwood, Fir Needle, Eucalyptus, Aloe, Black Spruce, " +
52 | "Agave Root & Yellow Dock Root.\n\n" +
53 | "A soap that's as tough as wood. Because you want to be a lumberjack and you're ok. " +
54 | "A 2013 contest winning soap from Greg in Seattle, Washington.";
55 | this.AddProduct(lumberjack);
56 |
57 | Product matcha = new Product("Matcha Matcha Man");
58 | matcha.Price = 6.95m;
59 | matcha.PrimaryImage = new Uri("http://ft.b5z.net/i/u/6131027/i/matcha_matcha_man_1.jpg");
60 | matcha.AdditionalImages = new List();
61 | matcha.AdditionalImages.Add(new Uri("http://ft.b5z.net/zirw/805/i/u/6131027/i/matcha_matcha_man_2.jpg"));
62 | matcha.Description = "Matcha Tea Powder, Willow Charcoal, French Green Clay, Jasmine, Tea Tree, Ylang Ylang & Orange\n\n" +
63 | "Angelica in Los Angeles, CA knows what she like in a man -- a real man, a manly man, a man who likes healthy moisturized " +
64 | "skin and a smooth firm bar of soap with lots of antioxidants. That's why her Matcha Matcha Man is one of our " +
65 | "2016 soap contest winners!";
66 | this.AddProduct(matcha);
67 |
68 | Product boardwalk = new Product("Boardwalk Hempire");
69 | boardwalk.Price = 6.95m;
70 | boardwalk.PrimaryImage = new Uri("http://ft.b5z.net/i/u/6131027/i/boardwalk_hempire2_1.jpg");
71 | boardwalk.AdditionalImages = new List();
72 | boardwalk.AdditionalImages.Add(new Uri("http://soaptopia.com/i/u/6131027/i/boardwalk_hempire2_2.jpg"));
73 | boardwalk.Description = "A wood patchouli blend with ruby red grapefruit, lemon & hemp oil\n\n" +
74 | "Equally inspired by the lush green pacific slopes of Humbolt and the drum circles of Venice Beach, " +
75 | "this soap has a grounded, earthy scent with fresh, crisp, light notes.";
76 | this.AddProduct(boardwalk);
77 | }
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/server/CleanCo.Ecomm.Catalog/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0-*",
3 |
4 | "dependencies": {
5 | "NETStandard.Library": "1.6.0",
6 | "CleanCo.Common": {
7 | "target": "project"
8 | },
9 | "CleanCo.Products": {
10 | "target": "project"
11 | }
12 | },
13 |
14 | "frameworks": {
15 | "netstandard1.6": {
16 | "imports": "dnxcore50"
17 | }
18 | },
19 |
20 | "tooling": {
21 | "defaultNamespace": "CleanCo.Ecomm.Catalog"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/server/CleanCo.Ecomm.RepoAdapter/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 |
84 | # Visual Studio profiler
85 | *.psess
86 | *.vsp
87 | *.vspx
88 | *.sap
89 |
90 | # TFS 2012 Local Workspace
91 | $tf/
92 |
93 | # Guidance Automation Toolkit
94 | *.gpState
95 |
96 | # ReSharper is a .NET coding add-in
97 | _ReSharper*/
98 | *.[Rr]e[Ss]harper
99 | *.DotSettings.user
100 |
101 | # JustCode is a .NET coding add-in
102 | .JustCode
103 |
104 | # TeamCity is a build add-in
105 | _TeamCity*
106 |
107 | # DotCover is a Code Coverage Tool
108 | *.dotCover
109 |
110 | # NCrunch
111 | _NCrunch_*
112 | .*crunch*.local.xml
113 | nCrunchTemp_*
114 |
115 | # MightyMoose
116 | *.mm.*
117 | AutoTest.Net/
118 |
119 | # Web workbench (sass)
120 | .sass-cache/
121 |
122 | # Installshield output folder
123 | [Ee]xpress/
124 |
125 | # DocProject is a documentation generator add-in
126 | DocProject/buildhelp/
127 | DocProject/Help/*.HxT
128 | DocProject/Help/*.HxC
129 | DocProject/Help/*.hhc
130 | DocProject/Help/*.hhk
131 | DocProject/Help/*.hhp
132 | DocProject/Help/Html2
133 | DocProject/Help/html
134 |
135 | # Click-Once directory
136 | publish/
137 |
138 | # Publish Web Output
139 | *.[Pp]ublish.xml
140 | *.azurePubxml
141 | # TODO: Comment the next line if you want to checkin your web deploy settings
142 | # but database connection strings (with potential passwords) will be unencrypted
143 | *.pubxml
144 | *.publishproj
145 |
146 | # NuGet Packages
147 | *.nupkg
148 | # The packages folder can be ignored because of Package Restore
149 | **/packages/*
150 | # except build/, which is used as an MSBuild target.
151 | !**/packages/build/
152 | # Uncomment if necessary however generally it will be regenerated when needed
153 | #!**/packages/repositories.config
154 |
155 | # Microsoft Azure Build Output
156 | csx/
157 | *.build.csdef
158 |
159 | # Microsoft Azure Emulator
160 | ecf/
161 | rcf/
162 |
163 | # Microsoft Azure ApplicationInsights config file
164 | ApplicationInsights.config
165 |
166 | # Windows Store app package directory
167 | AppPackages/
168 | BundleArtifacts/
169 |
170 | # Visual Studio cache files
171 | # files ending in .cache can be ignored
172 | *.[Cc]ache
173 | # but keep track of directories ending in .cache
174 | !*.[Cc]ache/
175 |
176 | # Others
177 | ClientBin/
178 | ~$*
179 | *~
180 | *.dbmdl
181 | *.dbproj.schemaview
182 | *.pfx
183 | *.publishsettings
184 | node_modules/
185 | orleans.codegen.cs
186 |
187 | # RIA/Silverlight projects
188 | Generated_Code/
189 |
190 | # Backup & report files from converting an old project file
191 | # to a newer Visual Studio version. Backup files are not needed,
192 | # because we have git ;-)
193 | _UpgradeReport_Files/
194 | Backup*/
195 | UpgradeLog*.XML
196 | UpgradeLog*.htm
197 |
198 | # SQL Server files
199 | *.mdf
200 | *.ldf
201 |
202 | # Business Intelligence projects
203 | *.rdl.data
204 | *.bim.layout
205 | *.bim_*.settings
206 |
207 | # Microsoft Fakes
208 | FakesAssemblies/
209 |
210 | # GhostDoc plugin setting file
211 | *.GhostDoc.xml
212 |
213 | # Node.js Tools for Visual Studio
214 | .ntvs_analysis.dat
215 |
216 | # Visual Studio 6 build log
217 | *.plg
218 |
219 | # Visual Studio 6 workspace options file
220 | *.opt
221 |
222 | # Visual Studio LightSwitch build output
223 | **/*.HTMLClient/GeneratedArtifacts
224 | **/*.DesktopClient/GeneratedArtifacts
225 | **/*.DesktopClient/ModelManifest.xml
226 | **/*.Server/GeneratedArtifacts
227 | **/*.Server/ModelManifest.xml
228 | _Pvt_Extensions
229 |
230 | # Paket dependency manager
231 | .paket/paket.exe
232 |
233 | # FAKE - F# Make
234 | .fake/
235 |
--------------------------------------------------------------------------------
/server/CleanCo.Ecomm.RepoAdapter/Repository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using CleanCo.JsonAdapter;
5 | using CleanCo.Common;
6 |
7 | namespace CleanCo.RepoAdapter
8 | {
9 |
10 | public class Repository: IRepository where T: IRecord
11 | {
12 | private static IDictionary store;
13 |
14 | static Repository()
15 | {
16 | store = new Dictionary();
17 | }
18 |
19 | public void Delete(Guid id)
20 | {
21 | store.Remove(id);
22 | }
23 | public List Find(string filter)
24 | {
25 | throw new NotImplementedException("Test me first");
26 | }
27 | public List RetrieveAll()
28 | {
29 | List list = new List();
30 | foreach (string item in store.Values)
31 | {
32 | T itemAsT = JsonHelper.FromJson(item);
33 | list.Add(itemAsT);
34 | }
35 | return list;
36 | }
37 | public T RetrieveById(Guid id)
38 | {
39 | validateId(id);
40 | if(store.ContainsKey(id))
41 | {
42 | string jsonRecord = store[id];
43 | T record = JsonHelper.FromJson(jsonRecord);
44 | return record;
45 | }
46 | return default(T);
47 |
48 | }
49 | public List RetrieveNext(Guid last, int max)
50 | {
51 | throw new NotImplementedException("Test me first");
52 | }
53 | public void Save(T record)
54 | {
55 | validateRecord(record);
56 | if(store.ContainsKey(record.Id))
57 | {
58 | add(record);
59 | }
60 | else
61 | {
62 | update(record);
63 | }
64 | }
65 |
66 |
67 | private void add(T record)
68 | {
69 | string recordAsJson = JsonHelper.ToJson(record);
70 | store.Add(record.Id, recordAsJson);
71 | }
72 | private void update(T record)
73 | {
74 | string recordAsJson = JsonHelper.ToJson(record);
75 | store[record.Id] = recordAsJson;
76 | }
77 | private void validateRecord(T record)
78 | {
79 | if(record == null)
80 | {
81 | throw new ArgumentNullException("record can not be null");
82 | }
83 | validateId(record.Id);
84 | }
85 | private void validateId(Guid id)
86 | {
87 | if(id == Guid.Empty)
88 | {
89 | throw new ArgumentException("Id can not be empty");
90 | }
91 | }
92 |
93 | public int RecordCount
94 | {
95 | get
96 | {
97 | return store.Count;
98 | }
99 | }
100 | }
101 |
102 | }
--------------------------------------------------------------------------------
/server/CleanCo.Ecomm.RepoAdapter/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0-*",
3 |
4 | "dependencies": {
5 | "NETStandard.Library": "1.6.0",
6 | "Newtonsoft.Json": "9.0.1",
7 | "CleanCo.Common": {
8 | "target": "project"
9 | },
10 | "CleanCo.JsonAdapter": {
11 | "target": "project"
12 | }
13 | },
14 |
15 | "frameworks": {
16 | "netstandard1.6": {
17 | "imports": "dnxcore50"
18 | }
19 | },
20 |
21 | "tooling": {
22 | "defaultNamespace": "Clean.RepoAdapter"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/server/CleanCo.Ecomm.RestAdapter/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 |
84 | # Visual Studio profiler
85 | *.psess
86 | *.vsp
87 | *.vspx
88 | *.sap
89 |
90 | # TFS 2012 Local Workspace
91 | $tf/
92 |
93 | # Guidance Automation Toolkit
94 | *.gpState
95 |
96 | # ReSharper is a .NET coding add-in
97 | _ReSharper*/
98 | *.[Rr]e[Ss]harper
99 | *.DotSettings.user
100 |
101 | # JustCode is a .NET coding add-in
102 | .JustCode
103 |
104 | # TeamCity is a build add-in
105 | _TeamCity*
106 |
107 | # DotCover is a Code Coverage Tool
108 | *.dotCover
109 |
110 | # NCrunch
111 | _NCrunch_*
112 | .*crunch*.local.xml
113 | nCrunchTemp_*
114 |
115 | # MightyMoose
116 | *.mm.*
117 | AutoTest.Net/
118 |
119 | # Web workbench (sass)
120 | .sass-cache/
121 |
122 | # Installshield output folder
123 | [Ee]xpress/
124 |
125 | # DocProject is a documentation generator add-in
126 | DocProject/buildhelp/
127 | DocProject/Help/*.HxT
128 | DocProject/Help/*.HxC
129 | DocProject/Help/*.hhc
130 | DocProject/Help/*.hhk
131 | DocProject/Help/*.hhp
132 | DocProject/Help/Html2
133 | DocProject/Help/html
134 |
135 | # Click-Once directory
136 | publish/
137 |
138 | # Publish Web Output
139 | *.[Pp]ublish.xml
140 | *.azurePubxml
141 | # TODO: Comment the next line if you want to checkin your web deploy settings
142 | # but database connection strings (with potential passwords) will be unencrypted
143 | *.pubxml
144 | *.publishproj
145 |
146 | # NuGet Packages
147 | *.nupkg
148 | # The packages folder can be ignored because of Package Restore
149 | **/packages/*
150 | # except build/, which is used as an MSBuild target.
151 | !**/packages/build/
152 | # Uncomment if necessary however generally it will be regenerated when needed
153 | #!**/packages/repositories.config
154 |
155 | # Microsoft Azure Build Output
156 | csx/
157 | *.build.csdef
158 |
159 | # Microsoft Azure Emulator
160 | ecf/
161 | rcf/
162 |
163 | # Microsoft Azure ApplicationInsights config file
164 | ApplicationInsights.config
165 |
166 | # Windows Store app package directory
167 | AppPackages/
168 | BundleArtifacts/
169 |
170 | # Visual Studio cache files
171 | # files ending in .cache can be ignored
172 | *.[Cc]ache
173 | # but keep track of directories ending in .cache
174 | !*.[Cc]ache/
175 |
176 | # Others
177 | ClientBin/
178 | ~$*
179 | *~
180 | *.dbmdl
181 | *.dbproj.schemaview
182 | *.pfx
183 | *.publishsettings
184 | node_modules/
185 | orleans.codegen.cs
186 |
187 | # RIA/Silverlight projects
188 | Generated_Code/
189 |
190 | # Backup & report files from converting an old project file
191 | # to a newer Visual Studio version. Backup files are not needed,
192 | # because we have git ;-)
193 | _UpgradeReport_Files/
194 | Backup*/
195 | UpgradeLog*.XML
196 | UpgradeLog*.htm
197 |
198 | # SQL Server files
199 | *.mdf
200 | *.ldf
201 |
202 | # Business Intelligence projects
203 | *.rdl.data
204 | *.bim.layout
205 | *.bim_*.settings
206 |
207 | # Microsoft Fakes
208 | FakesAssemblies/
209 |
210 | # GhostDoc plugin setting file
211 | *.GhostDoc.xml
212 |
213 | # Node.js Tools for Visual Studio
214 | .ntvs_analysis.dat
215 |
216 | # Visual Studio 6 build log
217 | *.plg
218 |
219 | # Visual Studio 6 workspace options file
220 | *.opt
221 |
222 | # Visual Studio LightSwitch build output
223 | **/*.HTMLClient/GeneratedArtifacts
224 | **/*.DesktopClient/GeneratedArtifacts
225 | **/*.DesktopClient/ModelManifest.xml
226 | **/*.Server/GeneratedArtifacts
227 | **/*.Server/ModelManifest.xml
228 | _Pvt_Extensions
229 |
230 | # Paket dependency manager
231 | .paket/paket.exe
232 |
233 | # FAKE - F# Make
234 | .fake/
235 |
--------------------------------------------------------------------------------
/server/CleanCo.Ecomm.RestAdapter/Controllers/CatalogController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net.Http;
5 | using System.Threading.Tasks;
6 |
7 | using CleanCo.RepoAdapter;
8 | using CleanCo.Products;
9 | using CleanCo.Common;
10 | using CleanCo.Ecomm;
11 |
12 | using Microsoft.AspNetCore.Mvc;
13 |
14 | namespace CleanCo.Ecomm.RestAdapter
15 | {
16 | [Route("api/[controller]")]
17 | public class CatalogController : Controller
18 | {
19 | // GET api/catalog
20 | [HttpGet]
21 | public IEnumerable Get()
22 | {
23 | Catalog catalog;
24 | IRepository repo;
25 |
26 | repo = new Repository();
27 | catalog = new Catalog(repo);
28 | return (IEnumerable)catalog.GetProducts();
29 | }
30 |
31 | // GET api/values/5
32 | [HttpGet("{id}")]
33 | public IActionResult Get(string id)
34 | {
35 | Catalog catalog;
36 | IRepository repo;
37 | Product product;
38 |
39 | repo = new Repository();
40 | catalog = new Catalog(repo);
41 |
42 | product = (Product)catalog.GetProductById(Guid.Parse(id));
43 | if(product == null)
44 | {
45 | return NotFound();
46 | }
47 | return new ObjectResult(product);
48 | }
49 |
50 | // POST api/values
51 | [HttpPost]
52 | public void Post([FromBody]string value)
53 | {
54 |
55 | }
56 |
57 | // PUT api/values/5
58 | [HttpPut("{id}")]
59 | public void Put(int id, [FromBody]string value)
60 | {
61 | }
62 |
63 | // DELETE api/values/5
64 | [HttpDelete("{id}")]
65 | public void Delete(int id)
66 | {
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/server/CleanCo.Ecomm.RestAdapter/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM microsoft/dotnet:latest
2 |
3 | COPY . /app
4 |
5 | WORKDIR /app
6 |
7 | RUN ["dotnet", "restore"]
8 |
9 | RUN ["dotnet", "build"]
10 |
11 | EXPOSE 5000/tcp
12 |
13 | CMD ["dotnet", "run", "--server.urls", "http://*:5000"]
14 |
--------------------------------------------------------------------------------
/server/CleanCo.Ecomm.RestAdapter/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using Microsoft.AspNetCore.Hosting;
7 | using Microsoft.AspNetCore.Builder;
8 | using Microsoft.Extensions.Configuration;
9 |
10 | namespace Clean.Adapters.Rest
11 | {
12 | public class Program
13 | {
14 | public static void Main(string[] args)
15 | {
16 | var config = new ConfigurationBuilder()
17 | .AddCommandLine(args)
18 | .AddEnvironmentVariables(prefix: "ASPNETCORE_")
19 | .Build();
20 |
21 | var host = new WebHostBuilder()
22 | .UseConfiguration(config)
23 | .UseKestrel()
24 | .UseContentRoot(Directory.GetCurrentDirectory())
25 | .UseIISIntegration()
26 | .UseStartup()
27 | .Build();
28 |
29 | host.Run();
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/server/CleanCo.Ecomm.RestAdapter/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:1479/",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "launchUrl": "api/values",
15 | "environmentVariables": {
16 | "ASPNETCORE_ENVIRONMENT": "Development"
17 | }
18 | },
19 | "Clean.Adapters.Rest": {
20 | "commandName": "Project",
21 | "launchBrowser": true,
22 | "launchUrl": "http://localhost:5000/api/values",
23 | "environmentVariables": {
24 | "ASPNETCORE_ENVIRONMENT": "Development"
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/server/CleanCo.Ecomm.RestAdapter/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to ASP.NET Core
2 |
3 | We've made some big updates in this release, so it’s **important** that you spend a few minutes to learn what’s new.
4 |
5 | You've created a new ASP.NET Core project. [Learn what's new](https://go.microsoft.com/fwlink/?LinkId=518016)
6 |
7 | ## This application consists of:
8 |
9 | * Sample pages using ASP.NET Core MVC
10 | * [Bower](https://go.microsoft.com/fwlink/?LinkId=518004) for managing client-side libraries
11 | * Theming using [Bootstrap](https://go.microsoft.com/fwlink/?LinkID=398939)
12 |
13 | ## How to
14 |
15 | * [Add a Controller and View](https://go.microsoft.com/fwlink/?LinkID=398600)
16 | * [Add an appsetting in config and access it in app.](https://go.microsoft.com/fwlink/?LinkID=699562)
17 | * [Manage User Secrets using Secret Manager.](https://go.microsoft.com/fwlink/?LinkId=699315)
18 | * [Use logging to log a message.](https://go.microsoft.com/fwlink/?LinkId=699316)
19 | * [Add packages using NuGet.](https://go.microsoft.com/fwlink/?LinkId=699317)
20 | * [Add client packages using Bower.](https://go.microsoft.com/fwlink/?LinkId=699318)
21 | * [Target development, staging or production environment.](https://go.microsoft.com/fwlink/?LinkId=699319)
22 |
23 | ## Overview
24 |
25 | * [Conceptual overview of what is ASP.NET Core](https://go.microsoft.com/fwlink/?LinkId=518008)
26 | * [Fundamentals of ASP.NET Core such as Startup and middleware.](https://go.microsoft.com/fwlink/?LinkId=699320)
27 | * [Working with Data](https://go.microsoft.com/fwlink/?LinkId=398602)
28 | * [Security](https://go.microsoft.com/fwlink/?LinkId=398603)
29 | * [Client side development](https://go.microsoft.com/fwlink/?LinkID=699321)
30 | * [Develop on different platforms](https://go.microsoft.com/fwlink/?LinkID=699322)
31 | * [Read more on the documentation site](https://go.microsoft.com/fwlink/?LinkID=699323)
32 |
33 | ## Run & Deploy
34 |
35 | * [Run your app](https://go.microsoft.com/fwlink/?LinkID=517851)
36 | * [Run tools such as EF migrations and more](https://go.microsoft.com/fwlink/?LinkID=517853)
37 | * [Publish to Microsoft Azure Web Apps](https://go.microsoft.com/fwlink/?LinkID=398609)
38 |
39 | We would love to hear your [feedback](https://go.microsoft.com/fwlink/?LinkId=518015)
40 |
--------------------------------------------------------------------------------
/server/CleanCo.Ecomm.RestAdapter/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Builder;
6 | using Microsoft.AspNetCore.Hosting;
7 | using Microsoft.Extensions.Configuration;
8 | using Microsoft.Extensions.DependencyInjection;
9 | using Microsoft.Extensions.Logging;
10 |
11 | namespace Clean.Adapters.Rest
12 | {
13 | public class Startup
14 | {
15 | public Startup(IHostingEnvironment env)
16 | {
17 | var builder = new ConfigurationBuilder()
18 | .SetBasePath(env.ContentRootPath)
19 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
20 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
21 | .AddEnvironmentVariables();
22 | Configuration = builder.Build();
23 | }
24 |
25 | public IConfigurationRoot Configuration { get; }
26 |
27 | // This method gets called by the runtime. Use this method to add services to the container.
28 | public void ConfigureServices(IServiceCollection services)
29 | {
30 | // Add framework services.
31 | services.AddCors();
32 | services.AddMvc();
33 | }
34 |
35 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
36 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
37 | {
38 | loggerFactory.AddConsole(Configuration.GetSection("Logging"));
39 | loggerFactory.AddDebug();
40 |
41 | app.UseCors(builder =>
42 | builder.WithOrigins("http://localhost:3000")
43 | .AllowAnyHeader());
44 | app.UseMvc();
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/server/CleanCo.Ecomm.RestAdapter/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "IncludeScopes": false,
4 | "LogLevel": {
5 | "Default": "Debug",
6 | "System": "Information",
7 | "Microsoft": "Information"
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/server/CleanCo.Ecomm.RestAdapter/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "Microsoft.NETCore.App": {
4 | "version": "1.0.0",
5 | "type": "platform"
6 | },
7 | "Microsoft.AspNetCore.Mvc": "1.0.1",
8 | "Microsoft.AspNetCore.Routing": "1.0.1",
9 | "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
10 | "Microsoft.AspNetCore.Server.Kestrel": "1.0.1",
11 | "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
12 | "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0",
13 | "Microsoft.Extensions.Configuration.Json": "1.0.0",
14 | "Microsoft.Extensions.Configuration.CommandLine": "1.0.0",
15 | "Microsoft.Extensions.Logging": "1.0.0",
16 | "Microsoft.Extensions.Logging.Console": "1.0.0",
17 | "Microsoft.Extensions.Logging.Debug": "1.0.0",
18 | "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
19 | "CleanCo.Ecomm.RepoAdapter": {
20 | "target": "project"
21 | },
22 | "CleanCo.Common": {
23 | "target": "project"
24 | },
25 | "CleanCo.Products": {
26 | "target": "project"
27 | },
28 | "CleanCo.Ecomm.Catalog": {
29 | "target": "project"
30 | }
31 | },
32 |
33 | "tools": {
34 | "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final"
35 | },
36 |
37 | "frameworks": {
38 | "netcoreapp1.0": {
39 | "imports": [
40 | "dotnet5.6",
41 | "portable-net45+win8"
42 | ]
43 | }
44 | },
45 |
46 | "buildOptions": {
47 | "emitEntryPoint": true,
48 | "preserveCompilationContext": true
49 | },
50 |
51 | "runtimeOptions": {
52 | "configProperties": {
53 | "System.GC.Server": true
54 | }
55 | },
56 |
57 | "publishOptions": {
58 | "include": [
59 | "wwwroot",
60 | "Views",
61 | "Areas/**/Views",
62 | "appsettings.json",
63 | "web.config"
64 | ]
65 | },
66 |
67 | "scripts": {
68 | "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
69 | },
70 |
71 | "tooling": {
72 | "defaultNamespace": "Clean.Adapters.Rest"
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/server/CleanCo.Ecomm.RestAdapter/web.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/server/CleanCo.JsonAdapter/JsonHelper.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 |
3 | namespace CleanCo.JsonAdapter
4 | {
5 | public static class JsonHelper
6 | {
7 | public static string ToJson(T obj)
8 | {
9 | return JsonConvert.SerializeObject(obj);
10 | }
11 |
12 | public static T FromJson(string json)
13 | {
14 | return JsonConvert.DeserializeObject(json);
15 | }
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/server/CleanCo.JsonAdapter/bin/Debug/netstandard1.6/Clean.Adapters.Json.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dev-mastery/clean-architecture/8da65aafa1c41272e7d8fb46b77558dbc9bf5d00/server/CleanCo.JsonAdapter/bin/Debug/netstandard1.6/Clean.Adapters.Json.dll
--------------------------------------------------------------------------------
/server/CleanCo.JsonAdapter/bin/Debug/netstandard1.6/Clean.Adapters.Json.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dev-mastery/clean-architecture/8da65aafa1c41272e7d8fb46b77558dbc9bf5d00/server/CleanCo.JsonAdapter/bin/Debug/netstandard1.6/Clean.Adapters.Json.pdb
--------------------------------------------------------------------------------
/server/CleanCo.JsonAdapter/bin/Debug/netstandard1.6/CleanCo.JsonAdapter.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dev-mastery/clean-architecture/8da65aafa1c41272e7d8fb46b77558dbc9bf5d00/server/CleanCo.JsonAdapter/bin/Debug/netstandard1.6/CleanCo.JsonAdapter.dll
--------------------------------------------------------------------------------
/server/CleanCo.JsonAdapter/bin/Debug/netstandard1.6/CleanCo.JsonAdapter.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dev-mastery/clean-architecture/8da65aafa1c41272e7d8fb46b77558dbc9bf5d00/server/CleanCo.JsonAdapter/bin/Debug/netstandard1.6/CleanCo.JsonAdapter.pdb
--------------------------------------------------------------------------------
/server/CleanCo.JsonAdapter/obj/Debug/netstandard1.6/.IncrementalCache:
--------------------------------------------------------------------------------
1 | {"inputs":["/Users/bill/code/clean-architecture/server/CleanCo.JsonAdapter/project.json","/Users/bill/code/clean-architecture/server/CleanCo.JsonAdapter/project.lock.json","/Users/bill/code/clean-architecture/server/CleanCo.JsonAdapter/JsonHelper.cs"],"outputs":["/Users/bill/code/clean-architecture/server/CleanCo.JsonAdapter/bin/Debug/netstandard1.6/CleanCo.JsonAdapter.dll","/Users/bill/code/clean-architecture/server/CleanCo.JsonAdapter/bin/Debug/netstandard1.6/CleanCo.JsonAdapter.pdb"],"buildArguments":{"version-suffix":null}}
--------------------------------------------------------------------------------
/server/CleanCo.JsonAdapter/obj/Debug/netstandard1.6/.SDKVersion:
--------------------------------------------------------------------------------
1 | 635cf40e58ede8a53e8b9555e19a6e1ccd6f9fbe
2 | 1.0.0-preview2-003131
3 |
4 | osx.10.12-x64
--------------------------------------------------------------------------------
/server/CleanCo.JsonAdapter/obj/Debug/netstandard1.6/dotnet-compile.assemblyinfo.cs:
--------------------------------------------------------------------------------
1 | // This file has been auto generated.
2 | [assembly:System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
3 | [assembly:System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
4 | [assembly:System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
5 | [assembly:System.Runtime.Versioning.TargetFrameworkAttribute(".NETStandard,Version=v1.6")]
--------------------------------------------------------------------------------
/server/CleanCo.JsonAdapter/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0-*",
3 |
4 | "dependencies": {
5 | "NETStandard.Library": "1.6.0",
6 | "Newtonsoft.Json": "9.0.1"
7 | },
8 |
9 | "frameworks": {
10 | "netstandard1.6": {
11 | "imports": "dnxcore50"
12 | }
13 | },
14 |
15 | "tooling": {
16 | "defaultNamespace": "CleanCo.JsonAdapter"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/server/CleanCo.Products/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 |
84 | # Visual Studio profiler
85 | *.psess
86 | *.vsp
87 | *.vspx
88 | *.sap
89 |
90 | # TFS 2012 Local Workspace
91 | $tf/
92 |
93 | # Guidance Automation Toolkit
94 | *.gpState
95 |
96 | # ReSharper is a .NET coding add-in
97 | _ReSharper*/
98 | *.[Rr]e[Ss]harper
99 | *.DotSettings.user
100 |
101 | # JustCode is a .NET coding add-in
102 | .JustCode
103 |
104 | # TeamCity is a build add-in
105 | _TeamCity*
106 |
107 | # DotCover is a Code Coverage Tool
108 | *.dotCover
109 |
110 | # NCrunch
111 | _NCrunch_*
112 | .*crunch*.local.xml
113 | nCrunchTemp_*
114 |
115 | # MightyMoose
116 | *.mm.*
117 | AutoTest.Net/
118 |
119 | # Web workbench (sass)
120 | .sass-cache/
121 |
122 | # Installshield output folder
123 | [Ee]xpress/
124 |
125 | # DocProject is a documentation generator add-in
126 | DocProject/buildhelp/
127 | DocProject/Help/*.HxT
128 | DocProject/Help/*.HxC
129 | DocProject/Help/*.hhc
130 | DocProject/Help/*.hhk
131 | DocProject/Help/*.hhp
132 | DocProject/Help/Html2
133 | DocProject/Help/html
134 |
135 | # Click-Once directory
136 | publish/
137 |
138 | # Publish Web Output
139 | *.[Pp]ublish.xml
140 | *.azurePubxml
141 | # TODO: Comment the next line if you want to checkin your web deploy settings
142 | # but database connection strings (with potential passwords) will be unencrypted
143 | *.pubxml
144 | *.publishproj
145 |
146 | # NuGet Packages
147 | *.nupkg
148 | # The packages folder can be ignored because of Package Restore
149 | **/packages/*
150 | # except build/, which is used as an MSBuild target.
151 | !**/packages/build/
152 | # Uncomment if necessary however generally it will be regenerated when needed
153 | #!**/packages/repositories.config
154 |
155 | # Microsoft Azure Build Output
156 | csx/
157 | *.build.csdef
158 |
159 | # Microsoft Azure Emulator
160 | ecf/
161 | rcf/
162 |
163 | # Microsoft Azure ApplicationInsights config file
164 | ApplicationInsights.config
165 |
166 | # Windows Store app package directory
167 | AppPackages/
168 | BundleArtifacts/
169 |
170 | # Visual Studio cache files
171 | # files ending in .cache can be ignored
172 | *.[Cc]ache
173 | # but keep track of directories ending in .cache
174 | !*.[Cc]ache/
175 |
176 | # Others
177 | ClientBin/
178 | ~$*
179 | *~
180 | *.dbmdl
181 | *.dbproj.schemaview
182 | *.pfx
183 | *.publishsettings
184 | node_modules/
185 | orleans.codegen.cs
186 |
187 | # RIA/Silverlight projects
188 | Generated_Code/
189 |
190 | # Backup & report files from converting an old project file
191 | # to a newer Visual Studio version. Backup files are not needed,
192 | # because we have git ;-)
193 | _UpgradeReport_Files/
194 | Backup*/
195 | UpgradeLog*.XML
196 | UpgradeLog*.htm
197 |
198 | # SQL Server files
199 | *.mdf
200 | *.ldf
201 |
202 | # Business Intelligence projects
203 | *.rdl.data
204 | *.bim.layout
205 | *.bim_*.settings
206 |
207 | # Microsoft Fakes
208 | FakesAssemblies/
209 |
210 | # GhostDoc plugin setting file
211 | *.GhostDoc.xml
212 |
213 | # Node.js Tools for Visual Studio
214 | .ntvs_analysis.dat
215 |
216 | # Visual Studio 6 build log
217 | *.plg
218 |
219 | # Visual Studio 6 workspace options file
220 | *.opt
221 |
222 | # Visual Studio LightSwitch build output
223 | **/*.HTMLClient/GeneratedArtifacts
224 | **/*.DesktopClient/GeneratedArtifacts
225 | **/*.DesktopClient/ModelManifest.xml
226 | **/*.Server/GeneratedArtifacts
227 | **/*.Server/ModelManifest.xml
228 | _Pvt_Extensions
229 |
230 | # Paket dependency manager
231 | .paket/paket.exe
232 |
233 | # FAKE - F# Make
234 | .fake/
235 |
--------------------------------------------------------------------------------
/server/CleanCo.Products/Product.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using CleanCo.Common;
4 |
5 | namespace CleanCo.Products
6 | {
7 | public class Product : IRecord
8 | {
9 | private Guid id;
10 | private decimal price;
11 |
12 | public Product(string name, Guid id = new Guid())
13 | {
14 | validateName(name);
15 |
16 | this.Name = name.Trim();
17 | this.Id = (id == Guid.Empty) ? Guid.NewGuid() : id;
18 |
19 | }
20 |
21 | private void validateName(string name)
22 | {
23 | if(name == null)
24 | {
25 | throw new ArgumentNullException("name can not be null");
26 | }
27 |
28 | if(String.IsNullOrEmpty(name.Trim()))
29 | {
30 | throw new ArgumentException("name can not be empty");
31 | }
32 | }
33 |
34 | private void validatePrice(decimal price)
35 | {
36 | if(price < 0.00m)
37 | {
38 | throw new ArgumentOutOfRangeException("price must be greater than 0");
39 | }
40 | }
41 |
42 | private void validateId(Guid id)
43 | {
44 | if(id == Guid.Empty)
45 | {
46 | throw new ArgumentException("id can not be an empty Guid");
47 | }
48 | }
49 |
50 | public Guid Id
51 | {
52 | get { return id; }
53 | set
54 | {
55 | validateId(value);
56 | id = value;
57 | }
58 | }
59 |
60 | public string Name { get; }
61 |
62 | public decimal Price
63 | {
64 | get
65 | {
66 | return price;
67 | }
68 | set
69 | {
70 | validatePrice(value);
71 | price = value;
72 | }
73 | }
74 | public string Description { get; set; }
75 | public Uri PrimaryImage { get; set; }
76 | public List AdditionalImages { get; set; }
77 | public Dictionary> Attributes { get; set; }
78 | }
79 | }
--------------------------------------------------------------------------------
/server/CleanCo.Products/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0-*",
3 |
4 | "dependencies": {
5 | "NETStandard.Library": "1.6.0",
6 | "CleanCo.Common": {
7 | "target": "project"
8 | }
9 | },
10 |
11 | "frameworks": {
12 | "netstandard1.6": {
13 | "imports": "dnxcore50"
14 | }
15 | },
16 |
17 | "tooling": {
18 | "defaultNamespace": "CleanCo.Products"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/server/CleanCo.Tests/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 |
84 | # Visual Studio profiler
85 | *.psess
86 | *.vsp
87 | *.vspx
88 | *.sap
89 |
90 | # TFS 2012 Local Workspace
91 | $tf/
92 |
93 | # Guidance Automation Toolkit
94 | *.gpState
95 |
96 | # ReSharper is a .NET coding add-in
97 | _ReSharper*/
98 | *.[Rr]e[Ss]harper
99 | *.DotSettings.user
100 |
101 | # JustCode is a .NET coding add-in
102 | .JustCode
103 |
104 | # TeamCity is a build add-in
105 | _TeamCity*
106 |
107 | # DotCover is a Code Coverage Tool
108 | *.dotCover
109 |
110 | # NCrunch
111 | _NCrunch_*
112 | .*crunch*.local.xml
113 | nCrunchTemp_*
114 |
115 | # MightyMoose
116 | *.mm.*
117 | AutoTest.Net/
118 |
119 | # Web workbench (sass)
120 | .sass-cache/
121 |
122 | # Installshield output folder
123 | [Ee]xpress/
124 |
125 | # DocProject is a documentation generator add-in
126 | DocProject/buildhelp/
127 | DocProject/Help/*.HxT
128 | DocProject/Help/*.HxC
129 | DocProject/Help/*.hhc
130 | DocProject/Help/*.hhk
131 | DocProject/Help/*.hhp
132 | DocProject/Help/Html2
133 | DocProject/Help/html
134 |
135 | # Click-Once directory
136 | publish/
137 |
138 | # Publish Web Output
139 | *.[Pp]ublish.xml
140 | *.azurePubxml
141 | # TODO: Comment the next line if you want to checkin your web deploy settings
142 | # but database connection strings (with potential passwords) will be unencrypted
143 | *.pubxml
144 | *.publishproj
145 |
146 | # NuGet Packages
147 | *.nupkg
148 | # The packages folder can be ignored because of Package Restore
149 | **/packages/*
150 | # except build/, which is used as an MSBuild target.
151 | !**/packages/build/
152 | # Uncomment if necessary however generally it will be regenerated when needed
153 | #!**/packages/repositories.config
154 |
155 | # Microsoft Azure Build Output
156 | csx/
157 | *.build.csdef
158 |
159 | # Microsoft Azure Emulator
160 | ecf/
161 | rcf/
162 |
163 | # Microsoft Azure ApplicationInsights config file
164 | ApplicationInsights.config
165 |
166 | # Windows Store app package directory
167 | AppPackages/
168 | BundleArtifacts/
169 |
170 | # Visual Studio cache files
171 | # files ending in .cache can be ignored
172 | *.[Cc]ache
173 | # but keep track of directories ending in .cache
174 | !*.[Cc]ache/
175 |
176 | # Others
177 | ClientBin/
178 | ~$*
179 | *~
180 | *.dbmdl
181 | *.dbproj.schemaview
182 | *.pfx
183 | *.publishsettings
184 | node_modules/
185 | orleans.codegen.cs
186 |
187 | # RIA/Silverlight projects
188 | Generated_Code/
189 |
190 | # Backup & report files from converting an old project file
191 | # to a newer Visual Studio version. Backup files are not needed,
192 | # because we have git ;-)
193 | _UpgradeReport_Files/
194 | Backup*/
195 | UpgradeLog*.XML
196 | UpgradeLog*.htm
197 |
198 | # SQL Server files
199 | *.mdf
200 | *.ldf
201 |
202 | # Business Intelligence projects
203 | *.rdl.data
204 | *.bim.layout
205 | *.bim_*.settings
206 |
207 | # Microsoft Fakes
208 | FakesAssemblies/
209 |
210 | # GhostDoc plugin setting file
211 | *.GhostDoc.xml
212 |
213 | # Node.js Tools for Visual Studio
214 | .ntvs_analysis.dat
215 |
216 | # Visual Studio 6 build log
217 | *.plg
218 |
219 | # Visual Studio 6 workspace options file
220 | *.opt
221 |
222 | # Visual Studio LightSwitch build output
223 | **/*.HTMLClient/GeneratedArtifacts
224 | **/*.DesktopClient/GeneratedArtifacts
225 | **/*.DesktopClient/ModelManifest.xml
226 | **/*.Server/GeneratedArtifacts
227 | **/*.Server/ModelManifest.xml
228 | _Pvt_Extensions
229 |
230 | # Paket dependency manager
231 | .paket/paket.exe
232 |
233 | # FAKE - F# Make
234 | .fake/
235 |
--------------------------------------------------------------------------------
/server/CleanCo.Tests/AdapterTests/RepoTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Linq;
4 | using System.Collections.Generic;
5 | using Xunit;
6 |
7 | using CleanCo.RepoAdapter;
8 | using CleanCo.Common;
9 |
10 | namespace CleanCo.Tests
11 | {
12 | // see example explanation on xUnit.net website:
13 | // https://xunit.github.io/docs/getting-started-dotnet-core.html
14 | public class RepoTests
15 | {
16 | private IRepository repo = new Repository();
17 |
18 | [Fact]
19 | public void Should_Save_A_New_Entity()
20 | {
21 | DummyRecord savedRecord;
22 | DummyRecord dummyRecord = new DummyRecord("save me");
23 |
24 | repo.Save(dummyRecord);
25 | savedRecord = repo.RetrieveById(dummyRecord.Id);
26 |
27 | Assert.NotNull(savedRecord);
28 | Assert.IsType(dummyRecord.GetType(), savedRecord);
29 | Assert.Equal(dummyRecord.Id, savedRecord.Id);
30 | Assert.Equal(dummyRecord.Name, savedRecord.Name);
31 |
32 | repo.Delete(dummyRecord.Id);
33 | }
34 |
35 | [Fact]
36 | public void Should_Delete_An_Entity()
37 | {
38 | DummyRecord dummyRecord = new DummyRecord("delete me");
39 | DummyRecord deletedRecord;
40 |
41 | repo.Save(dummyRecord);
42 | repo.Delete(dummyRecord.Id);
43 | deletedRecord = repo.RetrieveById(dummyRecord.Id);
44 |
45 | Assert.Null(deletedRecord);
46 | }
47 |
48 | [Fact]
49 | public void Should_Return_A_List_Of_Entities()
50 | {
51 | List dummyRecords;
52 | DummyRecord one = new DummyRecord("One");
53 | DummyRecord two = new DummyRecord("Two");
54 | DummyRecord three = new DummyRecord("Three");
55 |
56 | List savedRecords;
57 | DummyRecord savedRecordOne;
58 | DummyRecord savedRecordTwo;
59 | DummyRecord savedRecordThree;
60 |
61 | dummyRecords = new List();
62 | dummyRecords.Add(one);
63 | dummyRecords.Add(two);
64 | dummyRecords.Add(three);
65 | dummyRecords.ForEach((dummyRecord) => repo.Save(dummyRecord));
66 |
67 | Assert.Equal(repo.RecordCount, dummyRecords.Count);
68 |
69 | savedRecords = repo.RetrieveAll();
70 | savedRecordOne = savedRecords.Find((savedRecord) => savedRecord.Id == one.Id);
71 | savedRecordTwo = savedRecords.Find((savedRecord) => savedRecord.Id == two.Id);
72 | savedRecordThree = savedRecords.Find((savedRecord) => savedRecord.Id == three.Id);
73 |
74 | Assert.Equal(savedRecordOne.Name, one.Name);
75 | Assert.Equal(savedRecordTwo.Name, two.Name);
76 | Assert.Equal(savedRecordThree.Name, three.Name);
77 |
78 | dummyRecords.ForEach((entity) => repo.Delete(entity.Id));
79 | }
80 |
81 | [Fact]
82 | public void Should_Return_A_Single_Entity()
83 | {
84 | DummyRecord one = new DummyRecord("One");
85 | repo.Save(one);
86 | DummyRecord saved = repo.RetrieveById(one.Id);
87 | Assert.Equal(saved.Id, one.Id);
88 | Assert.Equal(saved.Name, one.Name);
89 | repo.Delete(one.Id);
90 | }
91 |
92 | [Fact]
93 | public void Should_Return_An_Empty_List()
94 | {
95 | List noRecords = repo.RetrieveAll();
96 | Assert.NotNull(noRecords);
97 | Assert.Empty(noRecords);
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/server/CleanCo.Tests/EntityTests/ProductTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xunit;
3 | using CleanCo.Products;
4 |
5 | namespace CleanCo.Tests
6 | {
7 | // see example explanation on xUnit.net website:
8 | // https://xunit.github.io/docs/getting-started-dotnet-core.html
9 | public class ProductTest
10 | {
11 |
12 | [Fact]
13 | public void Should_Automatically_Set_A_Valid_Id()
14 | {
15 | Product testProduct = new Product("Soap");
16 |
17 | Assert.NotNull(testProduct.Id);
18 | Assert.True(testProduct.Id != Guid.Empty);
19 | }
20 |
21 | [Fact]
22 | public void Should_Manually_Set_A_Valid_Id()
23 | {
24 | Guid id = Guid.NewGuid();
25 | Product testProduct = new Product("Soap", id);
26 |
27 | Assert.Equal(id, testProduct.Id);
28 | }
29 |
30 | [Fact]
31 | public void Should_Throw_On_Setting_An_Invalid_Id()
32 | {
33 | Product testProduct = new Product("Soap");
34 | Assert.Throws(() => testProduct.Id = Guid.Empty);
35 | }
36 |
37 | [Theory,
38 | InlineData("good"),
39 | InlineData("still good"),
40 | InlineData(" also good ")]
41 | public void Should_Construct_A_Valid_Product(string name)
42 | {
43 | Product testProduct = new Product(name);
44 |
45 | Assert.Equal(testProduct.Name, name.Trim());
46 | }
47 |
48 | [Theory,
49 | InlineData(""),
50 | InlineData(" ")]
51 | public void Should_Throw_On_Constructing_With_An_Empty_Name(string invalidName)
52 | {
53 | Assert.Throws(() => new Product(invalidName));
54 | }
55 |
56 | [Fact]
57 | public void Should_Throw_On_Constructing_With_A_Null_Name()
58 | {
59 | string nullName = null;
60 | Assert.Throws(() => new Product(nullName));
61 | }
62 |
63 | [Theory,
64 | InlineData(-1.99),
65 | InlineData(-10),
66 | InlineData(1-2)]
67 | public void Should_Throw_Setting_A_Negative_Price(decimal invalidPrice)
68 | {
69 | Product testProduct = new Product("Soap");
70 | Assert.Throws(() => testProduct.Price = invalidPrice);
71 | }
72 |
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/server/CleanCo.Tests/TestDoubles/DummyRecord.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.RegularExpressions;
3 | using CleanCo.Common;
4 |
5 | namespace CleanCo.Tests
6 | {
7 | // An IRecord implementation we can use for testing.
8 | public class DummyRecord: IRecord
9 | {
10 | public DummyRecord(string name) {
11 | this.Id = Guid.NewGuid();
12 | this.Name = name;
13 | }
14 | public Guid Id { get; set; }
15 | public string Name { get; set; }
16 | }
17 | }
--------------------------------------------------------------------------------
/server/CleanCo.Tests/TestDoubles/FakeRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using CleanCo.Common;
5 |
6 | namespace CleanCo.Tests
7 | {
8 | public class FakeRepository: IRepository where T: IRecord
9 | {
10 | private static IDictionary store;
11 |
12 | static FakeRepository()
13 | {
14 | store = new Dictionary();
15 | }
16 |
17 | public void Delete(Guid id)
18 | {
19 | store.Remove(id);
20 | }
21 | public List Find(string filter)
22 | {
23 | throw new NotImplementedException("Test me first");
24 | }
25 | public List RetrieveAll()
26 | {
27 | List list = new List();
28 | foreach (T item in store.Values)
29 | {
30 | list.Add(item);
31 | }
32 | return list;
33 | }
34 | public T RetrieveById(Guid id)
35 | {
36 | if(store.ContainsKey(id))
37 | {
38 | return store[id];
39 | }
40 | else
41 | {
42 | return default(T);
43 | }
44 | }
45 | public List RetrieveNext(Guid last, int max)
46 | {
47 | throw new NotImplementedException("Test me first");
48 | }
49 | public void Save(T record)
50 | {
51 | if(store.ContainsKey(record.Id))
52 | {
53 | store[record.Id] = record;
54 | }
55 | else
56 | {
57 | store.Add(record.Id, record);
58 | }
59 | }
60 | public int RecordCount
61 | {
62 | get
63 | {
64 | return store.Count;
65 | }
66 | }
67 | }
68 | }
--------------------------------------------------------------------------------
/server/CleanCo.Tests/UseCaseTests/CatalogTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xunit;
3 | using CleanCo.Ecomm;
4 | using CleanCo.Products;
5 |
6 | namespace CleanCo.Tests
7 | {
8 | public class CatalogTests
9 | {
10 | private Catalog testCatalog = new Catalog(new FakeRepository());
11 | [Fact]
12 | public void Should_Disallow_Products_With_Same_Name()
13 | {
14 | Product productA = new Product("same");
15 | Product productB = new Product("same");
16 | testCatalog.AddProduct(productA);
17 | Assert.Throws(() => testCatalog.AddProduct(productB));
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/server/CleanCo.Tests/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0-*",
3 | "testRunner": "xunit",
4 | "dependencies": {
5 | "dotnet-test-xunit": "2.2.0-preview2-build1029",
6 | "xunit": "2.2.0-beta2-build3300",
7 | "CleanCo.Common": {
8 | "target": "project"
9 | },
10 | "CleanCo.Products": {
11 | "target": "project"
12 | },
13 | "CleanCo.Ecomm.RepoAdapter": {
14 | "target": "project"
15 | },
16 | "CleanCo.Ecomm.Catalog": {
17 | "target": "project"
18 | }
19 | },
20 | "frameworks": {
21 | "netcoreapp1.0": {
22 | "dependencies": {
23 | "Microsoft.NETCore.App": {
24 | "type": "platform",
25 | "version": "1.0.0"
26 | }
27 | }
28 | }
29 | },
30 | "buildOptions": {
31 | "copyToOutput": {
32 | "include": [ "xunit.runner.json" ]
33 | }
34 | },
35 | "tooling": {
36 | "defaultNamespace": "Clean.Tests"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/server/CleanCo.Tests/xunit.runner.json:
--------------------------------------------------------------------------------
1 | {
2 | "diagnosticMessages": false,
3 | "methodDisplay": "classAndMethod",
4 | "parallelizeTestCollections": true
5 | }
6 |
--------------------------------------------------------------------------------
/server/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "projects": [
3 | "CleanCo.JsonAdapter",
4 | "CleanCo.Ecomm.RepoAdapters",
5 | "CleanCo.Ecomm.RestAdapter",
6 | "CleanCo.Common",
7 | "CleanCo.Products",
8 | "CleanCo.Tests",
9 | "CleanCo.Ecomm.Catalog"
10 | ]
11 | }
--------------------------------------------------------------------------------