implement simple data storage api

This commit is contained in:
Adam Brown 2016-12-02 11:20:59 -05:00
parent 822a012129
commit 9d23d48b67
22 changed files with 13783 additions and 12800 deletions

3
.gitignore vendored
View File

@ -1,8 +1,9 @@
*~ *~
node_modules node_modules
# have to add the bundle to version control so deployment with git subtree works # have to add the bundle to version control so deployment with git subtree works
# browser-bundle.js # browser-bundle.js
# skeleton.css
browser-bundle.js.map browser-bundle.js.map
npm-debug.log.*

1
data/comp-123.json Normal file
View File

@ -0,0 +1 @@
{"a":"this is before","b":"this is after","id":"123"}

View File

@ -0,0 +1 @@
{"a":"this is string 1","b":"this is string 2","id":"367e05cc-3c82-4ed4-8325-89b5fef33dfa"}

View File

@ -0,0 +1 @@
{"a":"this is another before","b":"this is another after","id":"5ba17f67-08cc-4ee3-ad29-ab040fa2d396"}

View File

@ -0,0 +1 @@
{"a":"this is another string 1","b":"this is another string 2","id":"xxxxxxxx-3c82-4ed4-8325-89b5fef33dfa"}

View File

@ -0,0 +1 @@
{"a":"this is string 1","b":"this is string 2","id":"2e1a606a-04b2-4c64-a86d-28f58007919b"}

View File

@ -0,0 +1 @@
{"a":"this is string 1","b":"this is string 2","id":"xxxxxxxx-04b2-4c64-a86d-28f58007919b"}

28710
dist/browser-bundle.js vendored

File diff suppressed because one or more lines are too long

BIN
dist/img/03-small.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

BIN
dist/img/03-tiny.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
dist/img/03-tinyer.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

3
dist/main.css vendored Normal file
View File

@ -0,0 +1,3 @@
#masthead .header {
font-size: 4em;
}

View File

@ -6,7 +6,8 @@
"scripts": { "scripts": {
"build": "ebpack --colors", "build": "ebpack --colors",
"start": "webpack --progress --colors --watch", "start": "webpack --progress --colors --watch",
"serve": "node src/server.js", "serve": "node src/server/index.js",
"webpack-stats": "webpack --profile --json > stats.json",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"author": "", "author": "",
@ -14,8 +15,11 @@
"dependencies": { "dependencies": {
"babel-preset-es2015-mod": "^6.6.0", "babel-preset-es2015-mod": "^6.6.0",
"babel-preset-es3": "^1.0.1", "babel-preset-es3": "^1.0.1",
"body-parser": "^1.15.2",
"diff": "^3.0.1", "diff": "^3.0.1",
"express": "^4.14.0", "express": "^4.14.0",
"jsonfile": "^2.4.0",
"markdown-it": "^5.1.0",
"react": "^0.14.5", "react": "^0.14.5",
"react-dom": "^0.14.5", "react-dom": "^0.14.5",
"react-redux": "^4.4.6", "react-redux": "^4.4.6",
@ -23,14 +27,16 @@
"redux": "^3.5.1", "redux": "^3.5.1",
"redux-router": "^1.0.0-beta5", "redux-router": "^1.0.0-beta5",
"reselect": "^2.5.1", "reselect": "^2.5.1",
"semantic-ui-react": "^0.61.6" "semantic-ui-react": "^0.61.6",
"uuid": "^3.0.1"
}, },
"devDependencies": { "devDependencies": {
"babel-core": "^6.3.26", "babel-core": "^6.3.26",
"babel-loader": "^6.2.0", "babel-loader": "^6.2.0",
"babel-preset-es2015": "^6.3.13", "babel-preset-es2015": "^6.3.13",
"babel-preset-es2015-native-modules": "^6.9.4",
"babel-preset-react": "^6.3.13", "babel-preset-react": "^6.3.13",
"copyfiles": "^0.2.2", "copyfiles": "^0.2.2",
"webpack": "^1.12.9" "webpack": "^2.1.0-beta.27"
} }
} }

