implement simple data storage api
This commit is contained in:
parent
822a012129
commit
9d23d48b67
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,8 +1,9 @@
|
||||
*~
|
||||
node_modules
|
||||
|
||||
|
||||
# have to add the bundle to version control so deployment with git subtree works
|
||||
# browser-bundle.js
|
||||
# skeleton.css
|
||||
|
||||
browser-bundle.js.map
|
||||
npm-debug.log.*
|
1
data/comp-123.json
Normal file
1
data/comp-123.json
Normal file
@ -0,0 +1 @@
|
||||
{"a":"this is before","b":"this is after","id":"123"}
|
1
data/comp-367e05cc-3c82-4ed4-8325-89b5fef33dfa.json
Normal file
1
data/comp-367e05cc-3c82-4ed4-8325-89b5fef33dfa.json
Normal file
@ -0,0 +1 @@
|
||||
{"a":"this is string 1","b":"this is string 2","id":"367e05cc-3c82-4ed4-8325-89b5fef33dfa"}
|
1
data/comp-5ba17f67-08cc-4ee3-ad29-ab040fa2d396.json
Normal file
1
data/comp-5ba17f67-08cc-4ee3-ad29-ab040fa2d396.json
Normal file
@ -0,0 +1 @@
|
||||
{"a":"this is another before","b":"this is another after","id":"5ba17f67-08cc-4ee3-ad29-ab040fa2d396"}
|
1
data/comp-xxxxxxxx-3c82-4ed4-8325-89b5fef33dfa.json
Normal file
1
data/comp-xxxxxxxx-3c82-4ed4-8325-89b5fef33dfa.json
Normal file
@ -0,0 +1 @@
|
||||
{"a":"this is another string 1","b":"this is another string 2","id":"xxxxxxxx-3c82-4ed4-8325-89b5fef33dfa"}
|
1
data/id-2e1a606a-04b2-4c64-a86d-28f58007919b.json
Normal file
1
data/id-2e1a606a-04b2-4c64-a86d-28f58007919b.json
Normal file
@ -0,0 +1 @@
|
||||
{"a":"this is string 1","b":"this is string 2","id":"2e1a606a-04b2-4c64-a86d-28f58007919b"}
|
1
data/id-xxxxxxxx-04b2-4c64-a86d-28f58007919b.json
Normal file
1
data/id-xxxxxxxx-04b2-4c64-a86d-28f58007919b.json
Normal file
@ -0,0 +1 @@
|
||||
{"a":"this is string 1","b":"this is string 2","id":"xxxxxxxx-04b2-4c64-a86d-28f58007919b"}
|
27340
dist/browser-bundle.js
vendored
27340
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
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
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
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
3
dist/main.css
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
#masthead .header {
|
||||
font-size: 4em;
|
||||
}
|
12
package.json
12
package.json
@ -6,7 +6,8 @@
|
||||
"scripts": {
|
||||
"build": "ebpack --colors",
|
||||
"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"
|
||||
},
|
||||
"author": "",
|
||||
@ -14,8 +15,11 @@
|
||||
"dependencies": {
|
||||
"babel-preset-es2015-mod": "^6.6.0",
|
||||
"babel-preset-es3": "^1.0.1",
|
||||
"body-parser": "^1.15.2",
|
||||
"diff": "^3.0.1",
|
||||
"express": "^4.14.0",
|
||||
"jsonfile": "^2.4.0",
|
||||
"markdown-it": "^5.1.0",
|
||||
"react": "^0.14.5",
|
||||
"react-dom": "^0.14.5",
|
||||
"react-redux": "^4.4.6",
|
||||
@ -23,14 +27,16 @@
|
||||
"redux": "^3.5.1",
|
||||
"redux-router": "^1.0.0-beta5",
|
||||
"reselect": "^2.5.1",
|
||||
"semantic-ui-react": "^0.61.6"
|
||||
"semantic-ui-react": "^0.61.6",
|
||||
"uuid": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.3.26",
|
||||
"babel-loader": "^6.2.0",
|
||||
"babel-preset-es2015": "^6.3.13",
|
||||
"babel-preset-es2015-native-modules": "^6.9.4",
|
||||
"babel-preset-react": "^6.3.13",
|
||||
"copyfiles": "^0.2.2",
|
||||
"webpack": "^1.12.9"
|
||||
"webpack": "^2.1.0-beta.27"
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import Header from './Header'
|
||||
import Footer from './Footer'
|
||||
import CompareControls from './CompareControls'
|
||||
|
||||
import Show from './Show'
|
||||
import ShowPlaintext from './ShowPlaintext'
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
isMarkdownFormat: Selectors.isMarkdownFormat(state),
|
||||
@ -42,9 +42,9 @@ class Compare extends React.Component {
|
||||
<Segment>
|
||||
{ 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}
|
||||
isMarkdownFormat={this.props.isMarkdownFormat}
|
||||
/>
|
||||
|
11
src/components/Footer.js
Normal file
11
src/components/Footer.js
Normal 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
|
35
src/components/ShowMarkdown.js
Normal file
35
src/components/ShowMarkdown.js
Normal 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') ? ' ' : ''
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
|
||||
|
||||
const Show = (props) => {
|
||||
const ShowPlaintext = (props) => {
|
||||
return <div>
|
||||
<pre style={{whiteSpace:'pre-wrap'}}>
|
||||
{props.text ?
|
||||
@ -14,7 +14,7 @@ const Show = (props) => {
|
||||
</div>
|
||||
}
|
||||
|
||||
export default Show
|
||||
export default ShowPlaintext
|
||||
|
||||
function diffToPre(diff) {
|
||||
return diff.map(part => (
|
@ -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
93
src/server/comparison.js
Normal 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
35
src/server/index.js
Normal 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
43
src/util/dubdiff.js
Normal 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('')
|
||||
}))
|
||||
}
|
@ -13,7 +13,7 @@ module.exports = {
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
query: {
|
||||
presets: ['es2015', 'react'],
|
||||
presets: ['es2015-native-modules', 'react'],
|
||||
compact: "true"
|
||||
}
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user