Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ Branches
- index.js
- App.js - component showing ItemList
- ItemList.js - component with an inner template ItemListTemplate producing an html fragment
- ItemService.js, ItemWsClient.js, ItemRestClient.js
- itemService.js, ItemWsClient.js, ItemRestClient.js
- App and ItemList defined as components having a simple onInit - onDestroy lifecycle
- ItemList shows a loading text before items are fetched
- discuss component state changes
Expand All @@ -127,7 +127,7 @@ Branches
- index.js
- App.js - component showing ItemEdit and ItemList
- ItemList.js, ItemEdit.js
- ItemService.js, ItemWsClient.js, ItemRestClient.js
- itemService.js, ItemWsClient.js, ItemRestClient.js
- create and update items handled by ItemEdit; delete item handled by ItemList
- discuss event handling

Expand All @@ -138,7 +138,7 @@ Branches
- index.js
- App.js - component showing ItemEdit and ItemList
- ItemList.js, ItemEdit.js
- ItemService.js, ItemWsClient.js, ItemRestClient.js
- itemService.js, ItemWsClient.js, ItemRestClient.js
- add a render method to components lifecycle
- discuss component properties and state

Expand All @@ -149,7 +149,7 @@ Branches
- index.js
- App.js - component showing ItemEdit and ItemList
- ItemList.js, ItemEdit.js
- Store.js, Provider.js, ItemService.js, ItemWsClient.js, ItemRestClient.js
- Store.js, Provider.js, itemService.js, ItemWsClient.js, ItemRestClient.js
- implement a simple redux Store
- refactor ItemService in terms of actions and reducers
- write a simple Provider and provide the store to the entire App
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"koa": "^2.5.2",
"koa-bodyparser": "^4.2.1",
"koa-router": "^7.4.0",
"node-fetch": "^2.1.2"
"node-fetch": "^2.1.2",
"ws": "^5.2.2"
},
"devDependencies": {
"babel-plugin-transform-object-rest-spread": "^6.26.0",
Expand Down
4 changes: 2 additions & 2 deletions src/client/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ItemRestClient } from './ItemRestClient';
import itemService from './itemService';
import itemCli from './itemCli';

itemCli(new ItemRestClient());
itemCli();
10 changes: 6 additions & 4 deletions src/client/itemCli.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import readline from 'readline';
import Item from '../shared/Item';
import { createItem, getItems, removeItem, subscribe } from './itemService';

const cli = (() => {
const commandMap = {};
Expand Down Expand Up @@ -36,19 +37,20 @@ const cli = (() => {
}
})();

const itemCli = itemRestClient => {
const itemCli = () => {
subscribe(items => console.log('items updated', items));
cli.command('show', 'Show items', async () => {
console.log(await itemRestClient.search({}));
console.log(await getItems({}));
});
cli.command('add', 'Add item', async (args) => {
try {
console.log(await itemRestClient.create(new Item(args, true)));
await createItem(new Item(args, true));
} catch(error) {
console.log(error.issues);
}
});
cli.command('remove', 'Remove item by id', async (args) => {
console.log(await itemRestClient.remove(parseInt(args)));
await removeItem(parseInt(args));
});
cli.setErrorHandler(error => console.log(error));
cli.start();
Expand Down
55 changes: 55 additions & 0 deletions src/client/itemService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { ItemRestClient } from './ItemRestClient';

let cachedItems = null;

const subscribers = [];

export const subscribe = listener => subscribers.push(listener);

const itemRestClient = new ItemRestClient();

export const createItem = async (item) => {
const createdItem = await itemRestClient.create(item);
onCreatedItem(item);
};

export const onCreatedItem = item => {
const index = cachedItems.findIndex(item => createdItem.id === item.id);
if (index === -1) {
cachedItems.push(createdItem);
notifyUpdates();
}
};

export const getItems = async () => {
if (cachedItems) {
return cachedItems;
}
cachedItems = await itemRestClient.search({});
notifyUpdates();
};

const notifyUpdates = () => subscribers.forEach(listener => listener(cachedItems));

export const removeItem = async (id) => {
await itemRestClient.remove(id);
onRemovedItem({ id });
};

export const onRemovedItem = ({ id }) => {
const index = cachedItems.findIndex(item => id === item.id);
if (index !== -1) {
cachedItems.splice(index, 1);
}
notifyUpdates();
};

export const onUpdatedItem = item => {
const index = cachedItems.findIndex(it => item.id === it.id);
if (index !== -1) {
cachedItems.splice(index, 1, item);
} else {
cachedItems.push(item);
}
notifyUpdates();
};
11 changes: 11 additions & 0 deletions src/client/itemWsClient.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import itemService from './itemService';

const ws = new WebSocket('ws://localhost:3000/');

ws.on('open', function open() {
ws.send('something');
});

ws.on('message', function incoming(data) {
console.log(data);
});
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require("regenerator-runtime/runtime");
require('./re-intro');

require('./client');
require('./server');
require('./client');
6 changes: 5 additions & 1 deletion src/server/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import http from 'http';
import Koa from 'koa';
import Router from 'koa-router';
import bodyParser from 'koa-bodyparser';
import itemRouter from './itemRouter';
import { initWss } from './itemWss';

const app = new Koa();
const server = http.createServer(app.callback());
initWss(server);

const exceptionHandler = async (ctx, next) => {
try {
Expand Down Expand Up @@ -39,4 +43,4 @@ app
.use(apiRouter.allowedMethods());

console.log('server - listening on port', 3000);
app.listen(3000);
server.listen(3000);
7 changes: 6 additions & 1 deletion src/server/itemRouter.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Router from 'koa-router';
import { ItemStore } from './ItemStore';
import { notifyClients } from './itemWss';

const itemStore = new ItemStore();

Expand All @@ -23,8 +24,10 @@ router.get('/:id', async (ctx) => { // read
});

router.post('/', async (ctx) => { // create
ctx.response.body = await itemStore.insert(ctx.request.body);
const item = await itemStore.insert(ctx.request.body);
ctx.response.body = item;
ctx.response.status = 201; // created
notifyClients({ event: 'item/added', data: item });
});

router.put('/:id', async (ctx) => { // update
Expand All @@ -40,6 +43,7 @@ router.put('/:id', async (ctx) => { // update
if (updatedCount === 1) {
response.body = item;
response.status = 200; // ok
notifyClients({ event: 'item/updated', data: item });
} else {
response.body = { issues: [{ severity: 'error', description: 'Resource no longer exists' }] };
response.status = 405; // method not allowed
Expand All @@ -51,6 +55,7 @@ router.del('/:id', async (ctx) => { // delete
const count = await itemStore.remove({ id });
ctx.response.body = {};
ctx.response.status = 204; // no content
notifyClients({ event: 'item/deleted', data: { id } });
});

export default router;
23 changes: 23 additions & 0 deletions src/server/itemWss.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import WebSocket from 'ws';

let wss = null;

export const initWss = (server) => {
wss = new WebSocket.Server({ server });
wss.on('connection', (ws, request) => {
console.log('server - wss on connection');
ws.on('message', (message) => {
console.log('server - wss received: %s', message);
ws.send('echo' + message);
});
});
};

export const notifyClients = message => {
if (!wss) {
return;
}
wss.clients.forEach(ws => {
ws.send(JSON.stringify(message));
})
};