├── CovidSafe
├── CovidSafe.Entities
│ ├── README.md
│ ├── Validation
│ │ ├── RequestValidationIssue.cs
│ │ ├── IValidatable.cs
│ │ ├── RequestValidationProperty.cs
│ │ ├── RequestValidationFailedException.cs
│ │ ├── RequestValidationFailure.cs
│ │ └── RequestValidationResult.cs
│ ├── Geospatial
│ │ ├── RegionComparer.cs
│ │ ├── RegionBoundary.cs
│ │ ├── Coordinates.cs
│ │ ├── NarrowcastArea.cs
│ │ └── Region.cs
│ ├── Messages
│ │ ├── MessageContainerMetadata.cs
│ │ ├── BluetoothSeedMessage.cs
│ │ ├── NarrowcastMessage.cs
│ │ └── MessageContainer.cs
│ └── CovidSafe.Entities.csproj
├── CovidSafe.API
│ ├── appsettings.Development.json
│ ├── .config
│ │ └── dotnet-tools.json
│ ├── appsettings.json
│ ├── Properties
│ │ └── launchSettings.json
│ ├── CovidSafe.API.csproj
│ ├── Program.cs
│ ├── Swagger
│ │ └── SwaggerConfigureOptions.cs
│ ├── v20200611
│ │ ├── Protos
│ │ │ └── Interactions.proto
│ │ ├── Controllers
│ │ │ ├── MessageControllers
│ │ │ │ ├── AnnounceController.cs
│ │ │ │ └── ListController.cs
│ │ │ └── MessageController.cs
│ │ └── MappingProfiles.cs
│ ├── v20200505
│ │ ├── Protos
│ │ │ └── Interactions.proto
│ │ ├── Controllers
│ │ │ ├── MessageControllers
│ │ │ │ ├── AreaReportController.cs
│ │ │ │ ├── SeedReportController.cs
│ │ │ │ └── ListController.cs
│ │ │ └── MessageController.cs
│ │ └── MappingProfiles.cs
│ ├── v20200415
│ │ ├── Protos
│ │ │ └── Interactions.proto
│ │ ├── Controllers
│ │ │ ├── MessageControllers
│ │ │ │ ├── AreaReportController.cs
│ │ │ │ ├── SeedReportController.cs
│ │ │ │ └── ListController.cs
│ │ │ └── MessageController.cs
│ │ └── MappingProfiles.cs
│ └── Startup.cs
├── .runsettings
├── CovidSafe.DAL
│ ├── Services
│ │ ├── IService.cs
│ │ └── IMessageService.cs
│ ├── CovidSafe.DAL.csproj
│ ├── Repositories
│ │ ├── IRepository.cs
│ │ ├── Cosmos
│ │ │ ├── Client
│ │ │ │ ├── CosmosSchemaConfigurationSection.cs
│ │ │ │ ├── CosmosRepository.cs
│ │ │ │ ├── CosmosConnectionFactory.cs
│ │ │ │ └── CosmosContext.cs
│ │ │ └── Records
│ │ │ │ ├── CosmosRecord.cs
│ │ │ │ └── MessageContainerRecord.cs
│ │ └── IMessageContainerRepository.cs
│ └── Helpers
│ │ ├── PayloadSizeHelper.cs
│ │ ├── GeoHelper.cs
│ │ └── PrecisionHelper.cs
├── Local.testsettings
├── CovidSafe.Entities.Tests
│ └── CovidSafe.Entities.Tests.csproj
├── CovidSafe.API.Tests
│ ├── v20200611
│ │ ├── MappingProfilesTests.cs
│ │ └── Controllers
│ │ │ └── MessageControllers
│ │ │ └── AnnounceControllerTests.cs
│ ├── v20200415
│ │ ├── MappingProfilesTests.cs
│ │ └── Controllers
│ │ │ └── MessageControllers
│ │ │ ├── AreaReportControllerTests.cs
│ │ │ └── SeedReportControllerTests.cs
│ ├── v20200505
│ │ ├── MappingProfilesTests.cs
│ │ └── Controllers
│ │ │ └── MessageControllers
│ │ │ ├── AreaReportControllerTests.cs
│ │ │ └── SeedReportControllerTests.cs
│ └── CovidSafe.API.Tests.csproj
├── CovidSafe.DAL.Tests
│ ├── CovidSafe.DAL.Tests.csproj
│ └── Helpers
│ │ └── PrecisionHelperTests.cs
├── CovidSafe.API.Tests.Performance
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── CovidSafe.API.Tests.Performance.csproj
│ └── WebTest.webtest
└── CovidSafe.sln
├── LICENSE.md
└── README.md
/CovidSafe/CovidSafe.Entities/README.md:
--------------------------------------------------------------------------------
1 | ## ATTENTION!
2 |
3 | Use protobuf-net for proto compilation, which has better support for .NET.
4 |
5 | https://github.com/protobuf-net/protobuf-net
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.API/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/CovidSafe/.runsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 8
6 | MethodLevel
7 |
8 |
9 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.DAL/Services/IService.cs:
--------------------------------------------------------------------------------
1 | namespace CovidSafe.DAL.Services
2 | {
3 | ///
4 | /// Base service layer definition
5 | ///
6 | public interface IService
7 | {
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.API/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "dotnet-ef": {
6 | "version": "3.1.3",
7 | "commands": [
8 | "dotnet-ef"
9 | ]
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.Entities/Validation/RequestValidationIssue.cs:
--------------------------------------------------------------------------------
1 | namespace CovidSafe.Entities.Validation
2 | {
3 | ///
4 | /// issue type enumeration
5 | ///
6 | public enum RequestValidationIssue
7 | {
8 | InputEmpty,
9 | InputInvalid,
10 | InputNull
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.Entities/Validation/IValidatable.cs:
--------------------------------------------------------------------------------
1 | namespace CovidSafe.Entities.Validation
2 | {
3 | ///
4 | /// Object is validatable contract
5 | ///
6 | public interface IValidatable
7 | {
8 | ///
9 | /// Determines if the current object is valid
10 | ///
11 | /// summary
12 | RequestValidationResult Validate();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.API/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "AllowedHosts": "*",
3 | "ApplicationInsights": {
4 | "InstrumentationKey": ""
5 | },
6 | "ConnectionStrings": {
7 | "CosmosConnection": ""
8 | },
9 | "CosmosSchema": {
10 | "DatabaseName": "traces",
11 | "MaxDataAgeToReturnDays": 14,
12 | "MessageContainerName": "messages_api_v3"
13 | },
14 | "DefaultApiVersion": "2020-06-11",
15 | "KeyVaultUrl": "",
16 | "Logging": {
17 | "LogLevel": {
18 | "Default": "Warning"
19 | }
20 | },
21 | "SwaggerHosts": "https://localhost:44347/"
22 | }
--------------------------------------------------------------------------------
/CovidSafe/Local.testsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 | These are default test settings for a local test run.
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.Entities/Validation/RequestValidationProperty.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 |
3 | namespace CovidSafe.Entities.Validation
4 | {
5 | ///
6 | /// Request validation property name constants
7 | ///
8 | [ExcludeFromCodeCoverage]
9 | public static class RequestValidationProperty
10 | {
11 | ///
12 | /// String reference used when multiple properties cause a
13 | ///
14 | ///
15 | public const string Multiple = "multiple";
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.DAL/CovidSafe.DAL.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 | $(NoWarn);1591
6 | netstandard2.0
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.Entities.Tests/CovidSafe.Entities.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.Entities/Geospatial/RegionComparer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace CovidSafe.Entities.Geospatial
5 | {
6 | ///
7 | /// Compares two objects
8 | ///
9 | public class RegionComparer : IEqualityComparer
10 | {
11 | ///
12 | public bool Equals(Region x, Region y)
13 | {
14 | return x.LatitudePrefix == y.LatitudePrefix
15 | && x.LongitudePrefix == y.LongitudePrefix
16 | && x.Precision == y.Precision;
17 | }
18 |
19 | ///
20 | public int GetHashCode(Region obj)
21 | {
22 | return Tuple.Create(obj.LatitudePrefix, obj.LongitudePrefix, obj.LongitudePrefix)
23 | .GetHashCode();
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.DAL/Repositories/IRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 |
4 | namespace CovidSafe.DAL.Repositories
5 | {
6 | ///
7 | /// Base repository definition
8 | ///
9 | /// Object type handled by repository implementation
10 | /// Type used by the primary key
11 | public interface IRepository
12 | {
13 | ///
14 | /// Retrieves an object which matches the provided identifier
15 | ///
16 | /// Unique object identifier
17 | /// Cancellation token
18 | /// Object or null
19 | Task GetAsync(TT id, CancellationToken cancellationToken = default);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.API/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:1049",
7 | "sslPort": 44347
8 | }
9 | },
10 | "$schema": "http://json.schemastore.org/launchsettings.json",
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "launchUrl": "swagger",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | }
19 | },
20 | "CovidSafe.API": {
21 | "commandName": "Project",
22 | "launchBrowser": true,
23 | "launchUrl": "swagger",
24 | "environmentVariables": {
25 | "ASPNETCORE_ENVIRONMENT": "Development"
26 | },
27 | "applicationUrl": "https://localhost:5001;http://localhost:5000"
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.Entities/Messages/MessageContainerMetadata.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 |
3 | namespace CovidSafe.Entities.Messages
4 | {
5 | ///
6 | /// Basic metadata
7 | ///
8 | [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
9 | public class MessageContainerMetadata
10 | {
11 | ///
12 | /// unique identifier
13 | ///
14 | [JsonProperty("id", Required = Required.Always)]
15 | public string Id { get; set; }
16 | ///
17 | /// Timestamp of creation
18 | ///
19 | ///
20 | /// Reported in milliseconds (ms) since the UNIX epoch.
21 | ///
22 | [JsonProperty("timestampMs", Required = Required.Always)]
23 | public long Timestamp { get; set; }
24 | }
25 | }
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.Entities/Validation/RequestValidationFailedException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CovidSafe.Entities.Validation
4 | {
5 | ///
6 | /// used for failed objects
7 | ///
8 | public class RequestValidationFailedException : Exception
9 | {
10 | ///
11 | /// from process
12 | ///
13 | public RequestValidationResult ValidationResult { get; private set; }
14 |
15 | ///
16 | /// Creates a new instance
17 | ///
18 | ///
19 | public RequestValidationFailedException(RequestValidationResult validationResult) : base()
20 | {
21 | this.ValidationResult = validationResult;
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.API.Tests/v20200611/MappingProfilesTests.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using CovidSafe.API.v20200611;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 |
5 | namespace CovidSafe.API.Tests.v20200611
6 | {
7 | ///
8 | /// Unit Tests for AutoMapper
9 | ///
10 | [TestClass]
11 | public class MappingProfilesTests
12 | {
13 | ///
14 | /// pass their internal validation using
15 | /// AutoMapper
16 | ///
17 | [TestMethod]
18 | public void MappingProfiles_PassValidation()
19 | {
20 | // Assemble
21 | var mapperConfig = new MapperConfiguration(
22 | opts => opts.AddProfile()
23 | );
24 |
25 | // Act
26 | mapperConfig.AssertConfigurationIsValid();
27 |
28 | // Assert
29 | // Exception thrown on invalid mappings
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.API.Tests/v20200415/MappingProfilesTests.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using CovidSafe.API.v20200415;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 |
5 | namespace CovidSafe.API.Tests.v20200415
6 | {
7 | ///
8 | /// Unit Tests for AutoMapper
9 | ///
10 | [TestClass]
11 | public class MappingProfilesTests
12 | {
13 | ///
14 | /// pass their internal validation using
15 | /// AutoMapper
16 | ///
17 | [TestMethod]
18 | public void MappingProfiles_PassValidation()
19 | {
20 | // Assemble
21 | var mapperConfig = new MapperConfiguration(
22 | opts => opts.AddProfile()
23 | );
24 |
25 | // Act
26 | mapperConfig.AssertConfigurationIsValid();
27 |
28 | // Assert
29 | // Exception thrown on invalid mappings
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.API.Tests/v20200505/MappingProfilesTests.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using CovidSafe.API.v20200505;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 |
5 | namespace CovidSafe.API.Tests.v20200505
6 | {
7 | ///
8 | /// Unit Tests for AutoMapper
9 | ///
10 | [TestClass]
11 | public class MappingProfilesTests
12 | {
13 | ///
14 | /// pass their internal validation using
15 | /// AutoMapper
16 | ///
17 | [TestMethod]
18 | public void MappingProfiles_PassValidation()
19 | {
20 | // Assemble
21 | var mapperConfig = new MapperConfiguration(
22 | opts => opts.AddProfile()
23 | );
24 |
25 | // Act
26 | mapperConfig.AssertConfigurationIsValid();
27 |
28 | // Assert
29 | // Exception thrown on invalid mappings
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.DAL.Tests/CovidSafe.DAL.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | all
16 | runtime; build; native; contentfiles; analyzers; buildtransitive
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2020
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.DAL/Repositories/Cosmos/Client/CosmosSchemaConfigurationSection.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Azure.Cosmos;
2 |
3 | namespace CovidSafe.DAL.Repositories.Cosmos.Client
4 | {
5 | ///
6 | /// Settings object for using Cosmos with the CovidSafe schema
7 | ///
8 | public class CosmosSchemaConfigurationSection
9 | {
10 | ///
11 | /// Cosmos target database name
12 | ///
13 | public string DatabaseName { get; set; }
14 | ///
15 | /// Maximum age of data to return in queries, in number of days
16 | ///
17 | public int MaxDataAgeToReturnDays { get; set; }
18 | ///
19 | /// Name of used to store objects
20 | ///
21 | public string MessageContainerName { get; set; }
22 |
23 | ///
24 | /// Creates a new instance
25 | ///
26 | public CosmosSchemaConfigurationSection()
27 | {
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.DAL/Repositories/Cosmos/Client/CosmosRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using Microsoft.Azure.Cosmos;
4 |
5 | namespace CovidSafe.DAL.Repositories.Cosmos.Client
6 | {
7 | ///
8 | /// Base Cosmos-connected repository class
9 | ///
10 | public abstract class CosmosRepository
11 | {
12 | ///
13 | /// object used for database interaction
14 | ///
15 | public CosmosContext Context { get; private set; }
16 | ///
17 | /// used by the inheriting
18 | ///
19 | public Container Container { get; protected set; }
20 |
21 | ///
22 | /// Creates a new instance
23 | ///
24 | ///
25 | public CosmosRepository(CosmosContext dbContext)
26 | {
27 | // Set local variables
28 | this.Context = dbContext;
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.Entities/CovidSafe.Entities.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 | $(NoWarn);1591
6 | netstandard2.0
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | True
18 | True
19 | ValidationMessages.resx
20 |
21 |
22 |
23 |
24 |
25 | PublicResXFileCodeGenerator
26 | ValidationMessages.Designer.cs
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.Entities/Validation/RequestValidationFailure.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using ProtoBuf;
3 |
4 | namespace CovidSafe.Entities.Validation
5 | {
6 | ///
7 | /// Detail of individual request validation failure
8 | ///
9 | [JsonObject]
10 | [ProtoContract]
11 | public class RequestValidationFailure
12 | {
13 | ///
14 | /// classification
15 | ///
16 | [JsonIgnore]
17 | [ProtoIgnore]
18 | public RequestValidationIssue Issue { get; set; }
19 | ///
20 | /// Failure detail message
21 | ///
22 | [JsonProperty("message", Required = Required.Always)]
23 | [ProtoMember(1)]
24 | public string Message { get; set; }
25 | ///
26 | /// Name of object property which failed validation
27 | ///
28 | [JsonProperty("property", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)]
29 | [ProtoMember(2)]
30 | public string Property { get; set; }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.DAL/Helpers/PayloadSizeHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Runtime.Serialization.Formatters.Binary;
4 |
5 | namespace CovidSafe.DAL.Helpers
6 | {
7 | ///
8 | /// Helper for determining size (in bytes) of objects
9 | ///
10 | public static class PayloadSizeHelper
11 | {
12 | ///
13 | /// Calculates the size (in bytes) of an
14 | ///
15 | /// Source object
16 | /// Message size, in bytes
17 | public static long GetSize(object message)
18 | {
19 | if(message != null)
20 | {
21 | using (MemoryStream stream = new MemoryStream())
22 | {
23 | BinaryFormatter formatter = new BinaryFormatter();
24 | formatter.Serialize(stream, message);
25 | return stream.ToArray().Length;
26 | }
27 | }
28 | else
29 | {
30 | throw new ArgumentNullException(nameof(message));
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CovidSafe Backend API
2 |
3 | Contains source code for the backend service API, which provides data to CovidSafe client applications.
4 |
5 | ## Build Status
6 |
7 | Build + Unit Test Pipeline:
8 |
9 | [](https://dev.azure.com/msresearch/CovidSafe/_build/latest?definitionId=2388)
10 |
11 | Compliance Pipeline:
12 |
13 | [](https://dev.azure.com/msresearch/CovidSafe/_build/latest?definitionId=2384)
14 |
15 | Compilance Scans include:
16 |
17 | * [BinSkim](https://github.com/Microsoft/binskim)
18 | * [CredScan](https://secdevtools.azurewebsites.net/helpcredscan.html)
19 | * PoliCheck
20 | * Scans source code for words/phrases which may be insensitive.
21 | * [Roslyn Analyzers](https://secdevtools.azurewebsites.net/helpRoslynAnalyzers.html)
22 |
23 | ## Organization
24 |
25 | ### CovidSafe.API
26 |
27 | Web API project which contains controllers for each endpoint.
28 |
29 | ### CovidSafe.DAL
30 |
31 | Data Access Layer project which has the actual database interaction (repositories, etc.) and service layer classes.
32 |
33 | ### CovidSafe.Entities
34 |
35 | Library with all entity types used across the other projects.
36 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.API.Tests/CovidSafe.API.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | all
23 | runtime; build; native; contentfiles; analyzers; buildtransitive
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.DAL/Helpers/GeoHelper.cs:
--------------------------------------------------------------------------------
1 | using CovidSafe.Entities.Geospatial;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace CovidSafe.DAL.Helpers
7 | {
8 | public static class GeoHelper
9 | {
10 | ///
11 | /// Get distance in meters between two points
12 | ///
13 | /// Coordinates of the first point
14 | /// Coordinates of the second point
15 | /// Distance between points in meters
16 | ///
17 | /// https://en.wikipedia.org/wiki/Haversine_formula
18 | /// reimplementation based on GeoCoordinate.GetDistanceTo System.Device.dll (available only for .NET Framework)
19 | ///
20 | public static float DistanceMeters(Coordinates first, Coordinates second)
21 | {
22 | double radius = 6376500.0;
23 | double d1 = first.Latitude * (Math.PI / 180.0);
24 | double num1 = first.Longitude * (Math.PI / 180.0);
25 | double d2 = second.Latitude * (Math.PI / 180.0);
26 | double num2 = second.Longitude * (Math.PI / 180.0) - num1;
27 | double d3 = Math.Pow(Math.Sin((d2 - d1) / 2.0), 2.0) + Math.Cos(d1) * Math.Cos(d2) * Math.Pow(Math.Sin(num2 / 2.0), 2.0);
28 |
29 | return (float)(radius * (2.0 * Math.Atan2(Math.Sqrt(d3), Math.Sqrt(1.0 - d3))));
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.API.Tests.Performance/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("CovidSafe.API.WebPerformanceAndLoadTests")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("CovidSafe.API.WebPerformanceAndLoadTests")]
13 | [assembly: AssemblyCopyright("Copyright © 2020")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("b5f256b9-c0dc-4ba6-9c60-7bcf8e13f15a")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
36 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.Entities/Geospatial/RegionBoundary.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using Newtonsoft.Json;
4 |
5 | namespace CovidSafe.Entities.Geospatial
6 | {
7 | ///
8 | /// Defines boundaries of a
9 | ///
10 | [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
11 | [Serializable]
12 | public class RegionBoundary
13 | {
14 | ///
15 | /// Maximum boundary coordinates
16 | ///
17 | [JsonProperty("Max", Required = Required.Always)]
18 | public Coordinates Max { get; set; }
19 | ///
20 | /// Maximum boundary coordinates
21 | ///
22 | [JsonProperty("Min", Required = Required.Always)]
23 | public Coordinates Min { get; set; }
24 |
25 | ///
26 | /// Creates a new instance
27 | ///
28 | public RegionBoundary()
29 | {
30 | }
31 |
32 | ///
33 | /// Creates a new instance
34 | ///
35 | /// Source
36 | public RegionBoundary(RegionBoundary boundary)
37 | {
38 | if (boundary != null)
39 | {
40 | this.Max = new Coordinates { Longitude = boundary.Max.Longitude, Latitude = boundary.Max.Latitude };
41 | this.Min = new Coordinates { Longitude = boundary.Min.Longitude, Latitude = boundary.Min.Latitude };
42 | }
43 | else
44 | {
45 | throw new ArgumentNullException(nameof(boundary));
46 | }
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.Entities/Geospatial/Coordinates.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using CovidSafe.Entities.Validation;
4 | using Newtonsoft.Json;
5 |
6 | namespace CovidSafe.Entities.Geospatial
7 | {
8 | ///
9 | /// Geographic location coordinates
10 | ///
11 | [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
12 | [Serializable]
13 | public class Coordinates : IValidatable
14 | {
15 | ///
16 | /// Maximum allowable latitude
17 | ///
18 | public const double MAX_LATITUDE = 90;
19 | ///
20 | /// Maximum allowable longitude
21 | ///
22 | public const double MAX_LONGITUDE = 180;
23 | ///
24 | /// Minimum allowable latitude
25 | ///
26 | public const double MIN_LATITUDE = -90;
27 | ///
28 | /// Minimum allowable longitude
29 | ///
30 | public const double MIN_LONGITUDE = -180;
31 |
32 | ///
33 | /// Latitude of coordinate
34 | ///
35 | [JsonProperty("lat", Required = Required.Always)]
36 | public double Latitude { get; set; }
37 | ///
38 | /// Longitude of coordinate
39 | ///
40 | [JsonProperty("lng", Required = Required.Always)]
41 | public double Longitude { get; set; }
42 |
43 | ///
44 | public RequestValidationResult Validate()
45 | {
46 | RequestValidationResult result = new RequestValidationResult();
47 |
48 | // Ensure lat/lng are within range
49 | result.Combine(Validator.ValidateLatitude(this.Latitude, nameof(this.Latitude)));
50 | result.Combine(Validator.ValidateLongitude(this.Longitude, nameof(this.Longitude)));
51 |
52 | return result;
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.DAL/Repositories/Cosmos/Records/CosmosRecord.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.DataAnnotations;
3 |
4 | using Microsoft.Azure.Cosmos;
5 | using Newtonsoft.Json;
6 |
7 | namespace CovidSafe.DAL.Repositories.Cosmos.Records
8 | {
9 | ///
10 | /// Cosmos Database record object
11 | ///
12 | /// Object type to store
13 | [JsonObject]
14 | public abstract class CosmosRecord
15 | {
16 | ///
17 | /// Unique identifier
18 | ///
19 | ///
20 | /// Do NOT set this to 'Required'. Let CosmosDB auto-generate this.
21 | ///
22 | [JsonProperty("id")]
23 | public string Id { get; set; }
24 | ///
25 | /// Partition Key value
26 | ///
27 | [JsonProperty("partitionKey", Required = Required.Always)]
28 | [Required]
29 | public string PartitionKey { get; set; }
30 | ///
31 | /// Timestamp of record database insert, in ms since UNIX epoch
32 | ///
33 | [JsonProperty("timestamp", Required = Required.Always)]
34 | [Required]
35 | public long Timestamp { get; set; }
36 | ///
37 | /// Object value
38 | ///
39 | [JsonProperty("Value", Required = Required.Always)]
40 | [Required]
41 | public T Value { get; set; }
42 | ///
43 | /// Record schema version
44 | ///
45 | [JsonProperty("version", Required = Required.Always)]
46 | public string Version { get; set; } = "";
47 |
48 | ///
49 | /// Creates a new instance
50 | ///
51 | public CosmosRecord()
52 | {
53 | // Set default local values
54 | this.Id = Guid.NewGuid().ToString();
55 | this.Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.DAL/Repositories/Cosmos/Client/CosmosConnectionFactory.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Azure.Cosmos;
2 | using Microsoft.Extensions.Configuration;
3 |
4 | namespace CovidSafe.DAL.Repositories.Cosmos.Client
5 | {
6 | ///
7 | /// Helper class for creating a Cosmos account connection
8 | ///
9 | public class CosmosConnectionFactory
10 | {
11 | ///
12 | /// CosmosDB Connection String
13 | ///
14 | private string _connectionString;
15 | ///
16 | /// Default
17 | ///
18 | public CosmosClientOptions DefaultClientOptions { get; private set; } = new CosmosClientOptions
19 | {
20 | ApplicationRegion = Regions.EastUS
21 | };
22 |
23 | ///
24 | /// Creates a new instance
25 | ///
26 | /// Database connection string
27 | public CosmosConnectionFactory(string connectionString)
28 | {
29 | // Store local variables
30 | this._connectionString = connectionString;
31 | }
32 |
33 | ///
34 | /// Creates a new instance with default
35 | ///
36 | ///
37 | /// instance
38 | public CosmosClient GetClient()
39 | {
40 | // Use overload for simplicity
41 | return this.GetClient(this.DefaultClientOptions);
42 | }
43 |
44 | ///
45 | /// Creates a new instance with a custom configuration
46 | ///
47 | ///
48 | /// instance
49 | public CosmosClient GetClient(CosmosClientOptions options)
50 | {
51 | return new CosmosClient(this._connectionString, options);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.API/CovidSafe.API.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 | $(NoWarn);1591
6 | netcoreapp2.1
7 | ca6741f8-30f9-472c-a5c3-c6e0a78d1961
8 | Windows
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.Entities/Messages/BluetoothSeedMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using CovidSafe.Entities.Validation;
4 | using Newtonsoft.Json;
5 |
6 | namespace CovidSafe.Entities.Messages
7 | {
8 | ///
9 | /// Bluetooth seed
10 | ///
11 | [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
12 | [Serializable]
13 | public class BluetoothSeedMessage : IValidatable
14 | {
15 | ///
16 | /// Start of validity period
17 | ///
18 | ///
19 | /// Reported in milliseconds (ms) since the UNIX epoch.
20 | ///
21 | [JsonProperty("beginTimestampMs", Required = Required.Always)]
22 | public long BeginTimestamp { get; set; }
23 | ///
24 | /// End of validity period
25 | ///
26 | ///
27 | /// Reported in milliseconds (ms) since the UNIX epoch.
28 | ///
29 | [JsonProperty("endTimestampMs", Required = Required.Always)]
30 | public long EndTimestamp { get; set; }
31 | ///
32 | /// Seed backing field
33 | ///
34 | [NonSerialized]
35 | private string _seed;
36 | ///
37 | /// Seed value
38 | ///
39 | [JsonProperty("seed", Required = Required.Always)]
40 | public string Seed
41 | {
42 | get { return this._seed; }
43 | set { this._seed = value; }
44 | }
45 |
46 | ///
47 | public RequestValidationResult Validate()
48 | {
49 | RequestValidationResult result = new RequestValidationResult();
50 |
51 | // Seed validation
52 | result.Combine(Validator.ValidateSeed(this.Seed, nameof(this.Seed)));
53 |
54 | // Ensure times are valid
55 | result.Combine(Validator.ValidateTimestamp(this.BeginTimestamp, parameterName: nameof(this.BeginTimestamp)));
56 | result.Combine(Validator.ValidateTimestamp(this.EndTimestamp, parameterName: nameof(this.EndTimestamp)));
57 | result.Combine(Validator.ValidateTimeRange(this.BeginTimestamp, this.EndTimestamp));
58 |
59 | return result;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.Entities/Messages/NarrowcastMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using CovidSafe.Entities.Geospatial;
4 | using CovidSafe.Entities.Validation;
5 | using CovidSafe.Entities.Validation.Resources;
6 | using Newtonsoft.Json;
7 |
8 | namespace CovidSafe.Entities.Messages
9 | {
10 | ///
11 | /// Narrowcast message container
12 | ///
13 | [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
14 | [Serializable]
15 | public class NarrowcastMessage : IValidatable
16 | {
17 | ///
18 | /// targeted by this
19 | ///
20 | [JsonProperty("Area", Required = Required.Always)]
21 | public NarrowcastArea Area { get; set; }
22 | ///
23 | /// Internal UserMessage backing field
24 | ///
25 | [NonSerialized]
26 | private string _userMessage;
27 | ///
28 | /// Message displayed to user on positive match
29 | ///
30 | [JsonProperty("userMessage", NullValueHandling = NullValueHandling.Ignore)]
31 | public string UserMessage
32 | {
33 | get { return this._userMessage; }
34 | set { _userMessage = value; }
35 | }
36 |
37 | ///
38 | public RequestValidationResult Validate()
39 | {
40 | RequestValidationResult result = new RequestValidationResult();
41 |
42 | // Validate provided NarrowcastArea
43 | if (this.Area != null)
44 | {
45 | // Use NarrowcastArea.Validate()
46 | result.Combine(this.Area.Validate());
47 | }
48 | else
49 | {
50 | result.Fail(
51 | RequestValidationIssue.InputEmpty,
52 | nameof(this.Area),
53 | ValidationMessages.EmptyAreas
54 | );
55 | }
56 |
57 | // Validate message
58 | if (String.IsNullOrEmpty(this.UserMessage))
59 | {
60 | result.Fail(
61 | RequestValidationIssue.InputEmpty,
62 | nameof(this.UserMessage),
63 | ValidationMessages.EmptyMessage
64 | );
65 | }
66 |
67 | return result;
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.API/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 |
4 | using Microsoft.AspNetCore;
5 | using Microsoft.AspNetCore.Hosting;
6 | using Microsoft.Azure.KeyVault;
7 | using Microsoft.Azure.Services.AppAuthentication;
8 | using Microsoft.Extensions.Configuration;
9 | using Microsoft.Extensions.Configuration.AzureKeyVault;
10 |
11 | namespace CovidSafe.API
12 | {
13 | ///
14 | /// Main application class
15 | ///
16 | ///
17 | /// CS1591: Ignores missing documentation warnings.
18 | /// CodeCoverageExclusion: Generic program entry point.
19 | ///
20 | [ExcludeFromCodeCoverage]
21 | #pragma warning disable CS1591
22 | public class Program
23 | {
24 | ///
25 | /// Application entry point
26 | ///
27 | /// Command-line arguments
28 | public static void Main(string[] args)
29 | {
30 | CreateWebHostBuilder(args).Build().Run();
31 | }
32 |
33 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
34 | WebHost.CreateDefaultBuilder(args)
35 | .ConfigureAppConfiguration((context, config) =>
36 | {
37 | // We need to generate the original config before we can get the Key Vault URL
38 | var builtConfig = config.Build();
39 |
40 | // Is there a Key Vault URL specified?
41 | if(!String.IsNullOrEmpty(builtConfig["KeyVaultUrl"]))
42 | {
43 | // Get Managed Service Identity token
44 | AzureServiceTokenProvider tokenProvider = new AzureServiceTokenProvider();
45 | KeyVaultClient kvClient = new KeyVaultClient(
46 | new KeyVaultClient.AuthenticationCallback(
47 | tokenProvider.KeyVaultTokenCallback
48 | )
49 | );
50 |
51 | config.AddAzureKeyVault(
52 | builtConfig["KeyVaultUrl"],
53 | kvClient,
54 | new DefaultKeyVaultSecretManager()
55 | );
56 | }
57 | })
58 | .UseStartup();
59 | }
60 | #pragma warning restore CS1591
61 | }
62 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.DAL/Helpers/PrecisionHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CovidSafe.DAL.Helpers
4 | {
5 | ///
6 | /// Helper functions for working with rounding numbers with controlled precision.
7 | ///
8 | public static class PrecisionHelper
9 | {
10 | public static int GetStep(int precision)
11 | {
12 | int bits = Math.Max(8 - precision, 0);
13 | return 1 << bits;
14 | }
15 |
16 | ///
17 | /// Rounds given number and precision parameter.
18 | ///
19 | /// Any integer
20 | /// Precision parameter, any integer
21 | /// Nearest number aligned with precision grid, equal to d or closer to 0.
22 | /// Precision=0 maps any int from [-256, 256] to 0.
23 | /// Precision>=8 maps any int to itself
24 | public static int Round(int d, int precision)
25 | {
26 | int bits = Math.Max(8 - precision, 0);
27 | return d >= 0 ? d >> bits << bits : -(-d >> bits << bits);
28 | }
29 |
30 | ///
31 | /// Rounds given number and precision parameter. Method for legacy API that supports float values.
32 | ///
33 | /// Any double value
34 | /// Precision parameter, any integer
35 | /// Nearest number aligned with precision grid, equal to d or closer to 0.
36 | /// Precision=0 maps any double from [-256, 256] to 0.
37 | /// Precision=9 maps any double to its int lower bound
38 | public static int Round(double d, int precision)
39 | {
40 | return Round((int)d, precision);
41 | }
42 |
43 | ///
44 | /// For given number and precision parameter, returns range [xmin, xmax] containing the number,
45 | /// where xmin < xmax, xmin and xmax are to numbers next to each other on the grid aligned with given precision
46 | /// Rounds given number and precision parameter. Method for legacy API that supports float values.
47 | ///
48 | /// Any double number
49 | /// Precision parameter, any integer
50 | /// Tuple of ints (xmin, xmax)
51 | public static Tuple GetRange(int d, int precision)
52 | {
53 | int rounded = Round(d, precision);
54 | int step = GetStep(precision);
55 |
56 | if (rounded == 0)
57 | return Tuple.Create(-step, step);
58 | else if (rounded > 0)
59 | return Tuple.Create(rounded, rounded + step);
60 | return Tuple.Create(rounded - step, rounded);
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.DAL/Repositories/Cosmos/Records/MessageContainerRecord.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 |
3 | using CovidSafe.DAL.Helpers;
4 | using CovidSafe.Entities.Geospatial;
5 | using CovidSafe.Entities.Messages;
6 | using Newtonsoft.Json;
7 |
8 | namespace CovidSafe.DAL.Repositories.Cosmos.Records
9 | {
10 | ///
11 | /// implementation of
12 | ///
13 | public class MessageContainerRecord : CosmosRecord
14 | {
15 | ///
16 | /// Boundary allowed by region
17 | ///
18 | [JsonProperty("RegionBoundary", Required = Required.Always)]
19 | [Required]
20 | public RegionBoundary RegionBoundary { get; set; }
21 |
22 | ///
23 | /// region
24 | ///
25 | [JsonProperty("Region", Required = Required.Always)]
26 | [Required]
27 | public Region Region { get; set; }
28 |
29 | ///
30 | /// Size of the record , in bytes
31 | ///
32 | [JsonProperty("size", Required = Required.Always)]
33 | [Required]
34 | public long Size { get; set; }
35 |
36 | ///
37 | /// Current version of record schema
38 | ///
39 | [JsonIgnore]
40 | public const string CURRENT_RECORD_VERSION = "4.0.0";
41 |
42 | ///
43 | /// Creates a new instance
44 | ///
45 | public MessageContainerRecord()
46 | {
47 | }
48 |
49 | ///
50 | /// Creates a new instance
51 | ///
52 | /// to store
53 | public MessageContainerRecord(MessageContainer report)
54 | {
55 | this.Size = PayloadSizeHelper.GetSize(report);
56 | this.Value = report;
57 | this.Version = CURRENT_RECORD_VERSION;
58 | }
59 |
60 | ///
61 | /// Generates a new Partition Key value for the record
62 | ///
63 | /// Partition Key value
64 | public static string GetPartitionKey(Region region)
65 | {
66 | return $"{region.LatitudePrefix},{region.LongitudePrefix},{region.Precision}";
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.DAL/Repositories/Cosmos/Client/CosmosContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Azure.Cosmos;
2 | using Microsoft.Extensions.Options;
3 |
4 | namespace CovidSafe.DAL.Repositories.Cosmos.Client
5 | {
6 | ///
7 | /// Cosmos database context
8 | ///
9 | public class CosmosContext
10 | {
11 | ///
12 | /// reference for
13 | ///
14 | ///
15 | /// Enables configuration change detection without restarting service
16 | ///
17 | private IOptionsMonitor _schemaConfig { get; set; }
18 | ///
19 | /// instance
20 | ///
21 | public CosmosClient Client { get; private set; }
22 | ///
23 | /// reference
24 | ///
25 | public Database Database { get; private set; }
26 | ///
27 | /// instance
28 | ///
29 | public CosmosSchemaConfigurationSection SchemaOptions
30 | {
31 | get
32 | {
33 | return this._schemaConfig.CurrentValue;
34 | }
35 | }
36 |
37 | ///
38 | /// Creates a new instance
39 | ///
40 | /// Database connection factory instance
41 | /// Schema configuration provider
42 | public CosmosContext(CosmosConnectionFactory connectionFactory, IOptionsMonitor schemaConfig)
43 | {
44 | // Set local variables
45 | this._schemaConfig = schemaConfig;
46 | this.Client = connectionFactory.GetClient();
47 |
48 | // Create Database object reference
49 | this.Database = this.Client.GetDatabase(this.SchemaOptions.DatabaseName);
50 | }
51 |
52 | ///
53 | /// Gets a reference to the specified from the
54 | /// local instance
55 | ///
56 | /// Target name
57 | /// reference
58 | public Container GetContainer(string containerName)
59 | {
60 | return this.Database.GetContainer(containerName);
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.API/Swagger/SwaggerConfigureOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 |
3 | using Microsoft.AspNetCore.Mvc.ApiExplorer;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Microsoft.Extensions.Options;
6 | using Microsoft.OpenApi.Models;
7 | using Swashbuckle.AspNetCore.SwaggerGen;
8 |
9 | namespace CovidSafe.API.Swagger
10 | {
11 | ///
12 | /// Configures Swagger generation options
13 | ///
14 | ///
15 | /// CodeCoverageExclusion: Does not provide core functionality.
16 | ///
17 | [ExcludeFromCodeCoverage]
18 | public class SwaggerConfigureOptions : IConfigureOptions
19 | {
20 | ///
21 | /// Configures API version description handling
22 | ///
23 | readonly IApiVersionDescriptionProvider _provider;
24 |
25 | ///
26 | /// Creates a new instance
27 | ///
28 | /// API Version Description provider implementation
29 | public SwaggerConfigureOptions(IApiVersionDescriptionProvider provider) => this._provider = provider;
30 |
31 | ///
32 | public void Configure(SwaggerGenOptions options)
33 | {
34 | // Generate a SwaggerDoc for each API version discovered in the project
35 | foreach(var description in this._provider.ApiVersionDescriptions)
36 | {
37 | options.SwaggerDoc(
38 | description.GroupName,
39 | CreateInfoForApiVersion(description)
40 | );
41 | }
42 | }
43 |
44 | ///
45 | /// Builds the description portion of a SwaggerDoc for each API version in the project
46 | ///
47 | /// Target API version information
48 | ///
49 | public static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
50 | {
51 | OpenApiInfo info = new OpenApiInfo()
52 | {
53 | Description = "Enables communication between CovidSafe client applications and backend data storage.",
54 | Title = "CovidSafe API",
55 | Version = description.ApiVersion.ToString(),
56 | };
57 |
58 | if(description.IsDeprecated)
59 | {
60 | info.Description = "This API version has been deprecated. Please migrate to a newer version.";
61 | }
62 |
63 | return info;
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.Entities/Validation/RequestValidationResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | using Newtonsoft.Json;
5 | using ProtoBuf;
6 |
7 | namespace CovidSafe.Entities.Validation
8 | {
9 | ///
10 | /// Object validation summary
11 | ///
12 | [JsonObject]
13 | [ProtoContract]
14 | public class RequestValidationResult
15 | {
16 | ///
17 | /// Tells if the result passed (true if yes, false if no)
18 | ///
19 | [JsonIgnore]
20 | [ProtoIgnore]
21 | public bool Passed
22 | {
23 | get
24 | {
25 | return this.Failures.Count == 0;
26 | }
27 | }
28 |
29 | ///
30 | /// Collection of objects
31 | ///
32 | [JsonProperty("validationFailures")]
33 | [ProtoMember(1)]
34 | public List Failures { get; set; } = new List();
35 |
36 | ///
37 | /// Add failures to this object
38 | ///
39 | public void Combine(RequestValidationResult other)
40 | {
41 | this.Failures.AddRange(other.Failures);
42 | }
43 |
44 | ///
45 | /// Report a new
46 | ///
47 | /// classification
48 | /// Failing property name
49 | /// Failure text
50 | public void Fail(RequestValidationIssue issue, string property, string message)
51 | {
52 | this.Failures.Add(new RequestValidationFailure
53 | {
54 | Issue = issue,
55 | Message = message
56 | });
57 | }
58 |
59 | ///
60 | /// Report a new
61 | ///
62 | /// classification
63 | /// Failing property name
64 | /// Failure text
65 | /// Failure text arguments (akin to
66 | public void Fail(RequestValidationIssue issue, string property, string message, params string[] args)
67 | {
68 | // Use overload
69 | this.Fail(issue, property, String.Format(message, args));
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.API/v20200611/Protos/Interactions.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package Corona;
4 | option csharp_namespace = "CovidSafe.API.v20200611.Protos";
5 |
6 | // Geographic location at a point in time;
7 | message Area {
8 | // Geographic location coordinates;
9 | Location location = 1;
10 | // Radius, in meters, of coordinate coverage;
11 | float radius_meters = 2;
12 | // Alert start time;
13 | int64 begin_time = 3;
14 | // Alert end time;
15 | int64 end_time = 4;
16 | }
17 |
18 | // Geographic location;
19 | message Location {
20 | // Latitude;
21 | double latitude = 1;
22 | // Longitude;
23 | double longitude = 2;
24 | }
25 |
26 | // Message metadata object;
27 | message MessageInfo {
28 | // Unique Message identifier;
29 | string message_id = 1;
30 | // Message timestamp;
31 | int64 message_timestamp = 2;
32 | }
33 |
34 | // Phone -> Server;
35 | // Request for metadata of added or updated messages based on client timestamp;
36 | message MessageListRequest {
37 | // Region targeted by request;
38 | Region region = 1;
39 | // Timestamp of most recent API request from the client;
40 | int64 last_query_time = 2;
41 | }
42 |
43 | // Server -> Phone;
44 | // Collection of MessageInfo matching MessageListRequest;
45 | message MessageListResponse {
46 | // Matching Message metadata collection;
47 | repeated MessageInfo message_info = 1;
48 | // Latest Message timestamp included in the MessageInfo collection;
49 | int64 max_response_timestamp = 2;
50 | }
51 |
52 | // Phone -> Server GetMessages(new_message_ids);
53 | // Request to download the details of given query ids;
54 | message MessageRequest {
55 | // Collection of Message metadata, used to pull specific Message objects;
56 | repeated MessageInfo requested_queries = 1;
57 | }
58 |
59 | // Server -> Phone (list of messages corresponding to touch points where
60 | // infection can occur);
61 | message MessageResponse {
62 | // Collection of Narrowcast messages matching MessageRequest criteria
63 | repeated NarrowcastMessage narrowcast_messages = 1;
64 | }
65 |
66 | // Phone <-> Server;
67 | // Narrowcast Message object;
68 | message NarrowcastMessage {
69 | // Message displayed to user on match;
70 | string user_message = 1;
71 | // Area of infection risk;
72 | Area area = 2;
73 | }
74 |
75 | // Geographic region quantized by precision of lat/long;
76 | message Region {
77 | // Latitude source, no decimal;
78 | int32 latitude_prefix = 1;
79 | // Longitude source, no decimal;
80 | int32 longitude_prefix = 2;
81 | // Mantissa mask/precision bits;
82 | int32 precision = 3;
83 | }
84 |
85 | // Request error message;
86 | message RequestValidationResult {
87 | // Collection of individual request validation failures;
88 | repeated RequestValidationFailure failures = 1;
89 | }
90 |
91 | // Individual request failure message;
92 | message RequestValidationFailure {
93 | // Validation error message;
94 | string message = 1;
95 | // Name of parameter failing validation;
96 | string property = 2;
97 | }
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.DAL/Repositories/IMessageContainerRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | using CovidSafe.Entities.Geospatial;
6 | using CovidSafe.Entities.Messages;
7 |
8 | namespace CovidSafe.DAL.Repositories
9 | {
10 | ///
11 | /// repository definition
12 | ///
13 | public interface IMessageContainerRepository : IRepository
14 | {
15 | ///
16 | /// Pulls a list of the latest objects, based on client parameters
17 | ///
18 | /// Target
19 | /// Timestamp of latest client for region, in ms since UNIX epoch
20 | /// Cancellation token
21 | /// Collection of objects
22 | Task> GetLatestAsync(Region region, long lastTimestamp, CancellationToken cancellationToken = default);
23 | ///
24 | /// Retrieves the data size, in bytes, based on a region's latest data
25 | ///
26 | /// Target
27 | /// Timestamp of latest client for region, in ms since UNIX epoch
28 | /// Cancellation token
29 | /// Data size, in bytes
30 | Task GetLatestRegionSizeAsync(Region region, long lastTimestamp, CancellationToken cancellationToken = default);
31 | ///
32 | /// Pulls a collection of objects, based on provided identifiers
33 | ///
34 | /// Collection of identifiers
35 | /// Cancellation token
36 | /// Collection of objects
37 | Task> GetRangeAsync(IEnumerable ids, CancellationToken cancellationToken);
38 | ///
39 | /// Store a new in the repository
40 | ///
41 | /// to store
42 | /// Target
43 | /// Cancellation token
44 | /// Unique identifier of stored object
45 | Task InsertAsync(MessageContainer message, Region region, CancellationToken cancellationToken = default);
46 | ///
47 | /// Store a new in the repository to multiple regions
48 | ///
49 | /// to store
50 | /// Target
51 | /// Cancellation token
52 | Task InsertAsync(MessageContainer message, IEnumerable regions, CancellationToken cancellationToken = default);
53 | }
54 | }
--------------------------------------------------------------------------------
/CovidSafe/CovidSafe.API/v20200505/Protos/Interactions.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package Corona;
4 | option csharp_namespace = "CovidSafe.API.v20200505.Protos";
5 |
6 | // Phone -> Server;
7 | // Get list of messages queued for a region since last query time
8 | message MessageListRequest {
9 | Region region = 1;
10 | int64 last_query_time = 2;
11 | }
12 |
13 | // Server -> Phone; list of messages for region;
14 | // as_of tells a client the current server time to enable clock synchronization
15 | message MessageListResponse {
16 | repeated MessageInfo message_info = 1;
17 | int64 max_response_timestamp = 2;
18 | }
19 |
20 | // Phone -> Server GetMessages(new_message_ids)
21 | // Request to download the details of given query ids
22 | message MessageRequest { repeated MessageInfo requested_queries = 1; }
23 |
24 | // Server -> Phone (list of messages corresponding to touch points where
25 | // infection can occour)
26 | message MatchMessageResponse {
27 | repeated MatchMessage match_messages = 1;
28 | }
29 |
30 | message MatchMessage {
31 | // Not used at the moment. Will eventually express a boolean relationship between
32 | // elements in the area_match and bluetooth_match collections
33 | string bool_expression = 1;
34 | // at or around for more than around