WeatherControl

Manage you WeatherData with Wissance

This project uses Wissance.WebApiToolkit so please give us a star! And for this project too!

1. General description

This project is a tutorial about how to design REST API, we are have here 2 simultaneously existing Web projects:

  1. REST API with EntityFramework ORM - Wissance.WeatherControl.WebApi project
  2. REST API with EdgeDb Graph DB - Wissance.WeatherControl.WebApi.V2 project

These 2 Project Have different data Model

2. REST API With Net Framework

This project target platform is a netcore 3.1

2.1 Glossary / Domain object

2.2 Application Overview

Web API REST service (.Net Core) that could store weather data from multiple weather station. We assume that we are going to store/manage following physical value measurements getting from appropriate sensors:

Application has 2 resources = Domain objects

Application uses MsSql as Database Server (this could be easily changed, but this required to re-generate migration).

  1. Station
  2. Measurement

2.3 Overall usage scenario

This is a very simple application (demo), if any feature is needed open new issue.

  1. Application client create one or multiple station using Station (/api/station) resource (CRUD)
  2. Client interacts with station, gets it measured data and store it using Measurements (/api/measurements) resource (CRUD)
2.3.1 Example of usage

It should be noted that Postman Requests stored in docs folder

2.3.2 Operations with Station resource
  1. Create Station:

POST http://localhost:8058/api/station

{
	"id": 0,
	"name": "Yekaterinburg main station",
	"description": "Yekaterinburg meteo station (meteo mountain)",
	"longitude": "60°37'55\"E",
	"latitude": "56°49'36\"N"
}

Result of running create station

We got a Operation result response:

{
    "success": true,
    "message": null,
    "data": {
        "id": 1,
        "name": "Yekaterinburg main station",
        "description": "Yekaterinburg meteo station (meteo mountain)",
        "longitude": "60°37'55\"E",
        "latitude": null
    }
}
  1. Station data update (could be updated name, description and coordinates):

PUT http://localhost:8058/api/station/1

Body and response are the same as at Create operation:

{
	"id": 0,
	"name": "Yekaterinburg main station",
	"description": "Yekaterinburg meteo station (meteo mountain)",
	"longitude": "60°37'55\"E",
	"latitude": "56°49'36\"N"
}

Result of running update station

  1. There are two get endpoints:
  1. To delete station with id 1 use endpoint DELETE http://localhost:8058/api/station/1
2.3.3 Operations with measurements resource
  1. Create measurements

POST http://localhost:8058/api/measurements

{
	"id": 0,
	"timestamp": "2022-05-24T10:13:43",
	"temperature": 16.1,
	"pressure": 742.3,
	"humidity": 60.5,
	"windSpeed": 0.5,
	"stationId": 1
}

We got following result in the output:

{
    "success": true,
    "message": null,
    "data": {
        "id": 1,
        "timestamp": "2022-05-24T10:13:43",
        "temperature": 16.1,
        "pressure": 742.3,
        "humidity": 60.5,
        "windSpeed": 0.5,
        "stationId": 1
    }
}

Result of running create measurements

  1. Update measurements: one or any number of weather parameters could be changed using PUT http://localhost:8058/api/measurements/1 with same body and result as at create measurements operation.

Result of running update measurements

  1. There are two get operations:
  1. To delete measurements with id 1 use endpoint DELETE http://localhost:8058/api/measurements/1

3. REST API With EdgeDB

Here we’ve got a net6.0 REST Service that have a slightly different data model:

Data project is Wissance.WeatherControl.GraphData

Relation between Models

3.1 Configure Edge DB (Prerequisites)

  1. Start Edgedb instance from Wissance.WeatherControl.GraphData directory
edgedb instance start -I Wissance_WeatherControl_Data --foreground

apply migration via

edgedb migrate
  1. Configure Edgedb to allow pass own identifiers (necessary for object return after create)
edgedb configure set allow_user_specified_id true
  1. Start edgedb ui:
edgedb ui

Once you loaded project you could use it in current application:

  1. Add proper connection string in the database section in appsettings.Development.jsonconfig file:
    "Application": {
     "Database": {
       "ConnStr": "edgedb://edgedb:VcJjK6blkKAV2MUTdJXzLPvS@localhost:10702/edgedb"
     }
      }
    

configuration string must have the following scheme: edgedb://user:password@host:port/database you could see your project credential on Windows machine in a directory: %USER_PROFILE%\AppData\Local\EdgeDB\config\credentials

3.2 REST API With Edge DB

We are having following Key Items:

  1. Controllers - we are using base classes from a Wissance.WebApiToollit, in this lib we have eithther controllers for read-only and for full CRUD resources.
  2. Managers - classes that are responsible for manage all business logic, in this project we have only one manager class - EdgeDbManager that is common for CRUD operation over all resources
  3. EqlResolver - class that is responsible for association model (resource) with operation (read , create, update or delete)
  4. Factories - static classes that constructs DTO from Models and params (dictionary for create and update perform) from DTO.
3.2.1 REST API Controllers

All controllers are located in a folder Controllers, just look how simply look full CRUD Controller:

namespace Wissance.WeatherControl.WebApi.V2.Controllers
{
    public class MeasurementController : BasicCrudController<MeasurementDto, MeasurementEntity, Guid>
    {
        public MeasurementController(EdgeDBClient edgeDbClient)
        {
            Manager = new EdgeDbManager<MeasurementDto, MeasurementEntity, Guid>(ModelType.Measurement, edgeDbClient,
                MeasurementFactory.Create, MeasurementFactory.Create);
        }
    }
}
3.2.2 Manager

We have only one manager for all controllers due to the power of C# generics we just have to pass to EdgeDbManager:

3.2.3 EqlResolver

Just a set of dictionaries every dictionary for one operation:

3.2.4 Factories for objects convertion

They are static classes in a Factories dicrectory, the looking quite simple:

namespace Wissance.WeatherControl.WebApi.V2.Factories
{
    public static class SensorFactory
    {
        public static SensorDto Create(SensorEntity entity)
        {
            SensorDto dto = new SensorDto()
            {
                Id = entity.Id,
                Name = entity.Name,
                Latitude = entity.Latitude,
                Longitude = entity.Longitude
            };

            if (entity.Measurements.Any())
            {
                dto.Measurements = entity.Measurements.Select(m => MeasurementFactory.Create((m))).ToList();
            }

            return dto;
        }
        
        public static IDictionary<string, object?> Create(SensorDto dto, bool generateId)
        {
            IDictionary<string, object?> dict = new Dictionary<string, object?>()
            {
                {"Name", dto.Name},
                {"Latitude", dto.Latitude},
                {"Longitude", dto.Longitude},
                {"Measurements", dto.Measurements.Where(m => m.Id.HasValue)
                    .Select(m => m.Id.Value).ToArray()}
            };
            
            // TODO(this if for further getting created object)
            dict["id"] = generateId ? Guid.NewGuid() : dto.Id;

            return dict;
        }
    }
}