add generated markdown lectures

main
borb 1 month ago
parent f43aaafe26
commit a8cc44a7ae

@ -0,0 +1,171 @@
# Introduction to the Internet
# What is the Internet?
* Web != the Internet
* Internet: connected networks that use the TCP/IP protocol
* made out of billions of computers, cables and antennae directing data to the right direction.
* Web (www): URL-based information system.
* a big part of the Internet but it consists of other kinds of traffic, too:
* Email
* VoIP (Skype/Teamspeak)
* File transfers between computers
* Remote connections to other computers
![](imgs/1-introduction-to-the-internet_0.png)
# HTTP
* __HTTP __ (Hypertext Transfer Protocol) is a TCP/IP based communication protocol, which provides a standardized way for computers to communicate with each other
* The communication happens between a client (browser, application…) and a server
* The client sends a __request__ , and the server returns a __response__
* HTTP is __connectionless__ :
* The client and server are only aware of each other during the request-response phase
* After that, each new request is sent through a new connection
---
TCP = Transmission Control Protocol; Controlled stream of bytes between applications communicating via an IP network
IP = Internet Protocol; Delivering packets from host to destination with IP addresses
# HTTP (continued)
Server Side Script
_HTTP protocol acts as a communication bus between the web client (e.g. web browser) and the web server. There can be multiple clients connected to the server simultaneously. The clients are not connected to each other but only to the server._
# HTTP Requests
* HTTP requests have the following format:
* Request line: request method, request URI
* Example: GET [https://example.com/products](https://example.com/products)
* Headers (optional)
* Example: Date: Mon, 07 Sep 2020 08:39:33 GMT
* Example: Content-Type: application/json
* Body (optional)
* Example:
* {
* "date": "2020-09-08T11:39:34.2837813+03:00",
* "temperatureC": 11,
* "temperatureF": 51,
* "summary": "Cool"
* }
# HTTP Requests (continued)
Some example HTTP request messages:
| __Method__ | __URI__ | __Headers__ | __Body__ |
| :-: | :-: | :-: | :-: |
| __GET__ | __http://someserver.com/__ | | |
| __GET__ | __http://someserver.com/10__ | | |
| __POST__ | __http://someserver.com/users__ | __Content-Type: application/json__ | __{"name": "Dan"}__ |
| __DELETE__ | __http://someserver.com/users/2__ | | |
---
Also request header fields
# PORT numbers
__The port number (16 bit unsigned integer) defines a port through which the communication to your application happens. Ports allow multiple applications to be located on the same host (IP).__
__The client connects to this port in some server (defined by an IP address), so the client and the server are able to communicate.__
__Computers have several different ports at their disposal __ _but_ __ some of them are being used and your application might not be able to assign those ports to itself.__
__Ports from 0 to 1023 are the system ports. There are restrictions in manually assigning applications to use them.__
__Generally speaking, ports from 1024 to 65535 are available for user to assign applications to use them.__
![](imgs/1-introduction-to-the-internet_1.png)
# Back-end vs Front-end
* Web development is split into _Back-end_ and _Front-end_
* Front-end is the website users can actually see
* Visual elements of a website or app the user can interact with
* Back-end is the invisible part
* "Server side" where user's data is stored
* Back-end is not interacted with directly, but gives and modifies its stored data based on user requests
* _Full stack development_ refers to working on both back-end and front-end!
# Front-end anatomy of a website
* Historically, websites looked something like this
* [https://web.archive.org/web/19970129232848/http://www.iltalehti.fi/](https://web.archive.org/web/19970129232848/http://www.iltalehti.fi/)
* Nowadays, a website can even be a full-blown application!
* [https://open.spotify.com/](https://open.spotify.com/)
* old and new sites alike are written in _HTML_ __, __ which describes the _structure _ of the site
* In mid-90s, plain HTML was extended with _CSS_ _ _ to implement _styling_
* The third building block of a website is JavaScript, or _JS,_ which is used to add _interactivity_
# Backend
![](imgs/1-introduction-to-the-internet_2.png)
* Backend handles e.g.,
* saving and fetching user data from a _database_
* user authentication and connecting multiple users together (chat apps!)
* Databases are used with _query languages_ like _SQL_
* Back-end software can be written with various different programming languages
* even with JavaScript! See: [Express](https://expressjs.com/)
* Simple websites don't require a dedicated back-end
![](imgs/1-introduction-to-the-internet_3.png)
© Buutti Oy, All Rights Reserved
# What is a server or "the Cloud"
* A Server or "the cloud" is just another computer
* Server software is constantly running
* Usually in data centers
* Your own computer could be a server!
* When you access a website your computer calls another computer that sends you the website
* If you need to save an image, post a comment on a news website, or just load a website, you send a _request _ to a server
* Server then gives an appropriate _response_ based on the request
![](imgs/1-introduction-to-the-internet_4.png)
# APIs
__APIs __ (Application Programming Interface) define a way of communicating between applications
With the help of APIs, you don't need everything to run in one place or even create all functionality yourself!
Each program defines their own interface (if they have one), and for other programs to consume it, they have to implement it specifically
There are some architectural models like __REST__ , which exist to help make APIs more general
__API in simple terms__
Most of the work is handled by the API and not the user.
Application 1(user)
Application 2(user)
Application 3(user)
---
E.g. filtering method; should you have that in the client code or the server?
__APIs are everywhere__
![](imgs/1-introduction-to-the-internet_5.png)
![](imgs/1-introduction-to-the-internet_6.png)
![](imgs/1-introduction-to-the-internet_7.png)
Application 1(user)
![](imgs/1-introduction-to-the-internet_8.png)
![](imgs/1-introduction-to-the-internet_9.png)
![](imgs/1-introduction-to-the-internet_10.png)
![](imgs/1-introduction-to-the-internet_11.png)

@ -0,0 +1,329 @@
# ASP.NET Basics
# ASP.NET Core
* __ASP.NET Core __ is
* a framework for building internet connected applications, like
* Web apps
* Web APIs
* Backend for desktop & mobile apps
* rebuilt from ground up, NOT an update for the old ASP.NET
* open source, cross platform
* __ASP__ .NET Core supports the use of __NuGet packages__ , which can be added to your projects modularly
* In this course, we will be focusing on web APIs and won't be covering the front end development tools of ASP.NET
---
APIs are interfaces that applicaitons use to communicate with each other
Xamarin for mobile
Use of NuGet packages add modularity and decrease the minimum memory footprint of your projects
# ASP.NET Core (continued)
* Full support for C#
* Base Class Library
* Great community support
* StackOverflow
* Open projects on GitHub
* Some companies have a long history with Microsoft frameworks
* ASP.NET is the logical choice in that case
* .NET is constantly getting updates and new releases
* Learning to read documentations is key
---
BCL includes all the things you have learned so far
__Swagger & Swagger UI__
![](imgs/2-aspnet-core-basics_0.png)
Swagger/OpenAPI: Language-independent specification for describing REST APIs without needing to look at the source code
Swagger UI: Web-based UI for automatically providing information about the API (actions and their capabilities) using the specification above
The default implementation of Swagger UI in ASP.NET is called [Swashbuckle](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)
Useful for basic debugging and testing
---
https://learn.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?view=aspnetcore-7.0
# Exercise 1: Creating an ASP.NET Core Web Application
Open Visual Studio and from the right select "Create a new project"
Search for _ASP.NET_ and select _ASP.NET Core Web API_
![](imgs/2-aspnet-core-basics_1.png)
![](imgs/2-aspnet-core-basics_2.png)
---
Configure for HTTPS would give the project the add secure authentication This would be for sensitive data
# Creating an ASP.NET Core Web Application (continued)
Give a name and click "Next" in the bottom right corner. Select .NET 7.0 under "Framework". Authentication type should be "None" for now. Uncheck "Configure for HTTPS" and click "Create" in the bottom right corner.
![](imgs/2-aspnet-core-basics_3.png)
![](imgs/2-aspnet-core-basics_4.png)
---
Configure for HTTPS would give the project the add secure authentication This would be for sensitive data
The new project is now created. Start debugging from the top (the play button with the text "http").
![](imgs/2-aspnet-core-basics_5.png)
---
Configure for HTTPS would give the project the add secure authentication This would be for sensitive data
A web page should open, showing SwaggerUI for a weather forecast API. Click it open and select "Try it out". Execute the GET request and see what it returns.
![](imgs/2-aspnet-core-basics_6.png)
![](imgs/2-aspnet-core-basics_7.png)
![](imgs/2-aspnet-core-basics_8.png)
---
Configure for HTTPS would give the project the add secure authentication This would be for sensitive data
# Creating an ASP.NET Core Web Application
Close the window. Browse through the source files on Solution Explorer on the right and check where the weather forecasts come from.
![](imgs/2-aspnet-core-basics_9.png)
---
Configure for HTTPS would give the project the add secure authentication This would be for sensitive data
# ASP.NET Core Web API
In the previous exercise, we chose an API template for our new project, which have some files and dependencies already added
The weather forecasts come from "WeatherForecastController.cs" in the "Controllers" folder (more on Controllers later)
Throughout this course, the aim is to get an understanding of the underlying logic of ASP.NET Core
You can use the API template for the assignments, though
# Program.cs
The Program.cs file in ASP.NET 7 is the file where the services for the web application are configured and middleware is defined
The file starts with defining the builder for the web application
var builder = WebApplication.CreateBuilder(args);
The program is actually a console application, which also hosts a web server
By default, ASP.NET applications use Kestrel and IIS as a server: [https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-7.0](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-7.0)
# Services
* The controllers and some other components (like Swagger) are added to the application as services
* Services are components that are available anywhere within your program via dependency injection (more on that [later in the course](https://docs.google.com/presentation/u/0/d/1vDSvwMafnibrc8ZfQXlKFAm2m--3b-aUUAxQUGzTBf0/edit) )
* builder.Services.AddControllers();builder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen();
* As the comment right above it suggests, more can be added as needed
---
This will make more sense after we start adding own services
# Configuring Middleware
Handling of each HTTP request is defined as a set of middlewares
Middleware is software that's assembled into an app pipeline to handle requests and responses
They can decide whether to pass the request into the next middleware, or modify the data/request as needed.
if (app.Environment.IsDevelopment()){ app.UseSwagger(); app.UseSwaggerUI(); } app.UseAuthorization(); app.MapControllers(); app.Run();
# Routing
__Routing __ is how web APIs match the requested URI to a corresponding action
The URI:s that can be used to get a response from the API are called the __endpoints __ of the API
| __Request method__ | __Endpoint__ | __Action__ |
| :-: | :-: | :-: |
| __GET __ | __http://someserver.com/api/products__ | __GetProducts()_ |
| __GET __ | __http://someserver.com/api/products/3__ | __GetProduct(int id)_ |
| __POST __ | __http://someserver.com/api/products__ | __PostProduct()_ |
# Attributes
Attributes are a way of attaching metadata with code (classes, methods, properties, etc.)
In ASP.NET, attributes have a strong role in __routing__ :
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
//...
}
__…__
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
//...
}
Attribute routing requirement, automatic HTTP 400 response, and more
HTTP GET requests are routed to this method
URIs starting with "/weatherforecast" are routed to this class
# Attribute Routing
| _Attribute_ | _Request_ |
| :-: | :-: |
| Class: [Route("api")]Method: [HttpGet] | _GET http://localhost:54106/api_ |
| Class: [Route("api")] __ __ Method: [HttpGet("products")] | _GET http://localhost:54106/api/products_ |
| Class: [Route("api")] __ __ Method: [HttpGet("products/{id}")] | _GET http://localhost:54106/api/products/12_ |
| Class: [Route("api")] __ __ Method: [HttpPost("products")] | _POST http://localhost:54106/api/products_ |
# Exercise 2: Setting up Routes
Change the routes in WeatherForecastController.cs so that the forecast result is printed at
[http://localhost:xxxxx/](http://localhost:54106/weatherforecast)api/weatherforecast
instead of
http://localhost:xxxxx/weatherforecast
You can see the route change in the Swagger UI get method.
# Handling HttpGet Requests
We have now established how to call methods with HTTP requests
Additional parameters can be passed to the method with the URI:
[Route("api")]
// class declaration
// …
[HttpGet("list/{someText}")]
public string[] GetArrayOfStrings(string someText)
{
return Enumerable.Range(1, 5).Select(index => new string(someText))
.ToArray();
}
![](imgs/2-aspnet-core-basics_10.png)
# Handling HttpGet Requests (continued)
The URI parameters can be made optional with '?'
A default value must be then set for the method parameter:
[Route("api")]
// class declaration
// …
[HttpGet("list/{someText?}")]
public string[] GetArrayOfStrings(string someText = "default")
{
return Enumerable.Range(1, 5).Select(index => new string(someText))
.ToArray();
}
![](imgs/2-aspnet-core-basics_11.png)
Apply constraints for the parameters by setting them after ':'
If the URI doesn't fit the constraints, the response will hold a 404 status code
[HttpGet("products/{id:int}")] // Required type: int
[HttpGet("list/{value:length(3,40)}")] // Required length: 3-40
![](imgs/2-aspnet-core-basics_12.png)
# Exercise 3: Returning Your Own List
Change the Get method so that instead of returning an IEnumerable of WeatherForecast objects, it returns a List of string objects. Fill the list with e.g. names and make it as long as you want. Test with browser (Swagger UI).
Create a new method, which is routed at http://localhost:xxxxx/api/numberlist/k, where _k_ is any integer. The method should return an array of integers from 1 to _k_ . For example: http://localhost:xxxxx/api/numberlist/5 would return [1,2,3,4,5]. Test with browser (Swagger UI).
# Postman
HTTP POST requests cannot be made with the browsers location bar
In websites, POST requests are usually made with forms
In applications, all requests are sent by the client application
For testing APIs, multiple tools like __Postman __ exist
Before we cover handling POST, PUT and other requests in ASP.NET, let's first see how to make them with Postman
![](imgs/2-aspnet-core-basics_13.png)
# Postman (continued)
When developing APIs, Postman will almost always turn up in the development cycle
Postman lets you create configured HTTP requests to APIs
This is great for testing your APIs without having to write code just for that purpose
Supports all the necessary HTTP requests, like GET, POST and DELETE
# Creating Requests with Postman
* To get started, open Postman
* You can sign in or skip the login
* Close the opened window to go straight to making requests
![](imgs/2-aspnet-core-basics_14.png)
# Creating Requests with Postman (continued)
Create your request by selecting the method and entering the URL
The response with the content body and status code show up below
![](imgs/2-aspnet-core-basics_15.png)
# Exercise 4: Trying out Postman
Run the program you have worked on in the previous lecture assignments. Test both methods with Postman.

@ -0,0 +1,105 @@
# HTTP Responses & Status Codes
# HTTP Responses
* HTTP responses have the following format:
* Status line: HTTP version, status code, reason phrase
* HTTP/1.1 200 OK
* Headers (optional)
* Date: Mon, 07 Sep 2020 08:39:33 GMT
* Content-Type: application/json
* Body (optional)
* {
* "date": "2020-09-08T11:39:34.2837813+03:00",
* "temperatureC": 11,
* "temperatureF": 51,
* "summary": "Cool"
* }
# Status Codes
* The status line of HTTP a response sent by an API should accurately describe the status of what has happened on the server after each request:
* Did the operation succeed? (2xx status codes)
* Was there an error with the request, i.e. the request line was malformed, or the server doesn't support it (4xx status codes)
* Did a server side exception occur? (5xx status codes)
* This is never the client's fault
# Status Codes (continued)
Your APIs should use the following status codes for responses to CRUD operations:
| __2xx SUCCESS__ | __4xx CLIENT ERROR__ | __5xx SERVER ERROR__ |
| :-: | :-: | :-: |
| 200 - OK | 400 - Bad Request | 500 - Internal Server Error |
| 201 - Created | 401 - Unauthorized | |
| 204 - No Content | 403 - Forbidden | |
| | 404 - Not Found | |
| | 409 - Conflict | |
[https://restfulapi.net/http-status-codes/](https://restfulapi.net/http-status-codes/)
# ASP.NET Status Codes
* There are multiple possible status codes for each action
* The ControllerBase which the controllers should inherit from includes __result methods__ for creating ActionResult objects
* All ActionResult objects implement IActionResult
* Includes at least a status code, and can contain data such as view items or an error message
* This result is processed into a response to then send to client
# ASP.NET Result Methods
| __Status Code__ | __Result Method__ | __Use__ |
| :-: | :-: | :-: |
| __200 - OK__ | __Ok()_ | __GET, DELETE__ |
| __201 - Created__ | __Created()_ | __POST__ |
| __204 - No content__ | __NoContent()_ | __PUT, PATCH__ |
| __400 - Bad request__ | __BadRequest()_ | __POST, PUT, PATCH__ |
| __404 - Not found__ | __NotFound()_ | __All actions__ |
| __Custom__ | __StatusCode()_ | __All actions__ |
# ASP.NET Status Codes (continued)
[HttpGet("{id}")]
public IActionResult GetContactById(int id)
{
// Contacts = list of contact objects, fetched from some repository
var contact = Contacts.FirstOrDefault(c => c.Id == id);
if (contact == null)
{
return NotFound();
}
return Ok(contact);
}
The previous code first attempts to find a Contacts object using the ID provided in the URI parameter
If an item is not found, a 404 response is returned using NotFound()
Otherwise, the object is sent as a payload with a 200 OK code response using Ok(contact)
![](imgs/3-http-responses-and-status-codes_0.png)
![](imgs/3-http-responses-and-status-codes_1.png)
![](imgs/3-http-responses-and-status-codes_2.png)
![](imgs/3-http-responses-and-status-codes_3.png)
# Exercise 1: Returning Status Codes
Without using parameter constraints, modify your number list method from Lecture 2, Exercise 2 to return a status code 400 (Bad Request) if _k_ is smaller than one or larger than 100.
Add a helpful error message to the result for both cases. The message should be seen as a message body in the response.
Test with Swagger/Postman.

@ -0,0 +1,300 @@
# MVC Pattern & Repositories
# The MVC Pattern
* For creating APIs, we now know how to
* set up an ASP.NET Core web application,
* set up routes with attributes to connect request URIs with methods,
* respond with HTTP responses
* The problem is that all this functionality is scattered here and there around the program, meaning we have no structure to what we are doing
* In order to write production level code, your API should follow the __MVC pattern__
# The MVC Pattern (continued)
* Helps to enforce __separation of concerns__ , and induce code readability and testability
* Acronym for Model - View - Controller
* Model
* Representation of data in code
* Could also include some logic to retrieve and save the data
* View
* The data that is shown to the client, e.g. web page
* More often than not different from the Model - client does not need to (and often shouldn't) see all possible data
* Controller
* Communicates with the Model and the View
* How the data (models) will be processed before sending it forwards to the client or to the view
In an API, the pattern is implemented like this:
class User
{
public int Id {get; set;}
public string Name {get; set;}
}
Get()
Post()
...
{
"id": 1
"name": "Sonja"
}
---
The "view" in APIs is basically just JSON data
# Exercise 1. Setting up the Project
Create a new ASP.NET Core Web API template project. Name it CourseAPI. Delete WeatherForecastController.cs and WeatherForecast.cs.
Add a new controller CoursesController by right-clicking the Controllers folder and selecting _Add_ > _Controller… > API Controller > Empty_
Add a new folder Models. Inside, add a new class file ( _Add > Class…_ ) Course.cs
To the Course class, create the properties int Id, string Name and int Credits
# Exercise 2: Creating GET Endpoints
Inside the Controller class, initialize a list of courses with a couple of test courses
Create endpoints for GET requests with URIs api/courses and api/courses/{id} which return all courses and a single course with a corresponding ID, respectively
# Repositories
* When following the _separation of concerns_ principle, the controllers or models should NOT be accessing the database directly
* Instead, the web app should have some kind of repository for reading from and writing to the database
* For now, we can just create a mock repository to a new folder called "Repositories" for our needs, and make it static so that it can be accessed everywhere
* Later on, we will add services to make the repository available within our program via dependency injection
# Repository Example
public class MockRepo
{
// Replace this later on with a database context
private List<Student> Students { get; set; }
// Constructor to initialize student list
private MockRepo(){
Students = new List<Student>();
}
// Replace this later on with dependency injection
public static MockRepo Instance { get; } = new MockRepo();
public List<Student> GetStudents() => Students;
// Update database here later
// Other methods to read/write from database
// ...
}
---
At this point, implementation of the db is up to you
Later, real database is added
# Repository Example (continued)
In the controller class:
[HttpGet]
public List<Student> Get()
{
return MockRepo.Instance.GetStudents();
}
---
At this point, implementation of the db is up to you
Later, real database is added
# Exercise 3: Using a Repository
Continue working on CourseAPI.
Within the solution, create a folder called Repositories
Add a mock repository to the folder. Move the course list from the controller to the repository to simulate our database for now. Add methods to get all courses in the list and a single course by ID.
Modify the controller so that it uses the repository for getting courses.
# Services
* [Services and dependency injection](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1) (DI) have been mentioned during this course
* Recap: DI allows for loose coupling between classes and their dependencies
* This decreases complexity, makes refactoring easier and increases code testability
* DI is used in ASP.NET to distribute services to classes in a controlled way
* Let's get started by making a repository service
* Recap: Repositories are the interface for handling operations to a database
* Repositories should be accessible within the API from multiple controllers
* Logical step is to make it a service!
# Services (continued)
Create a service interface and a class implementing it:
![](imgs/4-mvc-pattern-and-repositories_0.png)
---
Repository for each model?
1 repo for all tightly coupled models
Add _all _ methods of the service to the interface:
public interface IContactRepository
{
Contact GetContact(int id);
List<Contact> GetContacts();
void AddContact(Contact contact);
void UpdateContact(int id, Contact contact);
void DeleteContact(int id);
// UpdateDataBase() later on...
}
Create the class which implements the interface:
public class ContactRepository : IContactRepository
{
// Replace this with database context in a real life application
private static List<Contact> Contacts = new List<Contact>
{
new Contact{Id=0, Name="Johannes Kantola", Email="johkant@example.com"},
new Contact{Id=1, Name="Rene Orosz", Email="rene_king@example.com"}
};
public void AddContact(Contact contact) => Contacts.Add(contact);
public void DeleteContact(int id) =>
Contacts = Contacts.Where(c => c.Id != id).ToList();
public Contact GetContact(int id) => Contacts.FirstOrDefault(c => c.Id == id);
public List<Contact> GetContacts() => Contacts;
public void UpdateContact(int id, Contact newContact) =>
Contacts = Contacts.Select(c => c.Id != id ? c : newContact).ToList();
}
The service is now ready to be added to the container in the Program.cs file:
// ...
builder.Services.AddSingleton<IContactRepository, ContactRepository>();
builder.Services.AddControllers().AddNewtonsoftJson();
// ...
Add the service to your controller by creating a constructor and passing it as a parameter:
public class ContactsController : ControllerBase
{
private readonly IContactRepository _contactRepository;
public ContactsController(IContactRepository contactRepository)
{
_contactRepository = contactRepository;
}
}
If you need to add more services to the controller later, you can just add them as a parameter as well - the order of parameters does not matter!
That's it! Your service is now ready to be consumed by the controller (no need to use the Instance anymore!)
[HttpGet("{id}")]
public IActionResult GetContactById(int id)
{
Contact contact = _contactRepository.GetContact(id);
if (contact == null)
{
return NotFound();
}
return Ok(contact);
}
# Exercise 4:
Continue working on CourseAPI.
Create an interface ICourseRepository with methods for getting a single course or all courses. Rename your mock repository to CourseRepository and make sure the interface is fully implemented.
Add CourseRepository as a service, and refactor the controller to use methods for ICourseRepository
# Wrapping Things Up
At this point, the flow of your APIs should be in lines with this chart:
Client (e.g. Postman)
![](imgs/4-mvc-pattern-and-repositories_1.png)
* In solution explorer, the project hierarchy would look like this
* Once you start adding new classes, they should have their own models and controllers
* One repository can store multiple models, and there can be multiple repositories
* Every server/database/API you use should have its own repository!
# What the Heck Does This Do?
* A lot of the functionality in ASP.NET comes from the base class library
* At beginning, trying to remember all the methods and what did what can feel overwhelming
* If you ever end up getting confused about what did what, Ctrl + click Type name to go to definition
* There is always a summary about the method
* Also looking up the method from Microsoft documentation is helpful

@ -0,0 +1,453 @@
# 5. REST Architecture
![](imgs/5-rest-architecture_0.png)
---
# REST - What it actually is?
Style of software architecture commonly used to create networked services
REST comes from "representational state transfer"
Defined by Roy Fielding in 2000 as architecture for distributed hypermedia systems. Incidentally, NOT made for web services at all
Developed in parallel with HTTP1.1
While (almost) any application layer protocol can be used, REST APIs are almost always built on HTTP
---
Fielding's paper is available online at https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm and is relatively short
https://restfulapi.net/ seems like a good resource
Wikipedia is plain wrong, but less wrong in English than in Finnish
# REST architecture
* Data and functionality are resources
* Resources are accessed through URIs
* Resources are acted upon by simple and well-defined operations
* Clients and servers exchange representations of resources using a standardised protocol (usually HTTP)
* Resources are decoupled from representations
* Allows content to be accessed in a variety of formats (e.g. HTML, XML, plain text, image formats, JSON, etc.)
* Resource metadata is available to be used for detecting errors, negotiate the appropriate representation format, access control, control caching, etc.
* All resources are stateless
---
Access control: e.g. key token validation
Statelessness:
every response is isolated; fulfilling requests is not dependent on any other requests
client is responsible for sending the data about itself
# REST - Requirements
* For something to be considered RESTful, it needs to follow a set of architectural constraints
* Be client-server
* Client context must not be stored on server (server is stateless)
* Explicitly reusable responses (cacheable)
* Have an uniform interface, with the restrictions
* Be designed to be a layered system (you don't need to know if you are targeting the end server directly, or something in-between)
# REST constraints - Client-Server
* First constraint is client-server
* Separation of concerns
* Separate user interaction from data
* Allows independent evolvement of components; no need to rebuild server if UI needs a rework, and vice versa
* Scalability by simplification of server components
* Portability of the user interface across platforms
---
Fielding's paper is available online at https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm and is relatively short
https://restfulapi.net/ seems like a good resource
Wikipedia is plain wrong, but less wrong in English than in Finnish
# REST constraints - Statelessness
* Servers must be stateless
* Intended to improve visibility, reliability and scalability
* Each request from the client must contain all the necessary information to understand the request
* Requests cannot take advantage of stored context on the server
* This means that in true RESTful services session state is stored entirely on client side
* Single request can be used to determine full nature of the request
* No need to manage resource usage across requests
* Server can quickly free resources for other requests
* Easily recover from partial errors
---
Fielding's paper is available online at https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm and is relatively short
https://restfulapi.net/ seems like a good resource
Wikipedia is plain wrong, but less wrong in English than in Finnish
# REST constraints - Cacheability
* Client-cache-stateless-server
* Statelessness enables some endpoints to always produce the same result with the same request
* Responses to requests can then be explicitly marked as cacheable
* Clients can reuse cached responses for the same requests
* Reduces repetitive requests
* Potential to eliminate some interactions completely
* Trades reliability for performance, efficiency and scalability
* What happens when the data updates?
* At this point, we are at design rationale for the early (pre-1994) world wide web architecture
---
Fielding's paper is available online at https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm and is relatively short
https://restfulapi.net/ seems like a good resource
Wikipedia is plain wrong, but less wrong in English than in Finnish
# REST constraints - Uniform interface
* Central distinguishing feature of REST style
* REST implementations are restricted by four interface constraints
* identification of resources
* manipulation of resources through representations
* self-descriptive messages
* hypermedia as the engine of application state
* Transfer information in standardised form rather than specific to application's needs
* Implementations are decoupled from the services they provide
* Encourage independent evolvement at the cost of efficiency
---
Fielding's paper is available online at https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm and is relatively short
https://restfulapi.net/ seems like a good resource
Wikipedia is plain wrong, but less wrong in English than in Finnish
# REST constraints - Layered system
* Allow architecture to be composed of hierarchical layers
* Last required constraint
* Let components "see" only the immediate layer they are interacting with
* Arbitrary amount of servers accessible by this layer
* Reduce overall system complexity by restricting knowledge of the system to a single layer
* Encapsulate legacy services
* Protect new services from legacy clients
* Hence bugs and errors in old code cannot be exploited, while still preserving functionality
* Enable load balancing
* Tradeoff here is added overhead and latency
---
Fielding's paper is available online at https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm and is relatively short
https://restfulapi.net/ seems like a good resource
Wikipedia is plain wrong, but less wrong in English than in Finnish
# REST constraints - Code-on-Demand
* Optional final constraint
* Client functionality is allowed to be extended by downloading and executing code in the form of applications or scripts
* Goal is to simplify clients by reducing amount of pre-implemented features
* Is optional in REST because of reduced visibility
* Can be a problem from security viewpoint
---
Fielding's paper is available online at https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm and is relatively short
https://restfulapi.net/ seems like a good resource
Wikipedia is plain wrong, but less wrong in English than in Finnish
# REST elements - Resources
* Basic REST abstraction is a _resource_
* Any information that can be named
* Document, news article, image, collection, person, etc.
* Identified with a _resource identifier_
* Commonly URI
* Identifies resources interacting between components
* State of a resource at a certain timestamp is called a _resource representation_
---
Fielding's paper is available online at https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm and is relatively short
https://restfulapi.net/ seems like a good resource
Wikipedia is plain wrong, but less wrong in English than in Finnish
# REST elements - Representation
* Consist of data, metadata describing it and hypermedia links
* Hypermedia links help clients to transition to the next desired state
* This is where the name Representational State Transfer comes from
* Hypermedia means just that the controls are presented with the information, the links on web pages being the obvious example
* The data format of a representation is called a _media type_ . The media type identifies how a representation should be processed by the client
* Self-descriptive
* Client does not need to know if the resource is a book or an employee, it should act according to the media type associated with the resource
* Often leads to creating a lot of custom media types
---
Fielding's paper is available online at https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm and is relatively short
https://restfulapi.net/ seems like a good resource
Wikipedia is plain wrong, but less wrong in English than in Finnish
# RESTful APIs
Note that most "REST API"s out there do not fill these requirements
Often any web API on top of HTTP is incorrectly described as "RESTful"
Most commonly hypermedia isn't used as the engine of application state, or the application is not really stateless, and some people outright ignore those requirements from the get go (which in case of both, technically makes the system RPC, not REST, but the name still sticks)
---
This slide sucks
* A REST API should be entered without prior knowledge beyond the initial URI and the appropriate media types
* i.e. media types expected to be understood by all clients that might use the API
* Moving forward, application state transitions are driven by client selection from server-provided choices present in the received representations
* This is to keep interaction driven by in-band information
* REST does not equal HTTP
* As long as the REST constraints are honoured, the interface can be called RESTful
---
https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
# HTTP Methods for RESTful APIs
* You have so far implemented GET and POST methods for reading resources from the API and creating new ones to it
* All the primary methods for following the uniform interface requirement are __GET, POST, PUT, PATCH __ and __DELETE__
* Others exist, but these are by far most commonly used
* These methods correspond to __CRUD __ operations __Create__ , __Read__ , __Update __ and __Delete__
* CRUD describes what is done to a resource after a request is sent
# HTTP Methods for RESTful APIs (continued)
The primary HTTP request methods with descriptions:
| __Method__ | __Attribute__ | __Description__ |
| :-: | :-: | :-: |
| GET | [HttpGet] | _Read _ a representation of a resource |
| POST | [HttpPost] | _Create _ new resources |
| PUT | [HttpPut] | Fully _Update _ an existing resource |
| PATCH | [HttpPatch] | Partially _Update _ an existing resource |
| DELETE | [HttpDelete] | _Delete _ a resource |
[https://www.restapitutorial.com/lessons/httpmethods.html](https://www.restapitutorial.com/lessons/httpmethods.html)
# Handling HttpPost Requests
Remember, that HTTP requests can include a content body
In ASP.NET, this content is assigned to a variable with the [FromBody] attribute:
[HttpPost]
public string[] Post([FromBody] string someContent)
{
// someContent holds the content of the request body
return new string[] { text };
}
Note that the [FromBody] attribute can only be used on one parameter
However, nothing prevents you from using a custom type variable
# Handling HttpPost Requests (continued)
ASP.NET deserializes the request content body into an object:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
// In controller class:
[HttpPost]
public Contact Put(int id, [FromBody] Contact contact)
{
// Contacts = list of Contact objects, fetched from some repository
Contacts.Add(contact);
return contact;
}
# Creating a POST Request with Postman
* ASP.NET knows to deserialize the content if the content type is set to JSON in the HTTP requests __headers__
* Headers are optional parameters that can be included in every HTTP request
* Headers are set in Key-Value format
* When creating a request in Postman, to inform the server what type of content was just sent, add a new key "Content-Type" and set its value to "application/json" in the _Headers _ tab:
![](imgs/5-rest-architecture_1.png)
# Creating a POST Request with Postman (continued)
After setting the header,
select the Body tab,
change the content type to raw,
select JSON from the dropdown menu, and
insert the content in JSON format
![](imgs/5-rest-architecture_2.png)
# Creating a POST Request with Postman - Example
Suppose the Post method from before is routed at http://localhost:54106/api/students:
![](imgs/5-rest-architecture_3.png)
# Exercise 1: Creating a POST Endpoint
Continue working on CourseAPI.
Create a Post method and endpoint which adds a new course to the list in the repository, with a running ID. Content and Author values are obtained from the request body:
![](imgs/5-rest-architecture_4.png)
# HTTP PUT
Use PUT to replace an existing resource, e.g. an element in a list, with a new one
The ID of the resource to be replaced should be in the request URI
The information about the new resource should be in the request body like in POST requests
![](imgs/5-rest-architecture_5.png)
# Handling HttpPut Requests
The ID is fetched from the URI and the contents from the request body
Filtering is used to copy all objects from the original list except for the new contact object, which comes from body
[HttpPut("{id}")]
public List<Contact> Put(int id, [FromBody] Contact contact)
{
// Contacts = list of Contact objects, fetched from some repository
List<Contact> updatedList = Contacts.Select(c => c.Id != id ? c : contact).ToList();
Contacts = updatedList;
return updatedList;
}
# Exercise 2: Creating a PUT Endpoint
* In CoursesController class, create a method for PUT requests with the URI api/courses/{id}
* The ID of the course to be replaced with should be in the request URI and the contents of the new course should be in the request body.
* The method should replace the corresponding course from the Courses list in the repository with the new course.
* The method should return the updated Courses list (for testing purposes)
* Return a 404 status code if a course with the corresponding ID does not exist
* Test with Swagger/Postman
# HTTP DELETE
Use DELETE to delete an existing resource, e.g. an element in a list
The ID of the resource to be deleted should be in the request URI
As with GET method, a body is not needed
![](imgs/5-rest-architecture_6.png)
# Handling HttpDelete Requests
The ID is fetched from the URI
[HttpDelete("{id}")]
public List<Contact> Delete(int id)
{
// Contacts = list of contact objects, fetched from some repository
List<Contact> updatedList = Contacts.Where(c => c.Id != id).ToList();
Contacts = updatedList;
return Contacts;
}
# Exercise 3: Creating a DELETE Endpoint
* Continue working on CourseAPI.
* Create an endpoint for DELETE requests with the URI api/courses/{id}
* The ID of the course should be fetched from the URI
* The corresponding course should be removed from the list of courses in the repository
* The method should return the updated Courses list (for testing purposes)
* Return a 404 status code if a course with the corresponding ID does not exist
* Test with Postman
# HTTP PATCH
* Use PATCH to partially update a resource
* I.e. update some value inside of a resource
* This saves some resources as only a part of a resource has to be sent instead of an entire document (as opposed to PUT requests)
* Sending and handling PATCH requests with ASP.NET requires some extra work and the use of a third party package: [http://jsonpatch.com/](http://jsonpatch.com/)
# HTTP PATCH (continued)
* To handle PATCH requests in a standardized way, install and add the following NuGet packages to your project:
* Microsoft.AspNetCore.JsonPatch
* Microsoft.AspNetCore.Mvc.NewtonsoftJson
* Then, change the following line in Program.cs (ASP.NET 5: Startup.cs)...
* builder.Services.AddControllers();
* … to this:
* builder.Services.AddControllers().AddNewtonsoftJson();
The body of the PATCH request needs to be in the following form:
[{ "op": "replace", "path": "/propertyName", "value": "newValue"}]
Use the URI to specify the ID of the resource to update
![](imgs/5-rest-architecture_7.png)
# Handling HttpPatch Requests
The ID is fetched from the URI
The property and its new value are specified in the body, which is retrieved as JsonPatchDocument
PATCH is operated on the targeted object
[HttpPatch("{id}")]
public List<Contact> Patch(int id, [FromBody] JsonPatchDocument<Contact> patchDocument)
{
// Contacts = list of contact objects, fetched from some repository
var contact = Contacts.FirstOrDefault(c => c.Id == id);
if(contact != null){
patchDocument.ApplyTo(contact);
}
return Contacts;
}
# Exercise 4: Creating a PATCH Endpoint
* Continue working on CourseAPI.
* Create an endpoint for PATCH requests with the URI api/courses/{id}
* The ID of the course should be fetched from the URI
* The corresponding course should be updated according to the JSON PATCH document in the request body
* The method should return the updated Courses list (for testing purposes)
* Return a 404 status code if a course with the corresponding ID does not exist
* Test with Swagger/Postman: try to change the number of credits for some course

@ -0,0 +1,150 @@
# Model validation & API Design
# Model Validation
ASP.NET has a built-in system for validating whether the data sent in requests fits the model set in the code
Additional requirements can be set with attributes
public class Contact
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
[MaxLength(100)]
public string Email { get; set; }
}
# Model Validation (continued)
An object can be validated at any time with TryValidateModel method:
[HttpPost]
public IActionResult Post([FromBody] Contact contact)
{
if (!TryValidateModel(contact))
{
return BadRequest(ModelState);
}
Contacts.Add(contact);
return Created(Request.Path, contact);
}
The attributes set a corresponding error message and information to the ModelState which is sent with the bad request result
![](imgs/6-model-validation-and-designing-apis_0.png)
![](imgs/6-model-validation-and-designing-apis_1.png)
Sometimes you might have custom requirements and the action should, according to the standard, return a bad request if the action does not fit those requirements
For example, the simplest possible way to validate the format of an email is to check whether it contains the '@' symbol and with AddModelError add an error if it does not:
if (!contact.Email.Contains('@'))
{
ModelState.AddModelError("Description", "The email is not in valid form.");
}
Then check the model state:
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
# Exercise 1:
Continue working on CourseAPI.
Mark all the properties of the Course class with the [Required] attribute
Create an endpoint for POST requests with the URI api/courses
All the contents of the new course should come from the request body and the new course should be added to the Courses list
The maximum number of credits should be 20 and minimun 1 (Tip: Range)
Return an appropriate response
# Model Validation - Limiting PATCH
* If we only want to allow the PATCH operation to affect Name and Email properties of Contact class
* Begin by declaring a class with only those fields:
* public class ContactPatch
* {
* public string Name { get; set; }
* public string Email { get; set; }
* }
# Model Validation - Limiting PATCH (continued)
Use the ContactPatch class instead of the actual class for patching:
[HttpPatch("{id}")]
public IActionResult Patch(int id, [FromBody] JsonPatchDocument<ContactPatch> patchDocument)
{
Contact initialContact = _contactRepository.GetContact(id);
ContactPatch patchContact = new ContactPatch
{ Name = initialContact.Name, Email = initialContact.Email };
patchDocument.ApplyTo(patchContact, ModelState);
if (!ModelState.IsValid)
{
return BadRequest();
}
initialContact.Name = patchContact.Name;
initialContact.Email = patchContact.Email;
_contactRepository.UpdateContact(id, initialContact);
return Ok(initialContact);
}
# Designing APIs
It's useful to get a good overview of the API for yourself and others before getting to work
| __Method__ | __Endpoint__ | __Description__ | __Success__ | __Failure__ |
| :-: | :-: | :-: | :-: | :-: |
| __GET__ | __/api/contacts__ | __Return all contacts__ | __200 Ok__ | __400 Bad request__ __404 Not found__ |
| __POST__ | __/api/contacts__ | __Add a new contact__ | __201 Created__ | __400 Bad request__ |
| __PUT__ | __/api/contacts/{id}__ | __Update a contact__ | __204 No content__ | __400 Bad request__ __404 Not found__ |
| __PATCH__ | __/api/contacts/{id}__ | __Partially update a contact__ | __204 No content__ | __400 Bad request__ __404 Not found__ |
| __DELETE__ | __/api/contacts/{id}__ | __Remove a contact__ | __200 OK__ | __404 Not found__ |

@ -0,0 +1,809 @@
# SQL
* __Language __ to organize and manipulate data in a database
* Originally developed by IBM in the 70's
* Quickly became the most popular database language
* SELECT id, email
* FROM users
* WHERE first_name = 'Teppo';
# Relational Database Management Systems
* In relational databases, values are stored in __tables__
* Each table has __rows __ and __columns__
* Data is displayed in a two-dimensional matrix
* Values in a table are related to each other
* Values can also be related to values in other tables
* A relational database management system (RDBMS) is a program, which executes queries to relational databases
![](imgs/7-databases-with-entity-framework_0.png)
[https://db-engines.com/en/ranking](https://db-engines.com/en/ranking)
# PostgreSQL
Free open-source, cross-platform relational database management system
Emphasizes extensibility and SQL compliance
Fully ACID-compliant (atomicity, consistency, isolation and durability)
# pgAdmin
Administration and development platform for PostgreSQL
Cross-platform, features a web interface
Basically a control panel application for your PostgreSQL database
# PostgreSQL: Creating a Database
With pgAdmin:
Right-click Servers > my-postgres > _Databases_
Select _Create _ > _Database…_
Insert a name for the database _ _ and hit _Save_
![](imgs/7-databases-with-entity-framework_1.png)
With psql:
Enter commandCREATE DATABASE <database-name>;
# PostgreSQL: Querying
With pgAdmin:
Right-click _sqlpractice _ > _Query tool..._
Insert a query into the _Query Editor_ and hit _Execute _ (F5)
![](imgs/7-databases-with-entity-framework_2.png)
![](imgs/7-databases-with-entity-framework_3.png)
---
With psql:
List all available databases with \\l
Connect to the created database with \\c <database-name>
List all tables in the database with \\dt
Type a query and press enter
---
https://drive.google.com/file/d/1E5TqY5yt8DNcWHVkb8gxvBpgLtWB15DC/view?usp=sharing "provided query"
# Editing Data with pgAdmin
Inspect and edit data in pgAdmin by right-clicking a table and selecting _View/Edit Data_
![](imgs/7-databases-with-entity-framework_4.png)
![](imgs/7-databases-with-entity-framework_5.png)
Individual values in the table can be directly modified by double clicking the value and then editing the value in the visual user interface
Save the changes with the _Save Data Changes_ button
![](imgs/7-databases-with-entity-framework_6.png)
# Exercise 1: Preparing Database
Using either PgAdmin or PSQL
Create a new database called sqlpractice
Insert the [provided query](https://drive.google.com/file/d/1E5TqY5yt8DNcWHVkb8gxvBpgLtWB15DC/view) to the new database
Verify that the query has created new tables to your database
# Types of queries
Select
Insert
Delete
Update
Create & Drop
# Querying data with SELECT
Syntax:
__SELECT column1, column2, column3 FROM table_name;__
Examples:
SELECT full_name, email FROM users;
SELECT full_name AS name, email FROM users;
SELECT * FROM users;
# Filtering data with WHERE
Syntax:
__SELECT column1, column2 FROM table_name WHERE condition;__
Text is captured in _single quotes_ .
LIKE condition uses _%_ sign as wildcard.
IS and IS NOT are also valid operators.
Example:
SELECT full_name FROM users
WHERE full_name = 'Teppo Testaaja';
SELECT * FROM books WHERE name LIKE '%rr%';
SELECT * FROM books WHERE author IS NOT null;
# Ordering data with ORDER BY
Syntax:
__SELECT column1 FROM table_name ORDER BY column1 ASC;__
Examples:
SELECT full_name FROM users
ORDER BY full_name ASC;
SELECT full_name FROM users
ORDER BY full_name DESC
# Combining with JOIN
Also known as INNER JOIN
Corresponds to intersection from set theory
![](imgs/7-databases-with-entity-framework_7.png)
# JOIN examples
SELECT
users.id, users.full_name, borrows.id,
borrows.user_id, borrows.due_date, borrows.returned_at
FROM users
JOIN borrows ON
users.id = borrows.user_id;
SELECT
U.full_name AS name,
B.due_date AS due_date,
B.returned_at AS returned_at
FROM users AS U
JOIN borrows AS B ON
U.id = B.user_id;
# Combining with LEFT JOIN
Also known as LEFT OUTER JOIN
Example:
SELECT
U.full_name AS name,
B.due_date AS due_date,
B.returned_at AS returned_at
FROM users AS U
LEFT JOIN borrows AS B ON
U.id = B.user_id;
![](imgs/7-databases-with-entity-framework_8.png)
# Exercise 2: Querying the Library
Using SQL queries, get
all columns of borrows that are borrowed before 2020-10-27
all columns of borrows that are returned
columns user.full_name and borrows.borrowed_at of the user with an id of 1
columns book.name, book.release_year and language.name of all books that are released after 1960
# INSERT
Syntax
__INSERT INTO table_name (column1, column2, column3)_ __VALUES (value1, value2, value3);__
Example
INSERT INTO users (full_name, email, created_at)
VALUES ('Pekka Poistuja', 'pekka.poistuja@buutti.com', NOW());
Since id is not provided, it will be automatically generated.
# UPDATE
Syntax
__UPDATE table_name__ __SET column1 = value1, column2 = value2__ __WHERE condition;__
__Notice:__ if a condition is not provided, all rows will be updated!If updating only one row, it is usually best to use id.
Example
UPDATE usersSET email = 'taija.testaaja@gmail.com'WHERE id = 2;
# REMOVE
Syntax
__DELETE FROM table_name WHERE condition;__
Again, if the _condition_ is not provided, DELETE affects _all_ rows. Before deleting, it is a good practice to execute an equivalent SELECT query to make sure that only the proper rows will be affected.
Example:
SELECT __*__ FROM __users __ WHERE __id = 5;__
DELETE FROM users WHERE id = 5;
# Exercise 3: Editing Data
Postpone the due date of the borrow with an id of 1 by two days in the _borrows _ table
Add a couple of new books to the _books _ table
Delete one of the borrows.
# CREATE TABLE
Before data can be manipulated, a database and its tables need to be initialized.
Syntax
__CREATE TABLE table_name (__
__ column1 datatype,__ __ column2 datatype,__ __ …__ __);__
Example:CREATE TABLE "users" (
"id" SERIAL PRIMARY KEY,
"full_name" varchar NOT NULL,
"email" varchar UNIQUE NOT NULL,
"created_at" timestamp NOT NULL
);
# DROP
In order to remove tables or databases, we use a DROP statement
__DROP TABLE table_name;__ __DROP DATABASE database_name;__
These statements do not ask for confirmation and there is no undo feature. Take care when using a drop statement.
# NoSQL
* Many differing definitions, but...
* most agree that NoSQL databases store data in a format other than tables
* They can still store relational data - just differently
* Four different database types:
* Document databases
* Key-value databases
* Wide-column stores
* Graph databases
* Example database engines include MongoDB, Redis and Cassandra
---
Document databases store data in documents similar to JSON (JavaScript Object Notation) objects. Each document contains pairs of fields and values. The values can typically be a variety of types including things like strings, numbers, booleans, arrays, or objects, and their structures typically align with objects developers are working with in code. Because of their variety of field value types and powerful query languages, document databases are great for a wide variety of use cases and can be used as a general purpose database. They can horizontally scale-out to accomodate large data volumes. MongoDB is consistently ranked as the world's most popular NoSQL database according to DB-engines and is an example of a document database. For more on document databases, visit What is a Document Database?.
Key-value databases are a simpler type of database where each item contains keys and values. A value can typically only be retrieved by referencing its value, so learning how to query for a specific key-value pair is typically simple. Key-value databases are great for use cases where you need to store large amounts of data but you don't need to perform complex queries to retrieve it. Common use cases include storing user preferences or caching. Redis and DynanoDB are popular key-value databases.
Wide-column stores store data in tables, rows, and dynamic columns. Wide-column stores provide a lot of flexibility over relational databases because each row is not required to have the same columns. Many consider wide-column stores to be two-dimensional key-value databases. Wide-column stores are great for when you need to store large amounts of data and you can predict what your query patterns will be. Wide-column stores are commonly used for storing Internet of Things data and user profile data. Cassandra and HBase are two of the most popular wide-column stores.
Graph databases store data in nodes and edges. Nodes typically store information about people, places, and things while edges store information about the relationships between the nodes. Graph databases excel in use cases where you need to traverse relationships to look for patterns such as social networks, fraud detection, and recommendation engines. Neo4j and JanusGraph are examples of graph databases.
# Object-Relational Mappers
Allows a developer to write code instead of SQL to perform CRUD operations on their database
Some popular ORMs:
Hibernate (Java)
EFCore (.NET)
Sequelize (Node.js)
TypeORM (TypeScript)
# Databases with Entity Framework
![](imgs/7-databases-with-entity-framework_9.png)
---
# Entity Framework (EF)
* ORM made by Microsoft for the .NET framework
* Object-Relational Mapping: converting from database representation to objects in a programming language
* Allows creation of CRUD operations without writing SQL
# Entity Framework Core (EFC)
Cross-platform, can be used outside of the .NET framework unlike normal Entity Framework
Open-source, lightweight, extensible
Supports many database engines, such as MySQL, PostgreSQL, and so on
This is what we'll be using
---
Notes:
ORM - Object Relational Mapper
CRUD - CREATE READ UPDATE DELETE
# Code First, Database First & Model First
Three approaches through which Entity Framework is implemented
Database First and Code First are the most used ones and will be taught in this lecture
# Code First approach
![](imgs/7-databases-with-entity-framework_10.png)
---
# Code First
* Entity framework will create databases and tables based on defined entity classes
* Good for small applications
* Other advantages include:
* You can create database and tables from your business objects
* You can specify which related collections are…
* eager loaded
* not serialized at all
* Database version control
* Not preferred for data intensive applications
# Required Packages
Install and add the following packages to your project:
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Tools
Npgsql.EntityFrameworkCore.PostgreSQL
![](imgs/7-databases-with-entity-framework_11.png)
# Code First: DbContext
* Let's begin with the Code First Approach
* The DbContext class of EFCore is the bridge between the code representation of your data (entities) and the database
* DbContext holds methods to form the database schema with Code First approach and classes to keep the database up-to-date with CRUD operations
* DATABASE -> CODE: DbSet class property in DbContext can be queried directly with LINQ and this results in an object in your code
* CODE -> DATABASE: DbSet also has methods like Add, Update and Remove to make changes to the database from your code
# DbContext
* Create a context that inherits from DbContext
* Commonly located in the Models folder, but ideally should be in a separate abstraction/repository folder (for example Repositories)
* The class needs to have a constructor which calls the base constructor with : base(options)
* Create a DbSet property for each resource
* public class ContactsContext : DbContext
* {
* public DbSet<Contact> Contacts { get; set; }
* public ContactsContext(DbContextOptions<ContactsContext> options) : base(options){ }
* }
# DbContext (continued)
To further configure how the database will be structured, override the OnModelCreating method
In this example, one table named Contact with columns Id, Name and Email will be created:
public class ContactsContext : DbContext
{
public DbSet<Contact> Contacts { get; set; }
public ContactsContext(DbContextOptions<ContactsContext> options) : base(options){ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Contact>().ToTable("Contact");
}
}
In this example, the Contact table will be created with some starting values for Id, Name and Email columns:
public class ContactsContext : DbContext
{
public DbSet<Contact> Contacts { get; set; }
public ContactsContext(DbContextOptions<ContactsContext> options) : base(options){ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Contact>().HasData(
new Contact { Id = 1, Name = "Johannes Kantola", Email = "johkant@example.com" },
new Contact { Id = 2, Name = "Rene Orosz", Email = "rene_king@example.com" }
);
}
}
# DbContext as a Service
* In Program.cs, add the context to services with AddDbContext method
* This is where you set the DB management system you want to use (MySQL, PostgreSQL, SQLite)
* The EFCore support for PostgreSQL is called __Npgsql__ __ __ as in the package name
* Add the server, host, port, username, password and database name of the existing database inside options.UseNpgsql as a __connection string:__
* services.AddDbContext<ContactsContext>(options => options.UseNpgsql(
* @"Server=PostgreSQL 12;Host=localhost;Port=5432;Username=postgres;Password=1234;Database=contacts"));
* services.AddScoped<IContactRepository, ContactRepository>();
* services.AddControllers().AddNewtonsoftJson();
# Migrations
* As the development progresses, models and database schemas change over time
* This means that both the database and the code needs to be updated to match each other
* Migrations allow for the database to keep in sync with the code schematically
* The data stored in the database is also preserved
* EFCore migrations have built-in version control; a snapshot of each version of the schema is stored
# Migrations (continued)
* Open the Package Manager Console in Visual Studio
* If the tab is not in the bottom of the window, open it from _View _ -> _Other windows_ -> _Package Manager Console_
* Add your initial migration by entering the command Add-Migration <name> to the console, for example
* Add-Migration InitialMigration
* This now creates the first "blueprint" of how the database should be structured
* Update the database by entering the command Update-Database to the console
* This will update the existing database according to the ModelBuilder options
At this point, the values you have entered (Contacts table in this example) should show up in the database. You can check it up e.g. in pgAdmin.
![](imgs/7-databases-with-entity-framework_12.png)
![](imgs/7-databases-with-entity-framework_13.png)
* Notice that the table and column names are initialized with a capital letter
* The value naming in psql is case sensitive, so all names have to be in quotation marks
# Exercise 4: Adding Context
Continue working on the CourseAPI.
Create a new empty database course_db in pgAdmin or psql
Create a DbContext for the courses. Name it CoursesContext, and add a DbSet of type Course to it, named Courses
Add the OnModelCreating method to the context and add a couple of courses with some starting values to the modelBuilder
Add the CoursesContext to the services in Program.cs with a connection string pointing to course_db
Add the first migration and update the database from the Package Manager Console
Check that the Course table with the starting values has appeared to the database
# Using DbContext in the API
* Because DbContext is added to services, it can be accessed from any other service, such as the repository
* Using the DbSet for each model in your project, CRUD operations can be applied to the database from the repository with LINQ and DbSet methods
* Add()
* Update()
* Remove()
* After modifying the DbSet, update the changes to the database with DbContext.SaveChanges() method
# Injecting DbContext
Inject the DbContext to your repositories as you would any other service:
public class ContactRepository : IContactRepository
{
private readonly ContactsContext _context;
public ContactRepository(ContactsContext context)
{
_context = context;
}
//...
}
# DbContext: Read Operations
public class ContactRepository : IContactRepository
{
private readonly ContactsContext _context;
public ContactRepository(ContactsContext context) { ... }
public Contact GetContact(int id) =>
_context.Contacts.FirstOrDefault(c => c.Id == id);
public List<Contact> GetContacts() =>
_context.Contacts.ToList();
}
# DbContext: Create Operations
public class ContactRepository : IContactRepository
{
private readonly ContactsContext _context;
public ContactRepository(ContactsContext context) { ... }
// Read operations
// …
public void AddContact(Contact contact)
{
_context.Contacts.Add(contact);
_context.SaveChanges();
}
}
# DbContext: Update Operations
public class ContactRepository : IContactRepository
{
private readonly ContactsContext _context;
public ContactRepository(ContactsContext context) { ... }
// Read & create operations
// ...
public void UpdateContact(int id, Contact newContact)
{
var contact = GetContact(id);
contact.Email = newContact.Email;
contact.Name = newContact.Name;
_context.Contacts.Update(contact);
_context.SaveChanges();
}
}
# DbContext: Delete Operations
public class ContactRepository : IContactRepository
{
private readonly ContactsContext _context;
public ContactRepository(ContactsContext context) { ... }
// Read, create & update operations
// ...
public void DeleteContact(int id)
{
_context.Contacts.Remove(GetContact(id));
_context.SaveChanges();
}
}
# Exercise 5: CRUD on the DB
Continue working on CourseAPI.
Modify the CourseRepository to create, read, update and delete from the database instead of the locally stored list of courses
Test with Postman. Keep refreshing the DB in pgAdmin or creating queries with psql to make sure the request work as intended
# Summing Things Up
Now the API has been hooked up to a PostgreSQL database
Changes to the schema are kept up-to-date with migrations
Repository is processing CRUD operations to the database
Controllers accepting HTTP requests have access to the repository
# EFCore Code First Checklist
Install required packages
Create DbContext for database
Add DbContext to services
Add-Migration & Update-Database
Add CRUD operations to repository for DB
# Modifying the Relations
* Let's change the structure of our Contacts API by adding a new class, Account
* Instead of Contact directly having an Email, it will have an Account instead
* Account holds the information about the Email, as well as a Description about the nature of the account (personal, work, school etc.)
* Emails will be removed from the Contacts table
# Modifying the Relations (continued)
public class Contact
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Account> Accounts { get; set; }
}
public class Account
{
public int Id { get; set; }
public string Email { get; set; }
public string Description { get; set; }
public int ContactId { get; set; }
public Contact Contact { get; set; }
}
Adding a migration at this point will result in a warning:
![](imgs/7-databases-with-entity-framework_14.png)
* In the generated migration file, you can find Up and Down methods
* The Up method describes the changes that will be made with the migration
* In this case, removing the Email column from Contacts table, and creating the new Accounts table
* The Down method describes the changes that will be made if the migration is reverted
* Updating the database will still work, and the database will have a new table, Accounts
![](imgs/7-databases-with-entity-framework_15.png)
# Exercise 6: Adding Migrations
Continue working on CourseAPI.
Add a new model Lecture with properties int Id, DateTime StartTime, int Length, Course Course, and int CourseId
Add a new property ICollection<Lecture> Lectures to the Course model
Add a new migration, named _AddLectures_
Update the database. Check that the changes show up in the database with pgAdmin
# Database First approach
![](imgs/7-databases-with-entity-framework_16.png)
---
# Database First
* This is the other approach for creating a connection between the database and the application
* Databases and tables are created first, then you create entity data model using the created database
* Preferred for data intense, large applications
* Other advantages include:
* Data model is simple to create
* GUI
* You do not need to write any code to create your database
# Database First (continued)
* Use the Package Manager Console to "reverse engineer" the code for an existing database
* This is called __scaffolding__
* Scaffold the database with the following command:
* Scaffold-DbContext "Server=PostgreSQL 12;Host=localhost;Port=5432;Username=postgres;Password=1234;Database=sqlpractice" Npgsql.EntityFrameworkCore.PostgreSQL -OutputDir Models
* Using the connection string corresponding to your database, this will create all the classes for the entities in the DB as well as the context class
# Exercise 7: Database First
Create a new ASP.NET Core web app using the API template.
Install the required NuGet packages for using EFCore, EFCore Tools and PostgreSQL by using the package manager, or by copying the <PackageReference> lines from the .csproj file of the previous assignment to this projects .csproj file
Scaffold the _sqlpractice _ database (which was created in the Exercise 1) to the project using the Database First approach. If you have not yet created the database in PostGreSQL, it can be found [here](https://buuttilab.com/education/trainee-academy-joensuu/assignments/-/raw/94ddeaaa5abb240c0035d718d082df8704488045/Databases/initdb.txt)
# Reading: Authentication with roles
* Here's an example how to do Role-based Authentication by using JWT tokens
* [https://www.c-sharpcorner.com/article/jwt-token-creation-authentication-and-authorization-in-asp-net-core-6-0-with-po/](https://www.c-sharpcorner.com/article/jwt-token-creation-authentication-and-authorization-in-asp-net-core-6-0-with-po/)
# Extra: Exercise 8: Connection
[under construction]
Continuing the previous exercise,
Create and connect Postgres database to API and create a second entity with a relation to the first entity. Test your solution.

@ -0,0 +1,16 @@
# Authentication & Authorization
__Kato täältä esimerkki__
* Authentication: Verifying that you are indeed you
* Authorization: Determining access rights based on your privileges
* If you don't restrict access to your endpoints, something like [this](https://mastodon.gamedev.place/@badlogic/111246798083590676) [might happen](https://firesky.tv/) !!!
* Tee loput luennot tän esimerkin mukaan:
* [https://www.c-sharpcorner.com/article/jwt-token-creation-authentication-and-authorization-in-asp-net-core-6-0-with-po/](https://www.c-sharpcorner.com/article/jwt-token-creation-authentication-and-authorization-in-asp-net-core-6-0-with-po/)
Esimerkkisisältö: Tehtävänanto
* Make a simple HTML website using the template from the previous slide, so that it contains paragraphs, headings, images and links.
* Add at least two links; one remote and one local.
* How would you make an image to be a link?
* Later on, we will add more stuff to the site.

@ -0,0 +1,817 @@
# Testing
![](imgs/9-testing_0.png)
---
* What is testing?
* Proving that the program is working as intended during development on a
* micro-level: individual pieces of code, such as methods
* macro-level: larger functional components, such as services
* Automated testing & manual testing
* All possible paths in a piece of code should be tested
* Why do automated testing?
* Sets ground truths that can be relied on during development: "With this kind of input, this should happen"
* No need to take the programmers word for it
* Ensures code functionality before moving on, "finding bugs before debugging"
* Guides developers to create more testable and thus more readable, interchangeable and overall better code
# Automated Testing
* Unit testing
* Testing individual units (methods, classes) within a project
* "input x produces output y"
* Each unit has to be entirely independent and deterministic for it to be unit testable!
* Deterministic = Always the same result with the same input
* Integration Testing
* Testing complete modules and the interaction between them within a project
* Closer to real life scenarios
# C# Testing Frameworks
* Three main testing frameworks:
* MSTest
* NUnit
* XUnit
* You can install these using NuGet, or in the newer versions of Visual Studio you can create a new testing project
* NUnit is the most popular of the three, __and will be used in this lecture__
# MSTest
* Built-in to most versions of Visual Studio since 2005
* Simple to use, easy structure
* TestClass for denoting classes containing unit tests
* TestMethod for denoting unit test methods
# NUnit
* Most popular C# unit testing framework
* Very similar structure to MSTest tests
* Instead of TestClass, we use __TestFixture__
* Instead of TestMethod, we use __Test / TestCase__
* Has some advantages to MSTest or xUnit tests, for example:
* Ability to test a single test method using multiple different arguments
# XUnit
Free, open-source unit testing tool for .NET framework and .NET core
Made by the creator of NUnit v2
You can test C#, F#, VB.NET and other .NET languages
Part of the .NET Foundation
Unique and different style
# Test Case Naming Conventions
In C# unit testing, the most commonly followed naming convention for test cases/methods is as follows:
FunctionNameThatIsTested_Condition_ShouldDoX
For example:
Fibonacci_WhenNIs0_ShouldReturn0
# Solution & Sample Project
First, we need a solution and a sample project.
In Visual Studio, create a class library that targets __.NET Standard__ .
Call the project "Fibonacci.Library", and solution "Fibonacci". Remember to uncheck " _Place solution and project in the same directory_ " if it isn't unchecked already.
# Solution & Sample Project (continued)
![](imgs/9-testing_1.png)
Next, rename the default "Class1.cs" file to "Fibonacci.cs".
Allow Visual Studio to also rename the class itself to Fibonacci.
Make the class public and static.
Now, let's __ __ add a recursive fibonacci function, as shown in the next slide:
using System;
namespace Fibonacci.Library {
public static class Fibonacci {
public static long Recursive(
int n,
long previous = 0,
long current = 0,
int counter = 0
) {
return counter == n ?
previous + current :
Recursive(n, current, Math.Max(previous + current, 1), counter + 1);
}
}
}
# Adding a Testing Project
Now we need to add a testing project to our solution.
Right click your solution, and add a new project. Select the NUnit project for __.NET Core__ , and call it "Fibonacci.UnitTests".
Rename the created class file to "Fibonacci_Tests.cs", and the class to "Fibonacci_Tests"
# Adding a Testing Project (continued)
We then need to link the Fibonacci.Library project to Fibonacci.UnitTests.
Right click Fibonacci.UnitTests, and select Add > Reference…, and under "Projects" select Fibonacci.Library. Press OK.
Now we have linked Fibonacci.Library to Fibonacci.UnitTests, and we can begin testing our Recursive-function in the Fibonacci-class.
# Test Fixture For a Class
Since we have many things called "Fibonacci", we have to import our Fibonacci-class by using an alias:
using FibonacciLib = Fibonacci.Library.Fibonacci;
Put this to the top of your Fibonacci_Tests.cs file, underneath using NUnit.Framework;.
# Test Fixture For a Class (continued)
Now, add [TestFixture] on top of our class, like so:
using NUnit.Framework;
using FibonacciLib = Fibonacci.Library.Fibonacci;
namespace Fibonacci.UnitTests {
[TestFixture]
public class Fibonacci_Tests {
This denotes that this class contains our unit tests.
Then, inside our class Fibonacci_Tests, let's create our first method to test the Recursive-function, as shown in the next slide:
using NUnit.Framework;
using FibonacciLib = Fibonacci.Library.Fibonacci;
namespace Fibonacci.UnitTests {
[TestFixture]
public class Fibonacci_Tests {
[Test]
public void Recursive_WhenNIs0_ShouldReturn0()
=> Assert.That(0, Is.EqualTo(FibonacciLib.Recursive(n: 0)));
}
}
* Things to take into consideration:
* When passing in function arguments, to avoid magic numbers and to improve code readability, use the syntax _argumentName: value_
* Magic numbers = using numbers as input as-is without explanation, for example without assigning to a well-named variable
* Assert.That is a function (but not the only function) that determines whether the test passes.
* Its first argument is the expected value. In the second argument you give a constraint for the actual output, i.e. value is equal to, greater than, starts with, and [many others](https://docs.nunit.org/articles/nunit/writing-tests/constraints/Constraints.html) .
* Do not mix the arguments up! If you do, the errors you get do not make any sense.
# Running Tests
Now, let's open the built-in test runner in Visual Studio to run our tests.
On the top of Visual Studio, click "Test", then "Test Explorer".
In the test explorer, you should see the test case we created. If not, build the solution, and make sure there are no errors.
Either way, let's run our test case. Press "Run All Tests" in the top-left corner of test explorer. You should see our test case __passing__ .
# Adding More Test Cases
While there's nothing inherently wrong about our test case, it doesn't cover much of anything. We need to add more test cases to test different values and outputs.
In other testing frameworks, you would do this by creating more methods.
But with NUnit, we can use our existing method.
# Adding More Test Cases (continued)
Do the following modifications to our test method:
[TestCase(0, 0)]
public void Recursive_Always_ShouldReturnNthFibonacci(
int n,
long expected
)
=> Assert.That(expected, Is.EqualTo(FibonacciLib.Recursive(n)));
Using TestCase instead of Test, we can pass in arguments to our test case method, and we can use those passed in arguments in our actual tests.
Run the test cases again, and they should __pass__ .
But as you might notice, we haven't really changed anything yet.
Let's add more test cases, like so:
[TestCase(0, 0)]
[TestCase(1, 1)]
public void Recursive_Always_ShouldReturnNthFibonacci(
Now we have 2 test cases with only one test method. Run the tests again, and both cases should __pass__ .
Let's add one more TestCase, like so:
[TestCase(2, 1)]
Run the tests again, and you should see the test case __fail__ .
This is to be expected: the 2nd fibonacci number should be 1, but the actual value we got from our function was 2.
This means our function does not work as expected.
Let's fix it, as shown in the next slide:
public static long Recursive(
int n,
long previous = 0,
long current = 0,
int counter = 0
) {
return counter == n ?
current : // changed from previous + current
Recursive(n, current, Math.Max(previous + current, 1), counter + 1);
}
Run the tests again. You should see all __pass __ now.
Add a few more test cases; if you don't know the fibonacci sequence, you can check it from google.
# Exercise 1: Unit testing in practice
* Answer the following questions:
* How many test cases should we ideally have to test our Recursive-function?
* What happens if we pass in n = int.MaxValue (2147483647)? Or other huge numbers?
* How would you fix the function so it works as expected?
* What happens if we pass in n = -1 ? Or other negative numbers?
* How would you fix the function so it works as expected?
* (Answers on the next slide)
# Unit testing in practice: Exercise - Answers
* Answers:
* We should cover all the edge cases and code paths with our test cases. We do not need to have many test cases for the same code path.
* Stack Overflow, Int64 Overflow, or the test case just won't run.
* We should have a maximum allowed value for n, throw argument out of range exception if above
* Stack Overflow, since current never can equal n that is less than 0
* We should have a minimum allowed value for n, throw argument out of range exception if below
# Testing Exceptions
To expect exceptions to be thrown in test cases, we can use the method
Assert.Throws, syntax is as follows:
Assert.Throws<ExceptionType>(delegate);
In practice:
Assert.Throws<NullReferenceException>(() => TestedFunction(n));
# Handling Border Cases
We need to add handling for n that is less than 0 or more than a set amount. Then we need to add another test method and two test cases to test our new functionality.
Let's start with modifying our existing Recursive-function, as shown in the next slide:
# Handling Border Cases (continued)
public const int MinDepth = 0;
public const int MaxDepth = 10000;
public static long Recursive(
int n,
long previous = 0,
long current = 0,
int counter = 0
) {
if (n < MinDepth || n > MaxDepth)
throw new ArgumentOutOfRangeException("n is out of bounds!");
return counter == n ?
current :
Recursive(n, current, Math.Max(previous + current, 1), counter + 1);
}
Now our Recursive-function throws an ArgumentOutOfRange exception when n is less than 0 or more than 10000. Let's test that everything works as expected, by adding a new test method and a few test cases:
[TestCase(int.MaxValue)]
[TestCase(-1)]
public void Recursive_WhenNIsOutOfBounds_ShouldThrowArgumentOutOfRange(
int n
)
=> Assert.Throws<ArgumentOutOfRangeException>(() => FibonacciLib.Recursive(n));
__Rename the other method to Recursive_WhenNIsInBounds_ShouldReturnNthFibonacci__
# Removing Unnecessary Arguments
We have one more problem:
Our Recursive-function is a public method that has in total 4 arguments it takes, yet we only test 1 argument (n).
In order for our tests to be complete, we need to test all the arguments a public method takes in.
So, how do we fix this?
# Removing Unnecessary Arguments (continued)
The naive way would be to simply test all the arguments with more test cases.
But it's important to realize that __we don't have to__ . Since we basically never pass in any other arguments than n from outside Recursive, we could just create another __private __ function that has those arguments, and we could remove them from our __public __ Recursive-function.
Like shown in the next slide:
public static long Recursive(int nStartingValue) {
if (nStartingValue < MinDepth || nStartingValue> MaxDepth)
throw new ArgumentOutOfRangeException("n is out of bounds!");
long getRecursive(
int n,
long previous = 0,
long current = 0,
int counter = 0)
=> counter == n ?
current :
getRecursive(n, current, Math.Max(previous + current, 1), counter + 1);
return getRecursive(nStartingValue);
}
# End Result
Now we have tested all the arguments in our public Recursive function.
We do not need to test private functions such as getRecursive, only the public API has to be tested.
All tests should __pass __ by this point.
# Exercise 2: Unit Testing
Following the previously used architecture as closely as possible, unit test the following function using NUnit:
Questions on the next slide.
public static long DoX(int nStartingValue) {
if (nStartingValue < 0 || nStartingValue > 150)
throw new ArgumentOutOfRangeException("n is out of bounds!");
long doY(int n) => n > 0 ? n * doY(n - 1) : 1;
return doY(nStartingValue);
}
# Unit Testing: Exercise - Questions
Answer the following questions:
What does the function that we are testing actually do?
Do we have to test doY as well?
How would you name DoX so it is descriptive? How about doY?
# Unit Testing: Exercise - Answers
* It gets the nth factorial (n!)
* doY is a private method that gets tested when we test DoX, so no.
* DoX = TryGetFactorial, doY = getFactorial
* __Name your functions to these__
# Unit Testing - Final Testing Code Part 1
[TestCase(0, 1)]
[TestCase(1, 1)]
[TestCase(2, 2)]
[TestCase(3, 6)]
[TestCase(15, 1307674368000)]
public void TryGetFactorial_WhenNIsInBounds_ShouldReturnNthFactorial(
int n, long expected
)
=> Assert.That(expected, Is.EqualTo(TestExerciseLib.TryGetFactorial(n)));
[TestCase(-1)]
[TestCase(151)]
public void TryGetFactorial_NIsOutOfBounds_ShouldThrowArgumentOutOfRange(
int n
)
=> Assert.Throws<ArgumentOutOfRangeException>(
() => TestExerciseLib.TryGetFactorial(n)
);
# Writing Testable Code
* Writing testable code equals writing good code
* Testable units should
* be deterministic (functional): same input always produces the same output
* be loosely coupled: components have a loose and limited connection to other components in the system, meaning changes in one component should not require changes to many others
* abide to the _Single Responsibility Principle: _ a function, method or component is only responsible for one purpose
---
https://en.wikipedia.org/wiki/Loose_coupling
# Writing Testable Code - Example 1
public static class Season
{
public static string GetCurrentSeason()
{
DateTime currentTime = DateTime.Now;
if (currentTime.Month > 11 && currentTime.Month < 3)
return "Winter";
else if (currentTime.Month < 6)
return "Spring";
else if (currentTime.Month < 9)
return "Summer";
else
return "Fall";
}
}
Can GetCurrentSeason() be unit tested?
Why / why not?
How to fix the problems, if any?
# Writing Testable Code - Example 1 (continued)
public static class Season
{
public static string GetCurrentSeason(
DateTime currentTime
)
{
if (currentTime.Month > 11 && currentTime.Month < 3)
return "Winter";
else if (currentTime.Month < 6)
return "Spring";
else if (currentTime.Month < 9)
return "Summer";
else
return "Fall";
}
}
* Now the method is purely functional and deterministic
* Will return the same value for every call with equal arguments
* GetCurrentSeason() can now easily be tested as shown in the next slide
public class GetCurrentSeasonTest
{
[TestCase(12, "Winter")]
[TestCase(3, "Spring")]
[TestCase(6, "Summer")]
[TestCase(9, "Fall")]
public void GetCurrentSeason_ForFirstMonthsOfSeasons_ReturnsCorrectSeason(
int month,
string season
)
{
Assert.That(season, Is.EqualTo(Season.GetCurrentSeason(new DateTime(2020, month, 1))));
}
}
![](imgs/9-testing_2.png)
Test results indicate there is something wrong with the code path that should return "Winter"
if (currentTime.Month > 11 && currentTime.Month < 3)
The line is changed to
if (currentTime.Month > 11 || currentTime.Month < 3)
![](imgs/9-testing_3.png)
# Writing Testable Code - Example 2
Let's look at a HeatingUnit class, which returns a heating setting based on the current season. The current date problem is back again:
public class HeatingUnit
{
public string GetHeatingSetting()
{
DateTime currentDate = DateTime.Now;
if (Season.GetCurrentSeason(currentDate) == "Winter")
return "HEAT_SETTING_HIGH";
else if (Season.GetCurrentSeason(currentDate) == "Summer")
return "HEAT_SETTING_OFF";
else
return "HEAT_SETTING_MEDIUM";
}
}
# Writing Testable Code - Example 2 (continued)
Instead of taking the current date initialization even higher in the class hierarchy, let's make a service that returns the current date
public interface IDateTimeProvider
{
DateTime GetDateTime();
}
After this the service can be injected into HeatingUnit with constructor injection (next slide)
public class HeatingUnit
{
private readonly IDateTimeProvider _dateTimeProvider;
public HeatingUnit(IDateTimeProvider dateTimeProvider)
{
_dateTimeProvider = dateTimeProvider;
}
public string GetHeatingSetting()
{
if (Season.GetCurrentSeason(_dateTimeProvider.GetDateTime()) == "Winter")
return "HEAT_SETTING_HIGH";
else if (Season.GetCurrentSeason(_dateTimeProvider.GetDateTime()) == "Summer")
return "HEAT_SETTING_OFF";
else
return "HEAT_SETTING_MEDIUM";
}
}
For testing, a fake DateTimeProvider service can be used for injecting the HeatingUnit with any date:
class FakeDateTimeProvider : IDateTimeProvider
{
public DateTime Date { get; set; }
public DateTime GetDateTime() => Date;
}
In the real application the provider would return the real current date
class SetHeatingSettingTest
{
[TestCase(12, "HEAT_SETTING_HIGH")]
[TestCase(3, "HEAT_SETTING_MEDIUM")]
[TestCase(6, "HEAT_SETTING_OFF")]
[TestCase(9, "HEAT_SETTING_MEDIUM")]
public void SetHeatingSetting_ForFirstMonthsOfSeasons_ShouldReturnCorrectSetting(
int month,
string setting
)
{
FakeDateTimeProvider timeProvider
= new FakeDateTimeProvider { Date = new DateTime(2020, month, 1) };
HeatingUnit heatingUnit = new HeatingUnit(timeProvider);
Assert.That(setting, Is.EqualTo(heatingUnit.GetHeatingSetting()));
}
}
![](imgs/9-testing_4.png)
---
Näytä demo
# Testing with Postman
* So far, you have created individual request with Postman to see what the response of an API is for each request
* This is __not testing __ an API, this is __exploring __ an API
* Tests in Postman are inserted in the _Tests _ tab
* Postman uses __Chai.js__ testing library for creating tests
![](imgs/9-testing_5.png)
To get started immediately, you can select snippets from the right
Let's select the "Status code: Code is 200" snippet
![](imgs/9-testing_6.png)
Successfully ran tests show up in green in the Test Result tab of the response:
![](imgs/9-testing_7.png)
[The official Postman web page](https://learning.postman.com/docs/writing-scripts/test-scripts/) is a good starting point for learning testing with Postman
Using pm.expect will give a bit more info about the test:
![](imgs/9-testing_8.png)
The response to /serverinfo has the following body:
{
"appSettings": {
"applicationUrl": "http://localhost:63741",
"aspNetCoreEnvironment": "Development"
},
"serverStatus": {
"dbConnectionStatus": "Connected"
}
}
Let's make a test to see whether the server is connected to the database
Variables can be declared within the test:
![](imgs/9-testing_9.png)
# Exercise 3: Testing with Postman
Create a new collection in Postman by selecting the _Collections _ tab and clicking _New Collection._ Name it _CourseAPI Tests. _ Launch the CourseAPI you have developed during the lectures, and make sure the course_db database is connected
Create a new GET request for the URI /api/courses. Add a test: status code of the request should be 200. Save the request to the _CourseAPI Tests_ collection
Create a new GET request for the URI /api/courses/999999999. Add a test: status code of the request should be 404. Save the request to the _CourseAPI Tests_ collection
Create a new POST request for the URI /api/courses. Set the headers and the content correctly, but set the value of credits to 100. Add a test: status code should be 400. Save the request to the _CourseAPI Tests_ collection
Hover on CourseAPI Tests collection, click the arrow and click RUN. From the opened window, scroll down and click "Run CourseAPI Tests". This will create all the requests in your collection
# CI/CD/CT
* CI stands for _Continuous Integration_
* Each change in code triggers a build-and-test sequence for the given project
* Goal is to have a consistent and automated way to build, package, and test applications
* CD stands for _Continuous Delivery_
* Automates the delivery of applications to selected environments, such as Azure, AWS, GCP
* Both require _Continuous Testing_ , which includes automated regression, performance and other tests

@ -1,14 +1,13 @@
# Contents # Contents
Material completion denoted with 🌑🌘🌗🌖🌕 . | # | Lecture | Slides |
|:--|:--------------------------------------------------------------------------|:---------------------------------------------------------------------|
| # | Lecture | Materials | Exercises | | 1 | [Introduction to the Internet](1-introduction-to-the-internet.md) | [Download slides](1-introduction-to-the-internet-slides.html) |
| ---: | ------------------------------------- | --------: | --------: | | 2 | [ASP.NET Basics](2-aspnet-core-basics.md) | [Download slides](2-aspnet-core-basics-slides.html) |
| 1 | [Example Lecture](example-lecture.md) | 🌕 | 🌕 | | 3 | [HTTP Responses & Status Codes](3-http-responses-and-status-codes.md) | [Download slides](3-http-responses-and-status-codes-slides.html) |
| 4 | [MVC Pattern & Repositories](4-mvc-pattern-and-repositories.md) | [Download slides](4-mvc-pattern-and-repositories-slides.html) |
| 5 | [5. REST Architecture](5-rest-architecture.md) | [Download slides](5-rest-architecture-slides.html) |
## Repository notes (remove before publishing) | 6 | [Model validation & API Design](6-model-validation-and-designing-apis.md) | [Download slides](6-model-validation-and-designing-apis-slides.html) |
| 7 | [SQL](7-databases-with-entity-framework.md) | [Download slides](7-databases-with-entity-framework-slides.html) |
- After reading, remove [example-lecture.md](./example-lecture.md), [example-lecture-slides.html](./example-lecture-slides.html) and [buuttilogo.png](./imgs/buuttilogo.png) | 8 | [Authentication & Authorization](8-authentication-and-authorization.md) | [Download slides](8-authentication-and-authorization-slides.html) |
- See [Markdown code snippets](.vscode/markdown.code-snippets) for autocomplete stuff. | 9 | [Testing](9-testing.md) | [Download slides](9-testing-slides.html) |
- Remove the .gitkeep files from imgs and solutions folders after adding new files to those folders.

File diff suppressed because one or more lines are too long

@ -1,85 +0,0 @@
---
marp: true
paginate: true
math: mathjax
theme: buutti
title: N. Example Lecture
---
# Example Lecture
<!-- headingDivider: 5 -->
<!-- class: invert -->
## Section
- This line appears instantly
* This line appears by pressing spacebar (preferred)
* This line has an inline code `variable`
1. This line appears instantly
2) This line appears by pressing spacebar
## Code and maths
* code code code:
```js
console.log("Here's a syntax-highlighted JavaScript code block");
console.log("Remember indentation so it's revealed after the bullet point.");
```
* This line has an inline LaTeX maths equation $c = \frac{a^2}{\sqrt{b}}$
* Here's a maths block:
$$
F(x) = \int_a^b f(x) dx
$$
<!-- _footer: Footers are exclusive to presentation; they are not shown in the webpage markdown document -->
## Columns
<div class='columns' markdown='1'>
<div markdown='1'>
![](imgs/buuttilogo.png)
* Basic image example
</div>
<div markdown='1'>
![width:800px](imgs/buuttilogo.png)
* Wider image
</div>
</div>
* This line is outside the columns and goes from left all the way to the right
## Columns 2
<div class='columns21' markdown='1'>
<div markdown='1'>
* Another column example with a wider left panel
</div>
<div markdown='1'>
* Change `class` name to change proportions
* If suitable proportions not available, add to `buutti.css`
</div>
</div>
## Setup
* In VS Code, install the extensions
* [Marp for VS code](https://marketplace.visualstudio.com/items?itemName=marp-team.marp-vscode)
* So you can see the slideshow preview when editing.
* [Markdown all in one](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one)
* [Markdown table formatter](https://marketplace.visualstudio.com/items?itemName=fcrespo82.markdown-table-formatter)
* *Right click > Format document* makes tables pretty
* [Save and run](https://marketplace.visualstudio.com/items?itemName=wk-j.save-and-run)
* An HTML version of the lecture is created on save
* See [settings.json](./.vscode/settings.json)
* Add filenames to `notMatch` if a HTML on save is not needed

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Loading…
Cancel
Save