diff --git a/README.md b/README.md
index 8f2a9e6..58fa929 100644
--- a/README.md
+++ b/README.md
@@ -84,3 +84,21 @@ Submit a pull request when you are finished and satisfied with your work.
## License
[MIT](http://opensource.org/licenses/MIT)
+
+
+----
+edits by longda below:
+
+
+## Note #1:
+At this point, we've reached a critical juncture in our journey. If we were working a big team, we could start dividing things up:
+
+1. If we had a web app that user's could, um, use to make notes for instance, we could hand that off to a front-end dev to start whipping things out using the stub services rather than REAL services that have to hit a REAL data store. Another example (which could be developed in tandem) would be an admin site where our internal team could start setting up user accounts to use our API.
+2. We could start talking to a dba type to start setting up our db and our sprocs, etc.
+3. We could also have a middle-tier dev start fleshing out the API calls and the auth and maybe even an ORM or something.
+4. Or we could scrap all that and just have a tech lead do it! :boom:
+
+
+## Note #2:
+Here's the sample output from the get call as of this point:
+{"Id":1,"Title":"Go to the Galaxy Game.","Description":"Head over to Stub Hub Center in Carson, California for a lovely game of fútbol.","IsComplete":false}
\ No newline at end of file
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..16b413c
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,182 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.sln.docstates
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+build/
+bld/
+[Bb]in/
+[Oo]bj/
+
+# Roslyn cache directories
+*.ide/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+#NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding addin-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# If using the old MSBuild-Integrated Package Restore, uncomment this:
+#!**/packages/repositories.config
+
+# Windows Azure Build Output
+csx/
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+sql/
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
\ No newline at end of file
diff --git a/src/Notesy.Api/App_Start/NinjectWebCommon.cs b/src/Notesy.Api/App_Start/NinjectWebCommon.cs
new file mode 100644
index 0000000..c4d5d17
--- /dev/null
+++ b/src/Notesy.Api/App_Start/NinjectWebCommon.cs
@@ -0,0 +1,82 @@
+[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(Notesy.Api.App_Start.NinjectWebCommon), "Start")]
+[assembly: WebActivatorEx.ApplicationShutdownMethodAttribute(typeof(Notesy.Api.App_Start.NinjectWebCommon), "Stop")]
+
+namespace Notesy.Api.App_Start
+{
+ using System;
+ using System.Web;
+
+ using Microsoft.Web.Infrastructure.DynamicModuleHelper;
+
+ using Ninject;
+ using Ninject.Web.Common;
+
+ using Notesy.Core.Services.Concrete;
+ using Notesy.Core.Services.Interfaces;
+ using Notesy.Core.Services.Stubs;
+
+ public static class NinjectWebCommon
+ {
+ private static readonly Bootstrapper bootstrapper = new Bootstrapper();
+
+ ///
+ /// Starts the application
+ ///
+ public static void Start()
+ {
+ DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
+ DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
+ bootstrapper.Initialize(CreateKernel);
+ }
+
+ ///
+ /// Stops the application.
+ ///
+ public static void Stop()
+ {
+ bootstrapper.ShutDown();
+ }
+
+ ///
+ /// Creates the kernel that will manage your application.
+ ///
+ /// The created kernel.
+ private static IKernel CreateKernel()
+ {
+ var kernel = new StandardKernel();
+ try
+ {
+ kernel.Bind>().ToMethod(ctx => () => new Bootstrapper().Kernel);
+ kernel.Bind().To();
+
+ // Note: Look how easy that is to swap out the fake services (which might not hit a real db) for real ones. I bet we could
+ // even do that with a config setting or db setting too (or not)!
+
+ //RegisterServices(kernel);
+ RegisterStubServices(kernel);
+ return kernel;
+ }
+ catch
+ {
+ kernel.Dispose();
+ throw;
+ }
+ }
+
+ ///
+ /// Load your modules or register your services here!
+ ///
+ /// The kernel.
+ private static void RegisterServices(IKernel kernel)
+ {
+ kernel.Bind().To().InRequestScope();
+ kernel.Bind().To().InRequestScope();
+ }
+
+ private static void RegisterStubServices(IKernel kernel)
+ {
+ kernel.Bind().To().InRequestScope();
+ kernel.Bind().To().InRequestScope();
+ }
+ }
+}
diff --git a/src/Notesy.Api/App_Start/RouteConfig.cs b/src/Notesy.Api/App_Start/RouteConfig.cs
new file mode 100644
index 0000000..18a7f45
--- /dev/null
+++ b/src/Notesy.Api/App_Start/RouteConfig.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using System.Web.Mvc;
+using System.Web.Routing;
+
+namespace Notesy.Api
+{
+ public class RouteConfig
+ {
+ public static void RegisterRoutes(RouteCollection routes)
+ {
+ routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
+
+ routes.MapRoute(
+ name: "Default",
+ url: "{controller}/{action}/{id}",
+ defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
+ );
+ }
+ }
+}
diff --git a/src/Notesy.Api/Controllers/NoteController.cs b/src/Notesy.Api/Controllers/NoteController.cs
new file mode 100644
index 0000000..039ff42
--- /dev/null
+++ b/src/Notesy.Api/Controllers/NoteController.cs
@@ -0,0 +1,175 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using System.Web;
+using System.Web.Mvc;
+
+using Notesy.Core.Models;
+using Notesy.Core.Services.Interfaces;
+
+namespace Notesy.Api.Controllers
+{
+ // So, there are a quite a few ways to setup an API. For this one, since it's a quick-hitter, I'm just going to leverage a bunch of stuff
+ // that's already baked in so we can whip this thing out.
+
+ // For now, calls will come into the NoteController which will be where the authentication goes down. If a call passes auth, then we will
+ // send the call off to the dependency injected Note Service (which could be a real service or a fake/stub one). This provides us with a
+ // host of advantages such as being able to isolate auth to the very edge (aka application boundry) and we can then re-use all that delicious
+ // code we've written in the service that does, you know, actual work. We could then do things like re-use the API internally without auth
+ // on something akin to a private API or even call it from inside that Notesy.Web project (which could be a brochure site, a web app, etc).
+
+ // Additionally, auth can be done in many ways. We could do OAuth if we were leveraging an external service (e.g. we had folks sign up for
+ // API access with Twitter). I'm going to do my patented "fake auth" which will illustrate a "signed" API similar to this real world
+ // example: http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-canned-policy.html
+ // I'm going to do this inline to save time but if this were a real life app I'd look into using an Action Filter or possibly an
+ // existing library off nuget. Or a base controller. Or an extension method. Or a helper. Or a combination of these.
+
+ // TLDR; Everything always depends. Real world constraints might change how you architect things.
+
+ public class NoteController : Controller
+ {
+ private readonly INoteService noteService;
+ private readonly IApiUserService apiUserService;
+
+ public NoteController(INoteService noteService, IApiUserService apiUserService)
+ {
+ this.noteService = noteService;
+ this.apiUserService = apiUserService;
+ }
+
+ // Note: This will be at something like: http://localhost:63185/note/save
+ // TODO: since this is taking in a note model we'll need to make sure we're serializing the input properly before it hits the api
+
+ ///
+ /// Save a Note.
+ ///
+ /// The note to save.
+ /// The api key of the user request.
+ /// A rotating integer to uniquely identify this request. Usually just use current ticks count. There are a number of ways to do this.
+ /// Signature for this api request.
+ ///
+ public ActionResult Save(Note input, string apikey = null, int? callId = null, string signature = null)
+ {
+ if (!ApiHelper.ValidateAuth(ApiHelper.ToJsonString(input), apikey, signature)) { return new HttpUnauthorizedResult(); }
+
+ var result = noteService.SaveNote(input);
+
+ return Json(input, JsonRequestBehavior.AllowGet);
+ }
+
+ public ActionResult Get(int id, string apikey = null, int? callId = null, string signature = null)
+ {
+ // basic logic for this architecture (could be different)
+ // 1) validate the auth on the incoming request
+ // 2) next we need to get the api user by apikey (oops - we need to write that!)
+ // 3) next we need to get the note
+ // 4) last we need to check that the api user is the owner of this note
+
+ // obviously lots of decisions here... this might have been easier if we used
+ // an orm. we also could have pushed this logic one level lower so we could
+ // reuse it. this will be good for now though.
+
+ if (!ApiHelper.ValidateAuth(id.ToString(), apikey, callId.Value.ToString(), signature)) { return new HttpUnauthorizedResult(); }
+
+ var user = apiUserService.GetApiUserByApiKey(apikey);
+ var note = noteService.GetNote(id);
+
+ if (user.Id == note.ApiUserId)
+ {
+ return Json(note, JsonRequestBehavior.AllowGet);
+ }
+
+ // TODO: Have to think about what to do with errors.
+ return Json(note, JsonRequestBehavior.AllowGet);
+ }
+
+ public ActionResult Delete(int id, string apikey = null, int? callId = null, string signature = null)
+ {
+ if (!ApiHelper.ValidateAuth(id.ToString(), apikey, callId.Value.ToString(), signature)) { return new HttpUnauthorizedResult(); }
+
+ var result = noteService.DeleteNote(id);
+
+ return Json(result, JsonRequestBehavior.AllowGet);
+ }
+ }
+
+ public static class ApiHelper
+ {
+ // TODO: the following methods would be good candidates for relocation to a helper or other logical place.
+ // NOTE: Need to start making these public now for testing. It would be better to put this is in it's own
+ // helper class or something but we're running low on time (for now at least).
+
+ public static bool ValidateAuth(string apiKey, string signatureToCheck, params string[] args)
+ {
+ // TODO: actual auth stuff
+ // 1) lookup api user
+ // 2) generate signature of inputs
+ // 3) compare generated signature to incoming signature
+ // 4) if valid, return true, otherwise false - note there could be a number of other things to check here: api usage, incoming IP, etc.
+
+ return true;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static bool DoSignaturesMatch(ApiUser apiUser, string signature, params string[] args)
+ {
+ // how to generate a signature:
+ // 1) concat all the call parameters into one string (see string array above)
+ // 2) run some agreed upon encryption algorithm on concat string using the shared secret associated with the apikey => apiuser for this request
+
+ // IMPORTANT: for this to actually work correctly we need to store the call id so that api calls cannot be replayed
+ // NOTE: remember this is just sort of a quick homebrew system. you'd almost certainly want to reuse an appropriate auth library.
+ // TODO: i think i got that right, double-check once implemented and document any changes here
+
+ var sb = new StringBuilder();
+ foreach (var arg in args)
+ {
+ sb.Append(arg);
+ }
+ sb.Append(apiUser.ApiSecret);
+ var gen = ApiHelper.GetHash(sb.ToString());
+
+ return gen == signature;
+ }
+
+ public static string ToJsonString(object input)
+ {
+ // here is where we'd serialize the object out to a string containing a json object
+ return "";
+ }
+
+ private static HashAlgorithm HashProvider = new SHA1Managed();
+
+ public static string GetHash(string input)
+ {
+ byte[] inputBytes = Encoding.UTF8.GetBytes(input);
+ byte[] data = HashProvider.ComputeHash(inputBytes);
+ var output = ByteArrayToHex(data);
+
+ return output;
+ }
+
+ // http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa
+ public static string ByteArrayToHex(byte[] barray)
+ {
+ char[] c = new char[barray.Length * 2];
+ byte b;
+ for (int i = 0; i < barray.Length; ++i)
+ {
+ b = ((byte)(barray[i] >> 4));
+ c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30);
+ b = ((byte)(barray[i] & 0xF));
+ c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30);
+ }
+
+ return new string(c);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Notesy.Api/Global.asax b/src/Notesy.Api/Global.asax
new file mode 100644
index 0000000..606815a
--- /dev/null
+++ b/src/Notesy.Api/Global.asax
@@ -0,0 +1 @@
+<%@ Application Codebehind="Global.asax.cs" Inherits="Notesy.Api.MvcApplication" Language="C#" %>
diff --git a/src/Notesy.Api/Global.asax.cs b/src/Notesy.Api/Global.asax.cs
new file mode 100644
index 0000000..0e4389c
--- /dev/null
+++ b/src/Notesy.Api/Global.asax.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using System.Web.Mvc;
+using System.Web.Routing;
+
+namespace Notesy.Api
+{
+ public class MvcApplication : System.Web.HttpApplication
+ {
+ protected void Application_Start()
+ {
+ AreaRegistration.RegisterAllAreas();
+ RouteConfig.RegisterRoutes(RouteTable.Routes);
+ }
+ }
+}
diff --git a/src/Notesy.Api/Notesy.Api.csproj b/src/Notesy.Api/Notesy.Api.csproj
new file mode 100644
index 0000000..7830712
--- /dev/null
+++ b/src/Notesy.Api/Notesy.Api.csproj
@@ -0,0 +1,159 @@
+
+
+
+
+ Debug
+ AnyCPU
+
+
+ 2.0
+ {291D09DB-7DCB-41DB-B634-942B1F8BC545}
+ {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}
+ Library
+ Properties
+ Notesy.Api
+ Notesy.Api
+ v4.5
+ true
+
+
+
+
+
+
+ true
+ full
+ false
+ bin\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\
+ TRACE
+ prompt
+ 4
+
+
+
+
+ ..\packages\Ninject.3.2.2.0\lib\net45-full\Ninject.dll
+
+
+ ..\packages\Ninject.Web.Common.3.2.0.0\lib\net45-full\Ninject.Web.Common.dll
+
+
+ ..\packages\Ninject.MVC5.3.2.1.0\lib\net45-full\Ninject.Web.Mvc.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ..\packages\WebActivatorEx.2.0\lib\net40\WebActivatorEx.dll
+
+
+
+
+ True
+ ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll
+
+
+ ..\packages\Microsoft.AspNet.Razor.3.0.0\lib\net45\System.Web.Razor.dll
+
+
+ ..\packages\Microsoft.AspNet.Webpages.3.0.0\lib\net45\System.Web.Webpages.dll
+
+
+ ..\packages\Microsoft.AspNet.Webpages.3.0.0\lib\net45\System.Web.Webpages.Deployment.dll
+
+
+ ..\packages\Microsoft.AspNet.Webpages.3.0.0\lib\net45\System.Web.Webpages.Razor.dll
+
+
+ ..\packages\Microsoft.AspNet.Webpages.3.0.0\lib\net45\System.Web.Helpers.dll
+
+
+ ..\packages\Microsoft.AspNet.Mvc.5.0.0\lib\net45\System.Web.Mvc.dll
+
+
+
+
+
+
+
+
+
+
+
+ Global.asax
+
+
+
+
+
+
+
+ Web.config
+
+
+ Web.config
+
+
+
+
+
+
+
+ {5dd8ffda-34c8-4f81-8740-20e29bfc4e90}
+ Notesy.Core
+
+
+
+ 10.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+
+
+
+
+ True
+ True
+ 0
+ /
+ http://localhost:63185/
+ False
+ False
+
+
+ False
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Notesy.Api/Properties/AssemblyInfo.cs b/src/Notesy.Api/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..6e121e3
--- /dev/null
+++ b/src/Notesy.Api/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Notesy.Api")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Notesy.Api")]
+[assembly: AssemblyCopyright("Copyright © 2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("86bfb859-2218-496a-b8e5-e250645b26ba")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/Notesy.Api/Views/web.config b/src/Notesy.Api/Views/web.config
new file mode 100644
index 0000000..09da0d0
--- /dev/null
+++ b/src/Notesy.Api/Views/web.config
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Notesy.Api/Web.Debug.config b/src/Notesy.Api/Web.Debug.config
new file mode 100644
index 0000000..f7c5612
--- /dev/null
+++ b/src/Notesy.Api/Web.Debug.config
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Notesy.Api/Web.Release.config b/src/Notesy.Api/Web.Release.config
new file mode 100644
index 0000000..52c6bbe
--- /dev/null
+++ b/src/Notesy.Api/Web.Release.config
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Notesy.Api/Web.config b/src/Notesy.Api/Web.config
new file mode 100644
index 0000000..2cd7529
--- /dev/null
+++ b/src/Notesy.Api/Web.config
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Notesy.Api/packages.config b/src/Notesy.Api/packages.config
new file mode 100644
index 0000000..07a0910
--- /dev/null
+++ b/src/Notesy.Api/packages.config
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Notesy.Core/Models/ApiUser.cs b/src/Notesy.Core/Models/ApiUser.cs
new file mode 100644
index 0000000..cede989
--- /dev/null
+++ b/src/Notesy.Core/Models/ApiUser.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Notesy.Core.Models
+{
+ public class ApiUser
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public string ApiKey { get; set; }
+ public string ApiSecret { get; set; }
+ public DateTime DateCreated { get; set; }
+ }
+}
diff --git a/src/Notesy.Core/Models/Note.cs b/src/Notesy.Core/Models/Note.cs
new file mode 100644
index 0000000..1b96211
--- /dev/null
+++ b/src/Notesy.Core/Models/Note.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Notesy.Core.Models
+{
+ // At this point, if we were using a true ORM we could be linking our models and defining their relationships (such as NHibernate or Entity Framework, etc). You
+ // could do some of this in code (rather than a config) with Fluent NHibernate or similar library. For a huge example, see Sharp Architecture: https://github.com/sharparchitecture/Sharp-Architecture/tree/master/Solutions/SharpArch.NHibernate
+
+ public class Note
+ {
+ public int Id { get; set; }
+ public string Title { get; set; }
+ public string Description { get; set; }
+ public bool IsComplete { get; set; }
+ public int ApiUserId { get; set; } // this is where we could define an ORM relation ship... it'd be something more like: public ApiUser ApiUser { get; set; }, though it would need to be "glued" together with a true ORM
+ public DateTime DateCreated { get; set; }
+
+ public Note()
+ {
+ IsComplete = false; // this defaults to false but let's be explicit
+ DateCreated = DateTime.UtcNow;
+ }
+ }
+}
diff --git a/src/Notesy.Core/Notesy.Core.csproj b/src/Notesy.Core/Notesy.Core.csproj
new file mode 100644
index 0000000..e4c356a
--- /dev/null
+++ b/src/Notesy.Core/Notesy.Core.csproj
@@ -0,0 +1,67 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {5DD8FFDA-34C8-4F81-8740-20E29BFC4E90}
+ Library
+ Properties
+ Notesy.Core
+ Notesy.Core
+ v4.5
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\Dapper.1.29\lib\net45\Dapper.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Notesy.Core/Properties/AssemblyInfo.cs b/src/Notesy.Core/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..9ce2c37
--- /dev/null
+++ b/src/Notesy.Core/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("notesy")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("notesy")]
+[assembly: AssemblyCopyright("Copyright © 2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("90973b19-9c9b-4347-9217-bd533cea0d45")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/Notesy.Core/Services/Concrete/ApiUserService.cs b/src/Notesy.Core/Services/Concrete/ApiUserService.cs
new file mode 100644
index 0000000..9c3a5e0
--- /dev/null
+++ b/src/Notesy.Core/Services/Concrete/ApiUserService.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Data.SqlClient;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Dapper;
+using Notesy.Core.Models;
+using Notesy.Core.Services.Interfaces;
+
+namespace Notesy.Core.Services.Concrete
+{
+ public class ApiUserService : IApiUserService
+ {
+ public ApiUser GetApiUser(int id)
+ {
+ var output = new ApiUser();
+
+ var sql = @"SELECT
+ [Id]
+ ,[Name]
+ ,[ApiKey]
+ ,[ApiSecret]
+ ,[DateCreated]
+ FROM
+ [dbo].[ApiUser]
+ WHERE
+ [Id] = @Id";
+
+ using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["NOTESY_CORE"].ConnectionString))
+ {
+ connection.Open();
+ var users = connection.Query(sql, new { Id = id });
+ output = users.First();
+ }
+
+ return output;
+ }
+
+ public ApiUser SaveApiUser(ApiUser input)
+ {
+ var output = new ApiUser();
+
+ var insert = @"INSERT INTO
+ [dbo].[ApiUser]
+ ([Name]
+ ,[ApiKey]
+ ,[ApiSecret]
+ ,[DateCreated])
+ VALUES
+ (@Name,
+ @ApiKey,
+ @ApiSecret,
+ @DateCreated)
+
+ SELECT SCOPE_IDENTITY()";
+
+ var update = @"UPDATE
+ [dbo].[ApiUser]
+ SET
+ [Name] = @Name,
+ [ApiKey] = @ApiKey,
+ [ApiSecret] = @ApiSecret,
+ [DateCreated] = @DateCreated
+ WHERE
+ [Id] = @Id";
+
+ if (input.Id == 0)
+ {
+ // insert
+ using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["NOTESY_CORE"].ConnectionString))
+ {
+ connection.Open();
+ var result = connection.Query(insert, new { Name = input.Name, ApiKey = input.ApiKey, ApiSecret = input.ApiSecret, DateCreated = input.DateCreated });
+ output = input;
+ output.Id = result.First();
+ }
+ }
+ else
+ {
+ // update
+ using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["NOTESY_CORE"].ConnectionString))
+ {
+ connection.Open();
+ var result = connection.Execute(update, new { Name = input.Name, ApiKey = input.ApiKey, ApiSecret = input.ApiSecret, DateCreated = input.DateCreated });
+ output = input;
+ }
+ }
+
+ return output;
+ }
+
+ public ApiUser DeleteApiUser(int id)
+ {
+ var output = GetApiUser(id);
+
+ var sql = @"DELETE FROM
+ [dbo].[ApiUser]
+ WHERE
+ [Id] = @Id";
+
+ using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["NOTESY_CORE"].ConnectionString))
+ {
+ connection.Open();
+ var result = connection.Execute(sql, new { Id = id });
+ }
+
+ return output;
+ }
+
+
+ public ApiUser GetApiUserByApiKey(string apiKey)
+ {
+ var output = new ApiUser();
+
+ var sql = @"SELECT
+ [Id]
+ ,[Name]
+ ,[ApiKey]
+ ,[ApiSecret]
+ ,[DateCreated]
+ FROM
+ [dbo].[ApiUser]
+ WHERE
+ [ApiKey] = @ApiKey";
+
+ using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["NOTESY_CORE"].ConnectionString))
+ {
+ connection.Open();
+ var result = connection.Execute(sql, new { ApiKey = apiKey });
+ }
+
+ return output;
+ }
+ }
+}
diff --git a/src/Notesy.Core/Services/Concrete/NoteService.cs b/src/Notesy.Core/Services/Concrete/NoteService.cs
new file mode 100644
index 0000000..79f0f11
--- /dev/null
+++ b/src/Notesy.Core/Services/Concrete/NoteService.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Data.SqlClient;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Dapper;
+using Notesy.Core.Models;
+using Notesy.Core.Services.Interfaces;
+
+namespace Notesy.Core.Services.Concrete
+{
+ public class NoteService : INoteService
+ {
+ // https://github.com/StackExchange/dapper-dot-net#performance-of-select-mapping-over-500-iterations---poco-serialization
+ // Not exactly the best reason but there is something to say about dapper given it's simplicity and speed. We'll use it here.
+ // At this point, we could also just connect to a cloud service or any other data store and the rest of the app wouldn't need to know. It doesn't
+ // have to be a relational database.
+
+ // ASSUMPTIONS:
+ // * All incoming inputs are valid.
+ // * New objects have an id of 0.
+
+ public Note GetNote(int id)
+ {
+ var output = new Note();
+
+ // In most cases, you'd want to at least consider sprocs here rather than raw sql.
+ var sql = @"SELECT
+ [Id]
+ ,[Title]
+ ,[Description]
+ ,[IsComplete]
+ ,[ApiUserId]
+ ,[DateCreated]
+ FROM
+ [dbo].[Note]
+ WHERE
+ [Id] = @Id";
+
+ using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["NOTESY_CORE"].ConnectionString))
+ {
+ connection.Open();
+ var notes = connection.Query(sql, new { Id = id });
+ output = notes.First(); // TODO: lots to talk about in this line of code
+ }
+
+ return output;
+ }
+
+ public Note SaveNote(Note input)
+ {
+ var output = new Note();
+
+ var insert = @"INSERT INTO
+ [dbo].[Note]
+ ([Title]
+ ,[Description]
+ ,[IsComplete]
+ ,[ApiUserId]
+ ,[DateCreated])
+ VALUES
+ (@Title,
+ @Description,
+ @IsComplete,
+ @ApiUserId,
+ @DateCreated)
+
+ SELECT SCOPE_IDENTITY()";
+
+ var update = @"UPDATE
+ [dbo].[Note]
+ SET
+ [Title] = @Title,
+ [Description] = @Description,
+ [IsComplete] = @IsComplete,
+ [ApiUserId] = @ApiUserId,
+ [DateCreated] = @DateCreated
+ WHERE
+ [Id] = @Id";
+
+ if (input.Id == 0)
+ {
+ // insert
+ using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["NOTESY_CORE"].ConnectionString))
+ {
+ connection.Open();
+ var result = connection.Query(insert, new { Title = input.Title, Description = input.Description, IsComplete = input.IsComplete, ApiUserId = input.ApiUserId, DateCreated = input.DateCreated });
+ output = input;
+ output.Id = result.First();
+ }
+ }
+ else
+ {
+ // update
+ using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["NOTESY_CORE"].ConnectionString))
+ {
+ connection.Open();
+ var result = connection.Execute(update, new { Id = input.Id, Title = input.Title, Description = input.Description, IsComplete = input.IsComplete, ApiUserId = input.ApiUserId, DateCreated = input.DateCreated });
+ output = input;
+ }
+ }
+
+ return output;
+ }
+
+ public Note DeleteNote(int id)
+ {
+ var output = GetNote(id);
+
+ var sql = @"DELETE FROM
+ [dbo].[Note]
+ WHERE
+ [Id] = @Id";
+
+ using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["NOTESY_CORE"].ConnectionString))
+ {
+ connection.Open();
+ var result = connection.Execute(sql, new { Id = id });
+ }
+
+ return output;
+ }
+ }
+}
diff --git a/src/Notesy.Core/Services/Interfaces/IApiUserService.cs b/src/Notesy.Core/Services/Interfaces/IApiUserService.cs
new file mode 100644
index 0000000..cdb950a
--- /dev/null
+++ b/src/Notesy.Core/Services/Interfaces/IApiUserService.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Notesy.Core.Models;
+
+namespace Notesy.Core.Services.Interfaces
+{
+ public interface IApiUserService
+ {
+ ApiUser GetApiUser(int id);
+ ApiUser SaveApiUser(ApiUser input);
+ ApiUser DeleteApiUser(int id);
+ ApiUser GetApiUserByApiKey(string apiKey);
+ }
+}
diff --git a/src/Notesy.Core/Services/Interfaces/INoteService.cs b/src/Notesy.Core/Services/Interfaces/INoteService.cs
new file mode 100644
index 0000000..ad2e07c
--- /dev/null
+++ b/src/Notesy.Core/Services/Interfaces/INoteService.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Notesy.Core.Models;
+
+namespace Notesy.Core.Services.Interfaces
+{
+ public interface INoteService
+ {
+ Note GetNote(int id);
+ Note SaveNote(Note input);
+ Note DeleteNote(int id);
+ }
+}
diff --git a/src/Notesy.Core/Services/Stubs/ApiUserServiceStub.cs b/src/Notesy.Core/Services/Stubs/ApiUserServiceStub.cs
new file mode 100644
index 0000000..3b109e1
--- /dev/null
+++ b/src/Notesy.Core/Services/Stubs/ApiUserServiceStub.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Notesy.Core.Models;
+using Notesy.Core.Services.Interfaces;
+
+namespace Notesy.Core.Services.Stubs
+{
+ public class ApiUserServiceStub : IApiUserService
+ {
+ private string API_KEY = "BEF0A331-C388-43D2-96A0-6B94838F87E0";
+ private string API_SECRET = "01D25503-C5C4-46A3-B99A-8BE8BB2AF0D7";
+
+ public ApiUser GetApiUser(int id)
+ {
+ return new ApiUser()
+ {
+ ApiKey = API_KEY,
+ ApiSecret = API_SECRET,
+ Id = id,
+ Name = "Famous Dave"
+ };
+ }
+
+ public ApiUser SaveApiUser(ApiUser input)
+ {
+ // if id = 0 (or null if you're using a nullable type) then this is new so insert, otherwise it's an update (aka an upsert)
+ return input;
+ }
+
+ public ApiUser DeleteApiUser(int id)
+ {
+ return new ApiUser()
+ {
+ ApiKey = API_KEY,
+ ApiSecret = API_SECRET,
+ Id = id,
+ Name = "Famous Dave"
+ };
+ }
+
+ public ApiUser GetApiUserByApiKey(string apiKey)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/Notesy.Core/Services/Stubs/NoteServiceStub.cs b/src/Notesy.Core/Services/Stubs/NoteServiceStub.cs
new file mode 100644
index 0000000..6ad6f5e
--- /dev/null
+++ b/src/Notesy.Core/Services/Stubs/NoteServiceStub.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Notesy.Core.Models;
+using Notesy.Core.Services.Interfaces;
+
+namespace Notesy.Core.Services.Stubs
+{
+ public class NoteServiceStub : INoteService
+ {
+ public Note GetNote(int id)
+ {
+ return new Note()
+ {
+ Id = id,
+ Title = "Go to the Galaxy Game.",
+ Description = "Head over to Stub Hub Center in Carson, California for a lovely game of fútbol."
+ };
+ }
+
+ public Note SaveNote(Note input)
+ {
+ if (input.Id == 0) { input.Id = 456; }
+
+ return input;
+ }
+
+ public Note DeleteNote(int id)
+ {
+ return new Note()
+ {
+ Id = id,
+ Title = "Go to the Galaxy Game.",
+ Description = "Head over to Stub Hub Center in Carson, California for a lovely game of fútbol."
+ };
+ }
+ }
+}
diff --git a/src/Notesy.Core/packages.config b/src/Notesy.Core/packages.config
new file mode 100644
index 0000000..82a4f88
--- /dev/null
+++ b/src/Notesy.Core/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/Notesy.Database/Notesy.Database.sln b/src/Notesy.Database/Notesy.Database.sln
new file mode 100644
index 0000000..f11f3ef
--- /dev/null
+++ b/src/Notesy.Database/Notesy.Database.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "Notesy.Database", "Notesy.Database.sqlproj", "{99A761F7-FBAF-435A-A468-AACC24A7F4E9}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {99A761F7-FBAF-435A-A468-AACC24A7F4E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {99A761F7-FBAF-435A-A468-AACC24A7F4E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {99A761F7-FBAF-435A-A468-AACC24A7F4E9}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {99A761F7-FBAF-435A-A468-AACC24A7F4E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {99A761F7-FBAF-435A-A468-AACC24A7F4E9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {99A761F7-FBAF-435A-A468-AACC24A7F4E9}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/Notesy.Database/Notesy.Database.sqlproj b/src/Notesy.Database/Notesy.Database.sqlproj
new file mode 100644
index 0000000..af4f860
--- /dev/null
+++ b/src/Notesy.Database/Notesy.Database.sqlproj
@@ -0,0 +1,69 @@
+
+
+
+
+ Debug
+ AnyCPU
+ Notesy.Database
+ 2.0
+ 4.1
+ {99a761f7-fbaf-435a-a468-aacc24a7f4e9}
+ Microsoft.Data.Tools.Schema.Sql.Sql110DatabaseSchemaProvider
+ Database
+
+
+ Notesy.Database
+ Notesy.Database
+ 1033,CI
+ BySchemaAndSchemaType
+ True
+ v4.5
+ CS
+ Properties
+ False
+ True
+ True
+ SQL_Latin1_General_CP1_CI_AS
+ PRIMARY
+
+
+ bin\Release\
+ $(MSBuildProjectName).sql
+ False
+ pdbonly
+ true
+ false
+ true
+ prompt
+ 4
+
+
+ bin\Debug\
+ $(MSBuildProjectName).sql
+ false
+ true
+ full
+ false
+ true
+ true
+ prompt
+ 4
+
+
+
+ 10.0
+
+ True
+ 10.0
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Notesy.Database/dbo/Tables/ApiUser.sql b/src/Notesy.Database/dbo/Tables/ApiUser.sql
new file mode 100644
index 0000000..902375b
--- /dev/null
+++ b/src/Notesy.Database/dbo/Tables/ApiUser.sql
@@ -0,0 +1,9 @@
+CREATE TABLE [dbo].[ApiUser] (
+ [Id] INT IDENTITY (1, 1) NOT NULL,
+ [Name] NVARCHAR (256) NOT NULL,
+ [ApiKey] NVARCHAR (1024) NOT NULL,
+ [ApiSecret] NVARCHAR (1024) NOT NULL,
+ [DateCreated] DATETIME CONSTRAINT [DF_ApiUser_DateCreated] DEFAULT (getutcdate()) NOT NULL,
+ CONSTRAINT [PK_ApiUser] PRIMARY KEY CLUSTERED ([Id] ASC)
+);
+
diff --git a/src/Notesy.Database/dbo/Tables/Note.sql b/src/Notesy.Database/dbo/Tables/Note.sql
new file mode 100644
index 0000000..f408b2d
--- /dev/null
+++ b/src/Notesy.Database/dbo/Tables/Note.sql
@@ -0,0 +1,11 @@
+CREATE TABLE [dbo].[Note] (
+ [Id] INT IDENTITY (1, 1) NOT NULL,
+ [Title] NVARCHAR (512) NULL,
+ [Description] NVARCHAR (2048) NULL,
+ [IsComplete] BIT CONSTRAINT [DF_Note_IsComplete] DEFAULT ((0)) NOT NULL,
+ [ApiUserId] INT NOT NULL,
+ [DateCreated] DATETIME CONSTRAINT [DF_Note_DateCreated] DEFAULT (getutcdate()) NOT NULL,
+ CONSTRAINT [PK_Note] PRIMARY KEY CLUSTERED ([Id] ASC),
+ CONSTRAINT [FK_Note_ApiUser] FOREIGN KEY ([ApiUserId]) REFERENCES [dbo].[ApiUser] ([Id])
+);
+
diff --git a/src/Notesy.Tests/Api/ApiHelperTests.cs b/src/Notesy.Tests/Api/ApiHelperTests.cs
new file mode 100644
index 0000000..9a8ce35
--- /dev/null
+++ b/src/Notesy.Tests/Api/ApiHelperTests.cs
@@ -0,0 +1,50 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Notesy.Api.Controllers; // helper in a controler... starting to get wonky
+using Notesy.Core.Models;
+
+namespace Notesy.Tests.Api
+{
+ [TestClass]
+ public class ApiHelperTests
+ {
+ [TestMethod]
+ public void TestDoSignaturesMatch()
+ {
+ int id = 1;
+ string apikey = "BEF0A331-C388-43D2-96A0-6B94838F87E0";
+ int? callId = 123;
+ string apisecret = "01D25503-C5C4-46A3-B99A-8BE8BB2AF0D7";
+
+ string inputs = string.Format("{0}{1}{2}{3}", id, apikey, callId, apisecret);
+ string signature = ApiHelper.GetHash(inputs);
+ var user = new ApiUser()
+ {
+ Id = id,
+ ApiKey = apikey,
+ ApiSecret = apisecret,
+ Name = "Does not matter"
+ };
+
+ var result = ApiHelper.DoSignaturesMatch(user, signature, id.ToString(), apikey, callId.ToString());
+
+ Assert.IsTrue(result);
+ }
+
+ [TestMethod]
+ public void TestGetHash()
+ {
+ int id = 1;
+ string apikey = "BEF0A331-C388-43D2-96A0-6B94838F87E0";
+ int? callId = 123;
+ string apisecret = "01D25503-C5C4-46A3-B99A-8BE8BB2AF0D7";
+
+ string inputs = string.Format("{0}{1}{2}{3}", id, apikey, callId, apisecret);
+ string signature = ApiHelper.GetHash(inputs);
+
+ Assert.IsNotNull(signature);
+ Assert.AreEqual("9660A2B557FB0E3D8110C69F197944EBECCD8FFB", signature);
+ }
+ }
+}
diff --git a/src/Notesy.Tests/App.config b/src/Notesy.Tests/App.config
new file mode 100644
index 0000000..1801f64
--- /dev/null
+++ b/src/Notesy.Tests/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Notesy.Tests/Core/Services/NoteServiceTests.cs b/src/Notesy.Tests/Core/Services/NoteServiceTests.cs
new file mode 100644
index 0000000..1a3ac3c
--- /dev/null
+++ b/src/Notesy.Tests/Core/Services/NoteServiceTests.cs
@@ -0,0 +1,25 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Notesy.Core.Models;
+using Notesy.Core.Services.Concrete;
+
+namespace Notesy.Tests.Core.Services
+{
+ [TestClass]
+ public class NoteServiceTests
+ {
+ private NoteService svc = new NoteService();
+
+ [TestMethod]
+ public void TestNoteInsert()
+ {
+ var n = new Note() { ApiUserId = 1, Description = "Go to Santa Monica Pier and watch Dr. Who", Title = string.Format("[{0}] Dr Who @ the beach.", DateTime.UtcNow.Ticks) };
+
+ var result = svc.SaveNote(n);
+
+ Assert.IsNotNull(result);
+ Assert.IsTrue(result.Id != 0);
+ }
+ }
+}
diff --git a/src/Notesy.Tests/Notesy.Tests.csproj b/src/Notesy.Tests/Notesy.Tests.csproj
new file mode 100644
index 0000000..416f251
--- /dev/null
+++ b/src/Notesy.Tests/Notesy.Tests.csproj
@@ -0,0 +1,98 @@
+
+
+
+ Debug
+ AnyCPU
+ {AE724AF0-7C54-427D-87F8-AD0E9056EE25}
+ Library
+ Properties
+ Notesy.Tests
+ Notesy.Tests
+ v4.5
+ 512
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 10.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
+ False
+ UnitTest
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {291d09db-7dcb-41db-b634-942b1f8bc545}
+ Notesy.Api
+
+
+ {5dd8ffda-34c8-4f81-8740-20e29bfc4e90}
+ Notesy.Core
+
+
+
+
+
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Notesy.Tests/Properties/AssemblyInfo.cs b/src/Notesy.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..f6a834a
--- /dev/null
+++ b/src/Notesy.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Notesy.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Notesy.Tests")]
+[assembly: AssemblyCopyright("Copyright © 2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("94535b77-6e32-47d2-bd23-959d5aaa29ef")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/Notesy.Web/App_Start/RouteConfig.cs b/src/Notesy.Web/App_Start/RouteConfig.cs
new file mode 100644
index 0000000..ac4ee7d
--- /dev/null
+++ b/src/Notesy.Web/App_Start/RouteConfig.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using System.Web.Mvc;
+using System.Web.Routing;
+
+namespace Notesy.Web
+{
+ public class RouteConfig
+ {
+ public static void RegisterRoutes(RouteCollection routes)
+ {
+ routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
+
+ routes.MapRoute(
+ name: "Default",
+ url: "{controller}/{action}/{id}",
+ defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
+ );
+ }
+ }
+}
diff --git a/src/Notesy.Web/Global.asax b/src/Notesy.Web/Global.asax
new file mode 100644
index 0000000..5dbaa50
--- /dev/null
+++ b/src/Notesy.Web/Global.asax
@@ -0,0 +1 @@
+<%@ Application Codebehind="Global.asax.cs" Inherits="Notesy.Web.MvcApplication" Language="C#" %>
diff --git a/src/Notesy.Web/Global.asax.cs b/src/Notesy.Web/Global.asax.cs
new file mode 100644
index 0000000..c5b4c06
--- /dev/null
+++ b/src/Notesy.Web/Global.asax.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+using System.Web.Mvc;
+using System.Web.Routing;
+
+namespace Notesy.Web
+{
+ public class MvcApplication : System.Web.HttpApplication
+ {
+ protected void Application_Start()
+ {
+ AreaRegistration.RegisterAllAreas();
+ RouteConfig.RegisterRoutes(RouteTable.Routes);
+ }
+ }
+}
diff --git a/src/Notesy.Web/Notesy.Web.csproj b/src/Notesy.Web/Notesy.Web.csproj
new file mode 100644
index 0000000..4e87ddd
--- /dev/null
+++ b/src/Notesy.Web/Notesy.Web.csproj
@@ -0,0 +1,141 @@
+
+
+
+
+ Debug
+ AnyCPU
+
+
+ 2.0
+ {7CAD7D06-49CF-4143-BBB0-1A884EC20D57}
+ {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}
+ Library
+ Properties
+ Notesy.Web
+ Notesy.Web
+ v4.5
+ true
+
+
+
+
+
+
+ true
+ full
+ false
+ bin\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll
+
+
+ ..\packages\Microsoft.AspNet.Razor.3.0.0\lib\net45\System.Web.Razor.dll
+
+
+ ..\packages\Microsoft.AspNet.Webpages.3.0.0\lib\net45\System.Web.Webpages.dll
+
+
+ ..\packages\Microsoft.AspNet.Webpages.3.0.0\lib\net45\System.Web.Webpages.Deployment.dll
+
+
+ ..\packages\Microsoft.AspNet.Webpages.3.0.0\lib\net45\System.Web.Webpages.Razor.dll
+
+
+ ..\packages\Microsoft.AspNet.Webpages.3.0.0\lib\net45\System.Web.Helpers.dll
+
+
+ ..\packages\Microsoft.AspNet.Mvc.5.0.0\lib\net45\System.Web.Mvc.dll
+
+
+
+
+
+
+
+
+
+ Global.asax
+
+
+
+
+
+
+
+ Web.config
+
+
+ Web.config
+
+
+
+
+
+
+
+
+ 10.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+
+
+
+
+ True
+ True
+ 0
+ /
+ http://localhost:63163/
+ False
+ False
+
+
+ False
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Notesy.Web/Properties/AssemblyInfo.cs b/src/Notesy.Web/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..be59c62
--- /dev/null
+++ b/src/Notesy.Web/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Notesy.Web")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Notesy.Web")]
+[assembly: AssemblyCopyright("Copyright © 2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("eb19e232-c2d1-46aa-b227-d5acba68b72d")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/Notesy.Web/Views/web.config b/src/Notesy.Web/Views/web.config
new file mode 100644
index 0000000..6dd92c5
--- /dev/null
+++ b/src/Notesy.Web/Views/web.config
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Notesy.Web/Web.Debug.config b/src/Notesy.Web/Web.Debug.config
new file mode 100644
index 0000000..f7c5612
--- /dev/null
+++ b/src/Notesy.Web/Web.Debug.config
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Notesy.Web/Web.Release.config b/src/Notesy.Web/Web.Release.config
new file mode 100644
index 0000000..52c6bbe
--- /dev/null
+++ b/src/Notesy.Web/Web.Release.config
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Notesy.Web/Web.config b/src/Notesy.Web/Web.config
new file mode 100644
index 0000000..67ea100
--- /dev/null
+++ b/src/Notesy.Web/Web.config
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Notesy.Web/packages.config b/src/Notesy.Web/packages.config
new file mode 100644
index 0000000..57f2336
--- /dev/null
+++ b/src/Notesy.Web/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/notesy.sln b/src/notesy.sln
new file mode 100644
index 0000000..2e240d3
--- /dev/null
+++ b/src/notesy.sln
@@ -0,0 +1,38 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Notesy.Web", "Notesy.Web\Notesy.Web.csproj", "{7CAD7D06-49CF-4143-BBB0-1A884EC20D57}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Notesy.Api", "Notesy.Api\Notesy.Api.csproj", "{291D09DB-7DCB-41DB-B634-942B1F8BC545}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Notesy.Core", "Notesy.Core\Notesy.Core.csproj", "{5DD8FFDA-34C8-4F81-8740-20E29BFC4E90}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Notesy.Tests", "Notesy.Tests\Notesy.Tests.csproj", "{AE724AF0-7C54-427D-87F8-AD0E9056EE25}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {7CAD7D06-49CF-4143-BBB0-1A884EC20D57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7CAD7D06-49CF-4143-BBB0-1A884EC20D57}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7CAD7D06-49CF-4143-BBB0-1A884EC20D57}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7CAD7D06-49CF-4143-BBB0-1A884EC20D57}.Release|Any CPU.Build.0 = Release|Any CPU
+ {291D09DB-7DCB-41DB-B634-942B1F8BC545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {291D09DB-7DCB-41DB-B634-942B1F8BC545}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {291D09DB-7DCB-41DB-B634-942B1F8BC545}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {291D09DB-7DCB-41DB-B634-942B1F8BC545}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5DD8FFDA-34C8-4F81-8740-20E29BFC4E90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5DD8FFDA-34C8-4F81-8740-20E29BFC4E90}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5DD8FFDA-34C8-4F81-8740-20E29BFC4E90}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5DD8FFDA-34C8-4F81-8740-20E29BFC4E90}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AE724AF0-7C54-427D-87F8-AD0E9056EE25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AE724AF0-7C54-427D-87F8-AD0E9056EE25}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AE724AF0-7C54-427D-87F8-AD0E9056EE25}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AE724AF0-7C54-427D-87F8-AD0E9056EE25}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal