Skip to content

Instantly share code, notes, and snippets.

@WillSams
Last active March 8, 2019 14:10
Show Gist options
  • Save WillSams/df9beb2ab44702c461eeb2902b8f2673 to your computer and use it in GitHub Desktop.
Save WillSams/df9beb2ab44702c461eeb2902b8f2673 to your computer and use it in GitHub Desktop.
VSCode Script For Customer-Orders-Core base template
#!/bin/bash
set -o nounset # unset variables are errors
SCRIPTVERSION="2018.10.09-Debian"
SCRIPTNAME="create-dotnetcore-example.sh"
SCRIPTFULLNAME="$0"
SOLUTIONNAME=Northwind
API=$SOLUTIONNAME.Api
MODELS=$SOLUTIONNAME.Models
SERVICES=$SOLUTIONNAME.Services
TESTS=$SOLUTIONNAME.Tests
NPMGLOBALS=(pm2 grunt-cli webpack cypress)
echoerror() { printf "\033[1;31m * ERROR\033[0m: %s\\n" "$@" 1>&2; }
usage() {
cat << EOT
Usage : ${SCRIPTNAME} [options]
Options:
-h Display this message
-v Display script version
-s Solution name. Default is Northwind.
EOT
} # ---------- end of function usage ----------
while getopts ':hvs:u:p:' opt
do
case "${opt}" in
h ) usage; exit 0 ;;
v ) echo "$0 -- Version $SCRIPTVERSION"; exit 0 ;;
s ) SOLUTIONNAME=$OPTARG ;;
\?) echo
echoerror "Option does not exist : $OPTARG"
usage
exit 1
;;
esac # --- end of case ---
done
shift $((OPTIND-1))
if [ -d /etc/nginx ]; then
echo "Nginx is already installed on this system."
else
echo "**************************** NGINX ******************************"
echo "Installing nginx web server."
echo "You may be prompted for root credentials to complete the install."
echo "*****************************************************************"
sudo bash -c "add-apt-repository ppa:certbot/certbot -y"
sudo bash -c "apt update && apt upgrade -y"
sudo bash -c "apt install nginx python-certbot-nginx -y"
sudo bash -c "ufw allow 'NGINX FULL' && ufw enable"
sudo bash -c "rm -v /etc/nginx/sites-available/default"
sudo bash -c "sed -e '/include \/etc\/nginx\/sites-enabled/ s/^#*/#/' -i /etc/nginx/nginx.conf"
sudo bash -c "echo '#Image Caching
include \/etc\/nginx\/conf.d\/img-cache.conf;' >> /etc/nginx/nginx.conf"
sudo bash -c "echo 'location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
expires 90d;
add_header Pragma $API/wwwroot;
add_header Cache-Control \"$API/wwwroot\";
}' >> /etc/nginx/conf.d/img-cache.conf"
touch /etc/nginx/conf.d/default.conf
sudo bash -c "systemctl enable nginx && service nginx start"
fi
if [ -f /usr/bin/dotnet ]; then
echo "Dotnet is already installed."
else
echo "************************ DOTNET 2.1 INSTALL **************************"
echo "Installing .NET."
echo "You may be prompted for root credentials to complete the install."
echo "******************************************************************"
sudo bash -c "add-apt-repository universe && apt update && apt upgrade -y"
wget https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb
sudo bash -c "dpkg -i packages-microsoft-prod.deb"
sudo bash -c "apt install apt-transport-https"
sudo bash -c "apt update && apt upgrade -y"
sudo bash -c "apt install dotnet-sdk-2.1 -y"
echo -e "export DOTNET_CLI_TELEMETRY_OPTOUT=1" >> ~/.bashrc
source ~/.bashrc
fi
if [ -d $HOME/.npm-global ]; then
echo "~/.npm-global exists in your path."
else
echo "********************* NODEJS & NPM INSTALL ***********************"
echo "Installing nodejs and npm. Also adding ~/.npm-global to your path"
echo "You may be prompted for root credentials to complete the install."
echo "******************************************************************"
mkdir ~/.npm-global
echo -e "export NPM_CONFIG_PREFIX=$HOME/.npm-global" >> ~/.bashrc
echo -e "PATH=$PATH:$HOME/.npm-global/bin" >> ~/.bashrc
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
sudo bash -c "apt update && apt upgrade -y"
sudo bash -c "apt install nodejs -y"
sudo bash -c "npm config set prefix '/home/$USER/.npm-global'"
sudo bash -c "chown $USER:$USER -R $HOME/.config"
fi
for npmglobal in ${NPMGLOBALS[*]}
do
if [ -d $HOME/.npm-global/lib/node_modules/$npmglobal ]; then
echo "NPM package '$npmglobal' exists globally via ~/.npm-global"
else
echo "************************* $npmglobal INSTALL ****************************"
echo "Installing NPM package '$npmglobal' to ~/.npm-global."
echo "******************************************************************"
npm i -g $npmglobal
fi
done
if [ -d $HOME/$SOLUTIONNAME ]; then
echo "$SOLUTIONNAME already exists in the home directory but should be in
/var/www if this script executed completely. Something went wrong?"
else
PROJECTHOME=$HOME/Projects/dotnet/$SOLUTIONNAME
mkdir -p $PROJECTHOME && cd $PROJECTHOME
dotnet new sln
mkdir -p .tools dist $MODELS $TESTS/specs
mkdir -p $API/wwwroot/js $API/wwwroot/css $API/wwwroot/css/sass $API/wwwroot/images
git init
echo "node_modules/
.sass-cache/
.vscode/
bin/
obj/" >> .gitignore
echo "{
\"name\": \"$SOLUTIONNAME\",
\"version\": \"1.0.0\",
\"description\": \"\",
\"scripts\": {
\"specs\": \"mocha $TESTS/specs/**/**_spec.js --exit\"
},
\"author\": \"Your Name\",
\"email\": \"you@syourdomain.com\",
\"url\": \"https://yourdomain.com\"
}" >> package.json
npm i
npm i --save express react react-dom react-scripts sugar
npm i --save-dev chai chai-http
npm i --save-dev grunt grunt-contrib-uglify grunt-contrib-concat grunt-contrib-sass gruntify-eslint grunt-contrib-watch
echo '{
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"env": {
"es6": true,
"browser": true,
"node": true,
"mocha": true
},
"extends": [
"eslint:recommended",
"jquery"
],
"rules": {
"strict": [ 2, "safe" ],
"no-debugger": 2,
"brace-style": [
2,
"1tbs",
{ "allowSingleLine": true }
],
"no-trailing-spaces": 2,
"keyword-spacing": 2,
"space-before-function-paren": [
2,
"never"
],
"spaced-comment": [2, "always" ],
"vars-on-top": 2,
"no-undef": 2,
"no-undefined": 2,
"comma-dangle": [ 2, "never" ],
"quotes": [ 2, "single" ],
"semi": [ 2, "always" ],
"guard-for-in": 2,
"no-eval": 2,
"no-with": 2,
"valid-typeof": 2,
"no-unused-vars": 2,
"no-continue": 1,
"no-extra-semi": 1,
"no-unreachable": 1,
"no-unused-expressions": 1,
"no-magic-numbers": 1,
"max-len": [1, 80, 4]
},
}' >> .eslintrc
echo "module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
dist: {
src: ['$API/wwwroot/js/**/*.js','!$API/wwwroot/js/application.min.js'],
dest: 'dist/application.js'
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today(\"dd-mm-yyyy\") %> */\n'
},
dist: {
files: [{ '$API/wwwroot/js/application.min.js': 'dist/application.js' }]
}
},
sass: {
dist: {
options: { style: 'compressed' },
files: [{ '$API/wwwroot/css/application.min.css' : '$API/wwwroot/css/application.scss' } ]
}
},
watch: {
options: {
dateFormat: function(time) {
grunt.log.writeln(`Change "'$'"{(new Date()).toString()} completed in "'$'"{time} ms.`);
grunt.log.writeln('Waiting for more changes...');
}
},
scripts: {
files: '$API/wwwroot/css/*.scss',
tasks: 'sass'
}
},
eslint: {
src: ['app.js', 'src/**/*.js'],
options: { configFile: '.eslintrc' }
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('gruntify-eslint');
grunt.registerTask('default', ['concat', 'uglify', 'sass', 'eslint']);
};" >> Gruntfile.js
cd $PROJECTHOME/$MODELS
dotnet new classlib
dotnet add package System.ComponentModel.Annotations
echo "using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace $MODELS {
$API/wwwroot class Customer {
$API/wwwroot Customer() { Orders = new HashSet<Order>(); }
[Column(\"CustomerID\")]
[MaxLength(5)]
$API/wwwroot string CustomerId { get; set; }
[MaxLength(60)]
$API/wwwroot string Address { get; set; }
[MaxLength(15)]
$API/wwwroot string City { get; set; }
[Required]
[MaxLength(40)]
$API/wwwroot string CompanyName { get; set; }
[MaxLength(30)]
$API/wwwroot string ContactName { get; set; }
[MaxLength(30)]
$API/wwwroot string ContactTitle { get; set; }
[MaxLength(15)]
$API/wwwroot string Country { get; set; }
[MaxLength(24)]
$API/wwwroot string Fax { get; set; }
[MaxLength(24)]
$API/wwwroot string Phone { get; set; }
[MaxLength(10)]
$API/wwwroot string PostalCode { get; set; }
[MaxLength(15)]
$API/wwwroot string Region { get; set; }
[InverseProperty(\"Customer\")]
$API/wwwroot virtual ICollection<Order> Orders { get; set; }
}
}" >> Customer.cs
echo "using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace $MODELS {
$API/wwwroot class Order {
$API/wwwroot Order() {
// OrderDetails = new HashSet<OrderDetail>();
}
[Column(\"OrderID\")]
$API/wwwroot int OrderId { get; set; }
[Column(\"CustomerID\")]
[MaxLength(5)]
$API/wwwroot string CustomerId { get; set; }
[Column(\"EmployeeID\")]
$API/wwwroot int? EmployeeId { get; set; }
$API/wwwroot decimal? Freight { get; set; }
$API/wwwroot DateTime? OrderDate { get; set; }
$API/wwwroot DateTime? RequiredDate { get; set; }
[MaxLength(60)]
$API/wwwroot string ShipAddress { get; set; }
[MaxLength(15)]
$API/wwwroot string ShipCity { get; set; }
[MaxLength(15)]
$API/wwwroot string ShipCountry { get; set; }
[MaxLength(40)]
$API/wwwroot string ShipName { get; set; }
[MaxLength(10)]
$API/wwwroot string ShipPostalCode { get; set; }
[MaxLength(15)]
$API/wwwroot string ShipRegion { get; set; }
$API/wwwroot int? ShipVia { get; set; }
$API/wwwroot DateTime? ShippedDate { get; set; }
/*[InverseProperty(\"Order\")]
$API/wwwroot virtual ICollection<OrderDetail> OrderDetails { get; set; }*/
[InverseProperty(\"Orders\")]
$API/wwwroot virtual Customer Customer { get; set; }
}
}" >> Order.cs
echo "using System.ComponentModel.DataAnnotations.Schema;
namespace $MODELS {
[Table(\"Order Details\")]
$API/wwwroot class OrderDetail {
[Column(\"OrderID\")]
$API/wwwroot int OrderId { get; set; }
[Column(\"ProductID\")]
$API/wwwroot int ProductId { get; set; }
$API/wwwroot float Discount { get; set; }
$API/wwwroot short Quantity { get; set; }
$API/wwwroot decimal UnitPrice { get; set; }
[InverseProperty(\"OrderDetails\")]
$API/wwwroot virtual Order Order { get; set; }
[InverseProperty(\"OrderDetails\")]
$API/wwwroot virtual Product Product { get; set; }
}
}" >> OrderDetail.cs
echo "using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace $MODELS {
$API/wwwroot class Product {
$API/wwwroot Product() { OrderDetails = new HashSet<OrderDetail>(); }
[Column(\"ProductID\")]
$API/wwwroot int ProductId { get; set; }
[Column(\"CategoryID\")]
$API/wwwroot int? CategoryId { get; set; }
$API/wwwroot bool Discontinued { get; set; }
[Required]
[MaxLength(40)]
$API/wwwroot string ProductName { get; set; }
[MaxLength(20)]
$API/wwwroot string QuantityPerUnit { get; set; }
$API/wwwroot short? ReorderLevel { get; set; }
[Column(\"SupplierID\")]
$API/wwwroot int? SupplierId { get; set; }
$API/wwwroot decimal? UnitPrice { get; set; }
$API/wwwroot short? UnitsInStock { get; set; }
$API/wwwroot short? UnitsOnOrder { get; set; }
[InverseProperty(\"Product\")]
$API/wwwroot virtual ICollection<OrderDetail> OrderDetails { get; set; }
/*[InverseProperty(\"Products\")]
$API/wwwroot virtual Category Category { get; set; }
[InverseProperty(\"Products\")]
$API/wwwroot virtual Supplier Supplier { get; set; }*/
}
}" >> Product.cs
cd $PROJECTHOME/$API
dotnet new webapi
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package AutoFac
dotnet add reference ../$MODELS/$MODELS.csproj
mkdir data
echo "using Microsoft.EntityFrameworkCore;
using $MODELS;
namespace $API {
$API/wwwroot class NorthwindContext : DbContext {
$API/wwwroot NorthwindContext(DbContextOptions<NorthwindContext> options) : base(options) { }
$API/wwwroot DbSet<Customer> Customers { get; set; }
$API/wwwroot DbSet<Order> Orders { get; set; }
/*$API/wwwroot DbSet<OrderDetail> OrderDetails { get; set; }
$API/wwwroot DbSet<Product> Products { get; set; }*/
}
}" >> NorthwindContext.cs
echo "using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using $MODELS;
namespace $API.Controllers {
[Route(\"api/Customers\")]
$API/wwwroot class CustomersController : Controller {
private readonly NorthwindContext _context;
$API/wwwroot CustomersController(NorthwindContext context){
_context = context;
if(_context.Customers.Count() == 0) {
_context.Customers.Add(new Customer { CustomerId = \"WSAMS\", ContactName = \"Mr. New Contact\" } );
_context.SaveChanges();
}
}
[HttpGet]
$API/wwwroot IEnumerable<Customer> GetAll(){ return _context.Customers.ToList(); }
[HttpGet(\"{id}\", Name = \"GetCustomer\")]
$API/wwwroot IActionResult GetById(string id) {
var item = _context.Customers.FirstOrDefault(c => c.CustomerId == id);
if (item == null) return NotFound();
return new ObjectResult(item);
}
[HttpPut(\"{id}\")]
$API/wwwroot IActionResult Update(string id, [FromBody] Customer item) {
if (item == null || item.CustomerId != id) return BadRequest();
var customer = _context.Customers.FirstOrDefault(c => c.CustomerId == id);
if (customer == null) return NotFound();
customer.ContactName = item.ContactName;
customer.ContactTitle = item.ContactTitle;
customer.Phone = item.Phone;
customer.Fax = item.Fax;
_context.Customers.Update(customer);
_context.SaveChanges();
return new NoContentResult();
}
[HttpDelete(\"{id}\")]
$API/wwwroot IActionResult Delete(string id){
var customer = _context.Customers.FirstOrDefault(c => c.CustomerId == id);
if (customer == null) return NotFound();
_context.Customers.Remove(customer);
_context.SaveChanges();
return new NoContentResult();
}
}
}" >> Controllers/CustomersController.cs
echo "using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using $MODELS;
namespace $API.Controllers {
[Route(\"api/Orders\")]
$API/wwwroot class OrdersController : Controller {
private readonly NorthwindContext _context;
$API/wwwroot OrdersController(NorthwindContext context){ _context = context; }
[HttpGet]
$API/wwwroot IEnumerable<Order> GetAll(){ return _context.Orders.ToList(); }
[HttpGet(\"{id}\", Name = \"GetOrder\")]
$API/wwwroot IActionResult GetById(int id) {
var item = _context.Orders.FirstOrDefault(o => o.OrderId == id);
if (item == null) return NotFound();
return new ObjectResult(item);
}
}
}" >> Controllers/OrdersController.cs
cd $PROJECTHOME/.tools
wget https://raw.githubusercontent.com/WillSams/CustomerOrders-ASPNET-MVC3-VB/master/sql/Northwind.Sqlite3.sql
echo "import os
import sqlite3
import codecs
def create_northwind_db():
if os.path.isfile(r'northwind.db'):
print 'This script will create the Norhtwind database for you.' + \
'If you want to re-create the database, please delete the ' + \
'existing file if you want to re-create it.'
else:
print 'Creating \'Northwind.db\'. Please wait as this may take up to a minute to complete.'
query = codecs.open('Northwind.Sqlite3.sql', 'r', encoding='iso-8859-1').read()
conn = sqlite3.connect(r'northwind.db')
cur = conn.cursor()
cur.executescript(query)
conn.commit()
cur.close()
conn.close()
if __name__ == '__main__':
create_northwind_db()" >> dbcreate.py
python dbcreate.py
mv northwind.db $PROJECTHOME/$API/data
cd $PROJECTHOME/$TESTS
dotnet new nunit
dotnet add package Moq
dotnet add package FluentAssertions
dotnet add package System.Linq.Queryable
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add reference ../$API/$API.csproj
dotnet add reference ../$MODELS/$MODELS.csproj
cd $PROJECTHOME
dotnet sln add $MODELS/$MODELS.csproj
dotnet sln add $API/$API.csproj
dotnet sln add $TESTS/$TESTS.csproj
dotnet restore
git add .
git commit -am "Initial commit."
grunt && grunt watch
code .
fi
//TODO: 3/7/2018 - w.sams - Yikes, I haven't updated the original logic since 2011. Need to lint, test, and comment.
const sugar = require('sugar');
const AppCommon = function(baseUrl) {
const url = decodeURI(window.location.pathname);
let privBaseUrl = baseUrl || '~/';
let reloadTimer = null;
const getFormatFromJSONDateTime = function(jsonDate) {
let date = null;
try { date = sugar.Date.create(jsonDate); }
catch (err) { date = eval(jsonDate.replace(/\/Date\((.*?)\)\//gi, 'new Date($1)')); }
return date;
};
return {
closeFormOK: (context, strReturn) => {
if (!strReturn) { strReturn = true; }
window.returnValue = strReturn;
context.close();
},
closeFormCancel: (context) => {
window.returnValue = '0';
context.close();
},
formatJSONDateTime: (jsonDate) => {
const date = getFormatFromJSONDateTime(jsonDate);
const month = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
return `${date.getDate()} ${month[date.getMonth()]} ${date.getFullYear()}
${date.getHours()}:${formatMinutes(date.getMinutes())}`;
},
formatJSONDate: (jsonDate) => {
const date = getFormatFromJSONDateTime(jsonDate);
const month = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
return `${date.getDate()} ${month[date.getMonth()]} ${date.getFullYear()}`;
},
tickerSlider: (container, delay) => {
if ($(container).length) {
container = $(container);
let slides = container.length, slide = 0;
setInterval(() => {
if (slide === slides) {
container.slideDown();
slide = 0;
} else {
container.eq(slide).slideUp();
slide++;
}
}, delay);
} else {
return false;
}
},
forcePageRefresh: (seconds) => {
if (arguments.length === 1) {
if (reloadTimer) { clearTimeout(reloadTimer); }
reloadTimer = setTimeout(() => { forcePageRefresh(); }, Math.ceil(parseFloat(seconds) * 1000));
} else {
reloadTimer = null;
location.reload(true);
window.location.replace(url);
}
},
parseUrl: (url) => {
const expr = /((http|ftp):\/)?\/?([^:\/\s]+)((\/\w+)*\/)([\w\-\.]+\.[^#?\s]+)(#[\w\-]+)?/;
let returnVal = null;
if (url.match(expr)) {
returnVal = { url: RegExp['$&'], protocol: RegExp.$2, host: RegExp.$3, path: RegExp.$4, file: RegExp.$6, hash: RegExp.$7 };
} else {
returnVal = { url: 'No match', protocol: '', host: '', path: '', file: '', hash: '' };
}
return returnVal;
},
stringEscape: (str) => {
const specials = ['#', '&', '~', '=', '>', ''', ':', ''', '!', ';', ','];
const regexSpecials = ['.', '*', '+', '|', '[', ']', '(', ')', '/', '^', '$'];
const regex = new RegExp('(' + specials.join('|') + '|\\' + regexSpecials.join('|\\') + ')', 'g');
return str.replace(regex, '\\$1');
},
getCharCount: (str, c) => {
return (str.length - str.replace(new RegExp(c, 'g'), '').length) / c.length;
},modalFadeOut = function(hash) {
hash.o.remove(); hash.w.fadeOut(100);
},
modalFadeIn: (hash) => { hash.w.fadeIn(100); },
isSameElement: (elm1, elm2) => { return elm1.get(0) === elm2.get(0); },
resolveUrl: (url) => {
if (url.indexOf('~/') === 0) { url = privBaseUrl + url.substring(2); }
return url;
},
messageBox: (boxId, title, actionFunc, html, dialogWidth) => {
if (actionFunc === null) { actionFunc = () => { return; }; }
if ((dialogWidth === null) || (dialogWidth === '')) { dialogWidth = 300; }
if (html.length > 50) {
dialogWidth = dialogWidth + ((html.length - 50) * 10);
if (dialogWidth > 860) { dialogWidth = 860; }
}
$('<div id=\'messageBox_' + boxId + '\'>' + html + '</div>').dialog({
modal: true,
title: title,
show: 'slide', hide: 'slide',
width: dialogWidth,
buttons: {
'OK': () => {
$(this).dialog('close');
actionFunc();
}
}
});
},
jqGridLoadError: (context, grid, jqXHR, textStatus, errorThrown) => {
$('#' + grid.id + '_err').remove(); // remove error div if exist
// insert div with the error description before the grid
$('#' + grid.id).closest('div.ui-jqgrid').before(
`<div id='${grid.id}_err' style='max-width:${grid.style.width}'>` +
'<div class=\'ui-state-error ui-corner-all\' style=\'padding:0.7em;float:left;\'>' +
'<span class=\'ui-icon ui-icon-alert\' style=\'float:left; margin-right: .3em;\'></span>' +
`<span style='clear:left'>${$(context)[0].decodeErrorMessage(jqXHR, textStatus, errorThrown)}</span>` +
'</div>' +
'<div style=\'clear:left\'/></div>'
);
},
decodeErrorMessage: (context, jqXHR, errorThrown, errorMsg) => {
let err = null;
let html = '';
try {
err = eval(`(${jqXHR.responseText})`);
html = `${errorThrown}<hr /><b>${errorMsg}</b><br /><br />` +
`Exception Type: ${err.ExceptionType}<br />` +
`Message: ${err.Message<br />` +
`Stack Trace: ${err.StackTrace}`;
} catch (ex) {
html = `${jqXHR.status} ${jqXHR.statusText}`;
}
$(context)[0].messageBox('divErrorMessage', errorThrown, null, html, null);
}
};
};
- Redo this with TDD (NUnit) and BDD (mocha, chai, Cypress.io) in mind for a future blog post
- Clean up script, add optional parameters for script
- Implement Web Api as the backend
- Implement a React CRUD grid as the front end
DONE-Add .NET Core install instructions-DONE
DONE-Remove 'cat' and use 'echo'-DONE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment