mirror of
https://github.com/Microsoft/sql-server-samples.git
synced 2025-12-08 14:58:54 +00:00
Merge pull request #211 from JocaPC/master
Added new sample - nodejs, react, and SQL Server
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
node_modules/*
|
||||
bin/*.dll
|
||||
obj/*
|
||||
*.sln
|
||||
*.log
|
||||
@@ -0,0 +1,21 @@
|
||||
var express = require('express');
|
||||
var bodyParser = require('body-parser');
|
||||
|
||||
var app = express();
|
||||
app.use(express.static('wwwroot'));
|
||||
app.use(bodyParser.urlencoded());
|
||||
app.use('/api/comments', require('./routes/comments'));
|
||||
|
||||
// catch 404 and forward to error handler
|
||||
app.use(function (req, res, next) {
|
||||
var err = new Error('Not Found');
|
||||
err.status = 404;
|
||||
next(err);
|
||||
});
|
||||
app.set('port', process.env.PORT || 3000);
|
||||
|
||||
var server = app.listen(app.get('port'), function() {
|
||||
console.log('Express server listening on port ' + server.address().port);
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
var debug = require('debug')('nodejs_express4_rest_api');
|
||||
var app = require('../app');
|
||||
@@ -0,0 +1,85 @@
|
||||
function createConnection() {
|
||||
|
||||
var config = {
|
||||
server : "<SERVER>",
|
||||
userName: "<USERNAME>",
|
||||
password: "<PASSWORD>",
|
||||
// If you're on Azure, you will need this:
|
||||
options: { encrypt: true, database: 'CommentsDb' }
|
||||
};
|
||||
|
||||
var Connection = require('tedious').Connection;
|
||||
var connection = new Connection(config);
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
function createRequest(query, connection) {
|
||||
var Request = require('tedious').Request;
|
||||
var req =
|
||||
new Request(query,
|
||||
function (err, rowCount) {
|
||||
if (err) {
|
||||
console.trace(err);
|
||||
throw err;
|
||||
}
|
||||
connection && connection.close();
|
||||
console.log('Connection closed');
|
||||
});
|
||||
return req;
|
||||
}
|
||||
|
||||
function stream (query, connection, output, defaultContent) {
|
||||
|
||||
var request = query;
|
||||
if (typeof query == "string") {
|
||||
request = createRequest(query, connection);
|
||||
}
|
||||
|
||||
var empty = true;
|
||||
request.on('row', function (columns) {
|
||||
if(empty) {
|
||||
console.log('Response fetched from SQL Database!');
|
||||
empty = false;
|
||||
}
|
||||
output.write(columns[0].value);
|
||||
});
|
||||
|
||||
request.on('done', function (rowCount, more, rows) {
|
||||
_OnDone(empty, defaultContent, output);
|
||||
});
|
||||
|
||||
request.on('doneProc', function (rowCount, more, rows) {
|
||||
_OnDone(empty, defaultContent, output);
|
||||
});
|
||||
|
||||
executeRequest (request, connection);
|
||||
}
|
||||
|
||||
function _OnDone(empty, defaultContent, output) {
|
||||
if(empty) {
|
||||
output.write(defaultContent);
|
||||
console.log('No results from database - default content is returned.');
|
||||
}
|
||||
try {
|
||||
console.log('Closing Http Response output.');
|
||||
output.end();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
function executeRequest (request, connection) {
|
||||
connection.on('connect', function (err) {
|
||||
if (err) {
|
||||
console.trace(err);
|
||||
throw err;
|
||||
}
|
||||
connection.execSql(request);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.createConnection = createConnection;
|
||||
module.exports.createRequest = createRequest;
|
||||
module.exports.executeRequest = executeRequest;
|
||||
module.exports.stream = stream;
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "nodejs-express4-rest-api",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node ./bin/www"
|
||||
},
|
||||
"description": "nodejs-express4-rest-api",
|
||||
"author": {
|
||||
"name": "Jovan Popovic",
|
||||
"email": "jovanpop@microsoft.com"
|
||||
},
|
||||
"dependencies": {
|
||||
"body-parser": "^1.15.2",
|
||||
"debug": "^2.2.0",
|
||||
"express": "^4.14.0",
|
||||
"tedious": "^1.14.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
var express = require('express');
|
||||
var router = express.Router();
|
||||
|
||||
var db = require('../db.js');
|
||||
var TYPES = require('tedious').TYPES;
|
||||
|
||||
/* GET comments. */
|
||||
router.get('/', function (req, res) {
|
||||
db.stream("select * from comments for json path", db.createConnection(), res, '[]');
|
||||
});
|
||||
|
||||
/* GET single comment. */
|
||||
router.get('/:id', function (req, res) {
|
||||
|
||||
var conn = db.createConnection();
|
||||
|
||||
var request = db.createRequest("select * from comments where id = @id for json path, without_array_wrapper", conn);
|
||||
request.addParameter('id', TYPES.Int, req.params.id);
|
||||
db.stream(request, conn, res, '{}');
|
||||
});
|
||||
|
||||
/* POST create comment. */
|
||||
router.post('/', function (req, res) {
|
||||
|
||||
var connection = db.createConnection();
|
||||
var request = db.createRequest("insert into Comments values (@author, @text)", connection);
|
||||
|
||||
request.addParameter('author', TYPES.NVarChar, req.body.author);
|
||||
request.addParameter('text', TYPES.NVarChar, req.body.text);
|
||||
|
||||
db.executeRequest(request, connection);
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,47 @@
|
||||
--CREATE DATABASE TodoDb;
|
||||
--USE TodoDb;
|
||||
DROP TABLE IF EXISTS Todo
|
||||
DROP PROCEDURE IF EXISTS createTodo
|
||||
DROP PROCEDURE IF EXISTS updateTodo
|
||||
GO
|
||||
|
||||
CREATE TABLE Todo (
|
||||
id int IDENTITY PRIMARY KEY,
|
||||
title nvarchar(30) NOT NULL,
|
||||
description nvarchar(4000),
|
||||
completed bit,
|
||||
dueDate datetime2 default (dateadd(day, 3, getdate()))
|
||||
)
|
||||
GO
|
||||
|
||||
INSERT INTO Todo (title, description, completed, dueDate)
|
||||
VALUES
|
||||
('Install SQL Server 2016','Install RTM version of SQL Server 2016', 0, '2017-03-08'),
|
||||
('Get new samples','Go to github and download new samples', 0, '2016-03-09'),
|
||||
('Try new samples','Install new Management Studio to try samples', 0, '2016-03-12')
|
||||
|
||||
GO
|
||||
|
||||
create procedure dbo.createTodo(@todo nvarchar(max))
|
||||
as begin
|
||||
insert into Todo
|
||||
select *
|
||||
from OPENJSON(@todo)
|
||||
WITH ( title nvarchar(30), description nvarchar(4000),
|
||||
completed bit, dueDate datetime2)
|
||||
end
|
||||
GO
|
||||
|
||||
create procedure updateTodo(@id int, @todo nvarchar(max))
|
||||
as begin
|
||||
update Todo
|
||||
set title = json.title, description = json.description,
|
||||
completed = json.completed, dueDate = json.dueDate
|
||||
from OPENJSON( @todo )
|
||||
WITH( title nvarchar(30), description nvarchar(4000),
|
||||
completed bit, dueDate datetime2) AS json
|
||||
where id = @id
|
||||
end
|
||||
go
|
||||
|
||||
select * from todo for json path
|
||||
@@ -0,0 +1,59 @@
|
||||
body {
|
||||
background: #fff;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
line-height: 1.7;
|
||||
margin: 0;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #4183c4;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #f8f8f8;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 3px;
|
||||
font-family: "Bitstream Vera Sans Mono", Consolas, Courier, monospace;
|
||||
font-size: 12px;
|
||||
margin: 0 2px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
font-weight: bold;
|
||||
margin: 0 0 15px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
border-bottom: 1px solid #ddd;
|
||||
font-size: 2.5em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
border-bottom: 1px solid #eee;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
p, ul {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 30px;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>React Tutorial</title>
|
||||
<!-- Not present in the tutorial. Just for basic styling. -->
|
||||
<link rel="stylesheet" href="css/base.css" />
|
||||
<script src="https://npmcdn.com/react@15.3.0/dist/react.js"></script>
|
||||
<script src="https://npmcdn.com/react-dom@15.3.0/dist/react-dom.js"></script>
|
||||
<script src="https://npmcdn.com/babel-core@5.8.38/browser.min.js"></script>
|
||||
<script src="https://npmcdn.com/jquery@3.1.0/dist/jquery.min.js"></script>
|
||||
<script src="https://npmcdn.com/remarkable@1.6.2/dist/remarkable.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content"></div>
|
||||
<script type="text/babel" src="scripts/example.js"></script>
|
||||
<script type="text/babel">
|
||||
// To get started with this tutorial running your own code, simply remove
|
||||
// the script tag loading scripts/example.js and start writing code here.
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* This file provided by Facebook is for non-commercial testing and evaluation
|
||||
* purposes only. Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
var Comment = React.createClass({
|
||||
rawMarkup: function() {
|
||||
var md = new Remarkable();
|
||||
var rawMarkup = md.render(this.props.children.toString());
|
||||
return { __html: rawMarkup };
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="comment">
|
||||
<h2 className="commentAuthor">
|
||||
{this.props.author}
|
||||
</h2>
|
||||
<span dangerouslySetInnerHTML={this.rawMarkup()} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var CommentBox = React.createClass({
|
||||
loadCommentsFromServer: function() {
|
||||
$.ajax({
|
||||
url: this.props.url,
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
success: function(data) {
|
||||
this.setState({data: data});
|
||||
}.bind(this),
|
||||
error: function(xhr, status, err) {
|
||||
console.error(this.props.url, status, err.toString());
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
handleCommentSubmit: function(comment) {
|
||||
var comments = this.state.data;
|
||||
// Optimistically set an id on the new comment. It will be replaced by an
|
||||
// id generated by the server. In a production application you would likely
|
||||
// not use Date.now() for this and would have a more robust system in place.
|
||||
comment.id = Date.now();
|
||||
var newComments = comments.concat([comment]);
|
||||
this.setState({data: newComments});
|
||||
$.ajax({
|
||||
url: this.props.url,
|
||||
dataType: 'json',
|
||||
type: 'POST',
|
||||
data: comment,
|
||||
success: function(data) {
|
||||
this.setState({data: data});
|
||||
}.bind(this),
|
||||
error: function(xhr, status, err) {
|
||||
this.setState({data: comments});
|
||||
console.error(this.props.url, status, err.toString());
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {data: []};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
this.loadCommentsFromServer();
|
||||
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<div className="commentBox">
|
||||
<h1>Comments</h1>
|
||||
<CommentList data={this.state.data} />
|
||||
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var CommentList = React.createClass({
|
||||
render: function() {
|
||||
var commentNodes = this.props.data.map(function(comment) {
|
||||
return (
|
||||
<Comment author={comment.author} key={comment.id}>
|
||||
{comment.text}
|
||||
</Comment>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<div className="commentList">
|
||||
{commentNodes}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var CommentForm = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {author: '', text: ''};
|
||||
},
|
||||
handleAuthorChange: function(e) {
|
||||
this.setState({author: e.target.value});
|
||||
},
|
||||
handleTextChange: function(e) {
|
||||
this.setState({text: e.target.value});
|
||||
},
|
||||
handleSubmit: function(e) {
|
||||
e.preventDefault();
|
||||
var author = this.state.author.trim();
|
||||
var text = this.state.text.trim();
|
||||
if (!text || !author) {
|
||||
return;
|
||||
}
|
||||
this.props.onCommentSubmit({author: author, text: text});
|
||||
this.setState({author: '', text: ''});
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<form className="commentForm" onSubmit={this.handleSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Your name"
|
||||
value={this.state.author}
|
||||
onChange={this.handleAuthorChange}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Say something..."
|
||||
value={this.state.text}
|
||||
onChange={this.handleTextChange}
|
||||
/>
|
||||
<input type="submit" value="Post" />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ReactDOM.render(
|
||||
<CommentBox url="/api/comments" pollInterval={2000} />,
|
||||
document.getElementById('content')
|
||||
);
|
||||
Reference in New Issue
Block a user