-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
First commit
- Loading branch information
0 parents
commit 7882bf2
Showing
77 changed files
with
7,463 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2021 Ludovic, Jean, Michel Jarno | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
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 THE | ||
AUTHORS OR COPYRIGHT HOLDERS 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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
# All-In-One CRUD | ||
|
||
```bash | ||
git clone https://github.com/ludojmj/svelte-netcore-identity.git | ||
``` | ||
|
||
The aim of this project is to gather, in a single place, useful front and back ends development tools: | ||
|
||
- A Database with SQLite; | ||
- A Web API server with .NET Core 5.x; | ||
- A Svelte JS client App; | ||
- A link to an external service for identity management, authorization, and API security. | ||
|
||
## Quick start (Development) | ||
|
||
Server | ||
|
||
```bash | ||
cd <myfolder>/Server | ||
dotnet run | ||
``` | ||
|
||
Client | ||
|
||
```bash | ||
cd <myfolder>/client | ||
npm install | ||
npm run dev | ||
``` | ||
|
||
--- | ||
|
||
## Inspiration | ||
|
||
- SQLite database powered by: <https://www.sqlite.org> | ||
- Server based on API mechanisms of: <https://reqres.in/api/whatever> | ||
- Svelte template client borrowed from: <https://github.com/sveltejs/template.git> | ||
- Identity service powered by: <https://demo.identityserver.io> | ||
- Identity client borrowed from: <https://github.com/dopry/svelte-oidc> | ||
- CSS borrowed from: <https://getbootstrap.com> | ||
- SVG borrowed from: <https://creativecommons.org> | ||
|
||
--- | ||
|
||
## Manufacturing process steps | ||
|
||
### >>>>> SQLite database | ||
|
||
#### Overwrite database if needed | ||
|
||
```bash | ||
cd <myfolder> | ||
sqlite3 Server/App_Data/stuff.db < Server/App_Data/create_tables.sql | ||
``` | ||
|
||
### >>>>> .NET Core 5.x Web API server | ||
|
||
#### Create the server project | ||
|
||
```bash | ||
cd <myfolder> | ||
dotnet new gitignore | ||
dotnet new webapi -n Server | ||
dotnet new xunit -n Server.UnitTest | ||
``` | ||
|
||
#### Generate the model from the database for the Web API server | ||
|
||
```bash | ||
dotnet tool install --global dotnet-ef | ||
cd <myfolder>/Server | ||
dotnet ef dbcontext scaffold "Data Source=App_Data/stuff.db" Microsoft.EntityFrameworkCore.Sqlite \ | ||
--output-dir DbModels --context-dir DbModels --context StuffDbContext --force | ||
``` | ||
|
||
#### Run the tests | ||
|
||
```bash | ||
cd <myfolder>/Server.UnitTest | ||
dotnet restore | ||
dotnet build | ||
dotnet test | ||
dotnet test /p:CollectCoverage=true | ||
``` | ||
|
||
#### Run the Web API server | ||
|
||
```bash | ||
cd <myfolder>/Server | ||
export ASPNETCORE_ENVIRONMENT=Development | ||
dotnet run | ||
``` | ||
|
||
### >>>>> Svelte client App | ||
|
||
#### Create the client project | ||
|
||
```bash | ||
cd <myfolder> | ||
git clone https://github.com/sveltejs/template.git | ||
mv template myApp | ||
``` | ||
|
||
#### Run the client App | ||
|
||
```bash | ||
cd <myfolder>/client | ||
npm install | ||
npm run dev | ||
``` | ||
|
||
#### Possibly run a standalone version of the client App (mocking) except identity server | ||
|
||
At the second line of the file: | ||
|
||
> ```<myfolder>/client/src/api/stuff.js``` | ||
Swap isProd to !isProd: | ||
|
||
> const rootApi = !isProd ? "https://localhost:5001/api/stuff" : "http://localhost:3000/mock/stuff"; | ||
--- | ||
|
||
## Troubleshooting | ||
|
||
### _An error occured. Please try again later._ | ||
|
||
**When?** | ||
|
||
- Creating a record in the SQLite database _stuff.db_ running Linux on Azure; | ||
- The "real" error (not displayed in Production) is: _SQLite Error 5: 'database is locked'_; | ||
- There is a restricted write access to the file on Linux web app when running on Azure. | ||
|
||
**How to solve:** | ||
|
||
- ==> Either use a real database or deploy the web app on Azure choosing Windows OS. | ||
|
||
### _SQLite Error 1: 'no such table: t_stuff'_ | ||
|
||
**When?** | ||
|
||
- Running the Svelte client App (```npm run dev```); | ||
- Connecting to: <http://localhost:3000/>. | ||
|
||
**How to solve:** | ||
|
||
- ==> Create the database _stuff.db_ (```sqlite3 Server/App_Data/stuff.db < Server/App_Data/create_tables.sql```). | ||
|
||
### _Network Error_ | ||
|
||
**When?** | ||
|
||
- Running the Svelte client App (```npm run dev```); | ||
- Connecting to: <http://localhost:3000/>. | ||
|
||
**How to solve:** | ||
|
||
- ==> Start the .NET Core server (```dotnet run```) before the Svelte client App (```npm run dev```). | ||
|
||
### _Your connection is not private_ (NET::ERR_CERT_AUTHORITY_INVALID) | ||
|
||
**When?** | ||
|
||
- Running the .NET Core server (dotnet run); | ||
- Connecting to: <http://localhost:5000/swagger>; | ||
- Or connecting to its redirection: <https://localhost:5001/swagger>. | ||
|
||
**How to solve:** | ||
|
||
- ==> Click "Advanced settings" button; | ||
- ==> Click on the link to continue to the assumed unsafe localhost site; | ||
- ==> Accept self-signed localhost certificate. | ||
|
||
### _You do not have permission to view this directory or page._ | ||
|
||
**When?** | ||
|
||
- Browsing the web site on a Azure Windows instance. | ||
|
||
**How to solve:** | ||
|
||
- ==> Add the web.config file since you've got IIS running; | ||
- ==> On Linux, the web.config file is useless | ||
(Update your http headers according to the suitable Web Server configuration file). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
using System.Collections.ObjectModel; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Moq; | ||
using Xunit; | ||
using Server.Controllers; | ||
using Server.Models; | ||
using Server.Repository.Interfaces; | ||
|
||
namespace Server.UnitTest.Controllers | ||
{ | ||
public class StuffControllerTest | ||
{ | ||
private static readonly UserModel CurrentUserModelTest = new UserModel() | ||
{ | ||
Id = "11", | ||
Name = "GivenName FamilyName", | ||
GivenName = "GivenName", | ||
FamilyName = "FamilyName", | ||
Email = "Email" | ||
}; | ||
|
||
private static readonly DatumModel TestDatum = new DatumModel | ||
{ | ||
Id = "1", | ||
Label = "Label", | ||
Description = "Description", | ||
OtherInfo = "OtherInfo", | ||
User = CurrentUserModelTest | ||
}; | ||
|
||
private static readonly StuffModel TestStuff = new StuffModel | ||
{ | ||
DatumList = new Collection<DatumModel> | ||
{ | ||
TestDatum | ||
} | ||
}; | ||
|
||
// ***** ***** ***** LIST | ||
[Fact] | ||
public async Task StuffController_GetStuffList_ShouldReturn_Ok() | ||
{ | ||
// Arrange | ||
var mockStuffRepo = Mock.Of<IStuffRepo>(x => x.GetListAsync(1) == Task.FromResult(TestStuff)); | ||
var controller = new StuffController(mockStuffRepo); | ||
int existingPage = 1; | ||
|
||
// Act | ||
IActionResult actionResult = await controller.GetList(existingPage, null); | ||
|
||
// Assert | ||
var okResult = Assert.IsType<OkObjectResult>(actionResult); | ||
var contentResult = Assert.IsType<StuffModel>(okResult.Value); | ||
var expected = TestDatum.Id; | ||
var actual = contentResult.DatumList.ToArray()[0].Id; | ||
Assert.Equal(expected, actual); | ||
} | ||
|
||
// ***** ***** ***** SEARCH | ||
[Fact] | ||
public async Task StuffController_SearchStuffList_ShouldReturn_Ok() | ||
{ | ||
// Arrange | ||
var mockStuffRepo = Mock.Of<IStuffRepo>(x => x.SearchListAsync("foo") == Task.FromResult(TestStuff)); | ||
var controller = new StuffController(mockStuffRepo); | ||
|
||
// Act | ||
IActionResult actionResult = await controller.GetList(0, "foo"); | ||
|
||
// Assert | ||
var okResult = Assert.IsType<OkObjectResult>(actionResult); | ||
var contentResult = Assert.IsType<StuffModel>(okResult.Value); | ||
var expected = TestDatum.Id; | ||
var actual = contentResult.DatumList.ToArray()[0].Id; | ||
Assert.Equal(expected, actual); | ||
} | ||
|
||
// ***** ***** ***** CREATE | ||
[Fact] | ||
public async Task StuffController_Create_ShouldReturnCreated() | ||
{ | ||
// Arrange | ||
var mockStuffRepo = Mock.Of<IStuffRepo>(x => x.CreateAsync(TestDatum) == Task.FromResult(TestDatum)); | ||
var controller = new StuffController(mockStuffRepo); | ||
|
||
// Act | ||
IActionResult actionResult = await controller.Create(TestDatum); | ||
|
||
// Assert | ||
var okResult = Assert.IsType<CreatedAtActionResult>(actionResult); | ||
var contentResult = Assert.IsType<DatumModel>(okResult.Value); | ||
Assert.Equal(TestDatum.Id, contentResult.Id); | ||
} | ||
|
||
// ***** ***** ***** READ SINGLE | ||
[Fact] | ||
public async Task StuffController_Read_ShouldReturn_Ok() | ||
{ | ||
// Arrange | ||
var mockStuffRepo = Mock.Of<IStuffRepo>(x => x.ReadAsync("1") == Task.FromResult(TestDatum)); | ||
var controller = new StuffController(mockStuffRepo); | ||
|
||
// Act | ||
IActionResult actionResult = await controller.Read("1"); | ||
|
||
// Assert | ||
var okResult = Assert.IsType<OkObjectResult>(actionResult); | ||
var contentResult = Assert.IsType<DatumModel>(okResult.Value); | ||
var expected = TestDatum.Id; | ||
var actual = contentResult.Id; | ||
Assert.Equal(expected, actual); | ||
} | ||
|
||
[Fact] | ||
public async Task StuffController_Read_ShouldReturn_Null() | ||
{ | ||
// Arrange | ||
var mockUserRepo = Mock.Of<IStuffRepo>(); | ||
var controller = new StuffController(mockUserRepo); | ||
|
||
// Act | ||
IActionResult actionResult = await controller.Read("1"); | ||
|
||
// Assert | ||
var okResult = Assert.IsType<OkObjectResult>(actionResult); | ||
var contentResult = okResult.Value; | ||
StuffModel expected = null; | ||
var actual = contentResult; | ||
Assert.Equal(expected, actual); | ||
} | ||
|
||
// ***** ***** ***** UPDATE | ||
[Fact] | ||
public async Task StuffController_UpdateStuff_ShouldReturn_Ok() | ||
{ | ||
// Arrange | ||
var mockStuffRepo = Mock.Of<IStuffRepo>(x => x.UpdateAsync("1", TestDatum) == Task.FromResult(TestDatum)); | ||
var controller = new StuffController(mockStuffRepo); | ||
string existingId = "1"; | ||
|
||
// Act | ||
IActionResult actionResult = await controller.Update(existingId, TestDatum); | ||
|
||
// Assert | ||
var okResult = Assert.IsType<OkObjectResult>(actionResult); | ||
var contentResult = Assert.IsType<DatumModel>(okResult.Value); | ||
Assert.Equal(TestDatum.Id, contentResult.Id); | ||
} | ||
|
||
// ***** ***** ***** DELETE | ||
[Fact] | ||
public async Task StuffController_DeleteStuff_ShouldReturnNoContent() | ||
{ | ||
// Arrange | ||
var mockStuffRepo = Mock.Of<IStuffRepo>(); | ||
var controller = new StuffController(mockStuffRepo); | ||
string badId = "2"; | ||
|
||
// Act | ||
IActionResult actionResult = await controller.Delete(badId); | ||
|
||
// Assert | ||
Assert.IsType<NoContentResult>(actionResult); | ||
} | ||
} | ||
} |
Oops, something went wrong.