View File

@ -10,7 +10,7 @@ import Header from './Header'
import Footer from './Footer' import Footer from './Footer'
import CompareControls from './CompareControls' import CompareControls from './CompareControls'
import Show from './Show' import ShowPlaintext from './ShowPlaintext'
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
isMarkdownFormat: Selectors.isMarkdownFormat(state), isMarkdownFormat: Selectors.isMarkdownFormat(state),
@ -42,9 +42,9 @@ class Compare extends React.Component {
<Segment> <Segment>
{ this.props.isShowDifference ? { this.props.isShowDifference ?
<Show diff={this.props.diff} isMarkdownFormat={this.props.isMarkdownFormat}>{this.props.diff}</Show>: <ShowPlaintext diff={this.props.diff} isMarkdownFormat={this.props.isMarkdownFormat}>{this.props.diff}</ShowPlaintext>:
<Show <ShowPlaintext
text={this.props.isShowOriginal? this.props.safeInput.original: this.props.safeInput.final} text={this.props.isShowOriginal? this.props.safeInput.original: this.props.safeInput.final}
isMarkdownFormat={this.props.isMarkdownFormat} isMarkdownFormat={this.props.isMarkdownFormat}
/> />

11
src/components/Footer.js Normal file
View File

@ -0,0 +1,11 @@
import React from 'react'
import {Segment} from 'semantic-ui-react'
const Footer = (props) => (
<Segment basic padded textAlign="center" as="footer">
<p><a href="http://adamarthurryan.com">Adam Brown</a> | This website is <a href="https://github.com/adamarthurryan/dubdiff">open source</a>.</p>
</Segment>
)
export default Footer

View File

@ -0,0 +1,35 @@
import React from 'react'
//use markdown-it to render markdown
//alternately use markdown to jsx
const ShowMarkdown = (props) => {
return <div>
<pre style={{whiteSpace:'pre-wrap'}}>
{props.text ?
props.text:
props.diff ?
diffToPre(props.diff) :
null
}
</pre>
</div>
}
export default ShowMarkdown
function diffToPre(diff) {
return diff.map(part => (
part.added ? <span><ins>{part.value}</ins>{ifNotNewlineSpace(part.value)}</span> :
part.removed ? <span><del>{part.value}</del>{ifNotNewlineSpace(part.value)}</span> :
<span>{part.value}{ifNotNewlineSpace(part.value)}</span>
))
}
const ifNotNewlineSpace = str => {
console.log(str)
return !str.endsWith('\n') ? ' ' : ''
}

View File

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
const Show = (props) => { const ShowPlaintext = (props) => {
return <div> return <div>
<pre style={{whiteSpace:'pre-wrap'}}> <pre style={{whiteSpace:'pre-wrap'}}>
{props.text ? {props.text ?
@ -14,7 +14,7 @@ const Show = (props) => {
</div> </div>
} }
export default Show export default ShowPlaintext
function diffToPre(diff) { function diffToPre(diff) {
return diff.map(part => ( return diff.map(part => (

View File

@ -1,16 +0,0 @@
var express = require('express')
var path = require('path')
var app = express()
app.use(express.static('dist'))
app.use('bower_components/*', express.static('bower_components'))
app.route('/*')
.get(function(req, res) {
res.sendFile(path.join(__dirname, '..', 'dist', 'index.html'));
});
app.listen(8080, function () {
console.log('Server listening on port 8080.')
})

93
src/server/comparison.js Normal file
View File

@ -0,0 +1,93 @@
const express = require('express')
const jf = require('jsonfile')
const fs = require('fs')
const uuid = require('uuid')
const router = express.Router()
router.get('/:id', showComparison)
router.post('/:id', createComparisonWithId)
router.post('/', createComparison)
//return the comparison given an id, if it exsits
function showComparison(req, res) {
const id = req.params.id
return readRecord(res, id)
}
// Creates a new comparison
function createComparison(req, res) {
//generate a new id
const id = uuid()
const {a,b} = req.body
return writeRecord(res, id, {a,b,id})
}
// Creates a new comparison
function createComparisonWithId(req, res) {
//use the id provided in the req
const id = req.params.id
const {a, b} = req.body
return writeRecord(res, id, {a, b, id})
}
//reads the record from the database
function readRecord(res, id, data) {
//generate a filename
const filename = fnData(id)
//check if that file exists
fs.exists(filename, function (exists) {
//if the file does not exist, return a 404
if (!exists) return res.status(404).send(`Data id ${id} not found.`)
//otherwise, read the file as JSON
jf.readFile(filename, function(err, data) {
if(err) { return handleError(res, err) }
//and return
return res.json(data)
})
})
}
//writes the record to the database, if it doesn't exist
function writeRecord(res, id, data) {
//look up its filename
var filename = fnData(id)
//need to test that the file does not exist
//check if that file exists
fs.exists(filename, (exists) => {
//if the file already exists, return a 405
if (exists) return res.status(405).send(`Data id ${id} is already in use.`)
//and write it to the filesystem
jf.writeFile(filename, data, (err) => (
err ?
handleError(res, err) :
//if successful, return the comparison object
res.status(201).json(data)
))
})
}
module.exports = router
function handleError(res, err) {
console.log(err);
return res.send(500, err);
}
// returns a filename for the given comparison
function fnData (id) {
return "./data/" + "id-" + id + ".json";
}

35
src/server/index.js Normal file
View File

@ -0,0 +1,35 @@
const express = require('express')
const path = require('path')
const bodyParser = require('body-parser');
const app = express()
const comparisonRouter = require('./comparison')
app.use(express.static('dist'))
app.use('bower_components/*', express.static('bower_components'))
app.use(bodyParser.json())
app.use('/api/compare', comparisonRouter);
app.route('/*')
.get(function(req, res) {
res.sendFile(path.join(__dirname, '..', '..', 'dist', 'index.html'));
});
app.listen(8080, function () {
console.log('Server listening on port 8080.')
})
//router.get('/', controller.index);

43
src/util/dubdiff.js Normal file
View File

@ -0,0 +1,43 @@
import * as JsDiff from 'diff'
export function plaintextDiff(original, final) {
let arrOriginal = plaintextSplit(original)
let arrFinal = plaintextSplit(final)
let diff = JsDiff.diffArrays(arrOriginal, arrFinal)
diff = plaintextRestoreSpaces(diff)
console.log(diffToLogString(diff))
return diff
// return JsDiff.diffWordsWithSpace(original,final)
// let diff = JsDiff.diffLines(original.replace(/ /g, '###\n'), final.replace(/ /g, '###\n'))
// console.log(diff, diff.map(({added, removed, value})=>({added, removed, value:value.replace(/###\n/g, ' ')})))
// return diff.map(({added, removed, value})=>({added, removed, value:value.replace(/###\n/g, ' ')}))
}
export function diffToLogString(diff) {
return diff.map(({added, removed, value}) => {
let sym = added ? "+" : removed ? '-' : '/'
return sym+value+sym
})
}
let plaintextSplit = text =>text.split(/[ ]|(\n)/)
function plaintextRestoreSpaces (diff) {
return diff.map(({added, removed, value}) => ({
added,
removed,
value:value.map((str, idx, arr) => (
(str!='\n' && (idx<arr.length-1)) ? str+" " : str)
).join('')
}))
}

View File

@ -13,7 +13,7 @@ module.exports = {
test: /\.js$/, test: /\.js$/,
loader: 'babel-loader', loader: 'babel-loader',
query: { query: {
presets: ['es2015', 'react'], presets: ['es2015-native-modules', 'react'],
compact: "true" compact: "true"
} }
}, },