diff --git a/.gitignore b/.gitignore index 1ade888129707fa0cd8bd18bc1c0ba23ac113ce2..4507e2b5d1d158b4b46d9160ce12368fb222a23a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ ## Ignore .openapi-generator Folder ## .openapi-generator/ -server/src/Org.OpenAPITools/.gitignore +server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/.gitignore # Visual Studio cache/options directory .vs/ @@ -9,6 +9,9 @@ server/src/Org.OpenAPITools/.gitignore # Visual Studio solution *.sln +# generated project-files +*.csproj + # build-jobs build.* @@ -22,7 +25,7 @@ server/programs/MongoDB/ !server/programs/MongoDB/readme.md #generated readme -server/README.md +server/worldstorage/README.md # all generated directories Attributes/ @@ -36,11 +39,8 @@ OpenAPI/ Properties/ wwwroot/ -# generated project-files -*.csproj - # generated Program.cs -Program.cs +#Program.cs # backup-files *.bak diff --git a/.gitmodules b/.gitmodules index 120fcda39cfee4122d0a409175f301b4a5405dda..54f9b6352793921f7bc974319072781cdb6780da 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,7 @@ [submodule "arf005"] path = arf005 url = git@forge.etsi.org:arf/arf005.git - branch = develop \ No newline at end of file + branch = develop +[submodule "openapi"] + path = openapi + url = https://forge.etsi.org/rep/arf/openapi.git diff --git a/server/.openapi-generator-ignore b/.openapi-generator-ignore similarity index 84% rename from server/.openapi-generator-ignore rename to .openapi-generator-ignore index cbd23cf64baafbfa18079cf451ee244ab18af239..38a29adafc14ce78675a6e1e9d59359e5eaf3b8d 100644 --- a/server/.openapi-generator-ignore +++ b/.openapi-generator-ignore @@ -22,16 +22,20 @@ # Then explicitly reverse the ignore rule for a single file: #!docs/README.md +# +# ETSI - ISG - ARF +# +**\ETSI.ARF.OpenAPI.WorldStorage.csproj +# Modules to initialize World Storage database, server.... +**/Program.cs **/Startup.cs -**/appsettings.json **/Dockerfile -**/docker-compose.yml -**/.openapi-generator-ignore - +**/appsettings.json -# ARF # Implementation of REST request and database functionalities -# -**/ControllersImpl -**/Services +**/ETSI-ARF + +# Design of some web pages +**/wwwroot/portal +**/wwwroot/index.html \ No newline at end of file diff --git a/arf005 b/arf005 deleted file mode 160000 index 533b9d3198b772c7b628b0ab0e0a144b89966b46..0000000000000000000000000000000000000000 --- a/arf005 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 533b9d3198b772c7b628b0ab0e0a144b89966b46 diff --git a/build-iis-docker-ws.bat b/build-iis-docker-ws.bat new file mode 100644 index 0000000000000000000000000000000000000000..ef7ec22c27c2b9398e152f0e1807333decda86db --- /dev/null +++ b/build-iis-docker-ws.bat @@ -0,0 +1,2 @@ +cd server\worldstorage\src\ETSI.ARF.OpenAPI.WorldStorage +docker build -t etsi.arf.openapi.worldstorage . \ No newline at end of file diff --git a/docker-compose.bat b/docker-compose.bat new file mode 100644 index 0000000000000000000000000000000000000000..81eda89a647ab99a76ade58d3594e5abba9f2b22 --- /dev/null +++ b/docker-compose.bat @@ -0,0 +1 @@ +docker-compose up -d --force-recreate --remove-orphans \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..0f23eca4742c5af318b6f37041442e46cb8c9a9a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,45 @@ +# Please refer https://aka.ms/HTTPSinContainer on how to setup an https developer certificate for your ASP .NET Core service. + +name: etsi_arf_server +version: '1.0' + +networks: + minionetwork: + driver: bridge + vpcbr: + ipam: + config: + - subnet: 172.24.30.0/24 + +services: + mongod: + container_name: etsi_mongodb + image: mongo:latest + environment: + - PUID=1000 + - PGID=1000 + volumes: + # - mongodbdata:/data/mongodb:rw + - C:/Fraunhofer/Servers/ETSI/MongoDB/data:/data/db:rw + - C:/Fraunhofer/Servers/ETSI/MongoDB/data-dump:/data/db-dump:rw + # - D:/Fraunhofer/Servers/ETSI/MongoDB/mongod.yaml:/etc/mongod.conf:rw + ports: + - 27037:27017 + - 27038:27018 + networks: + vpcbr: + ipv4_address: 172.24.30.101 + restart: unless-stopped + + etsiapi: + container_name: etsi_arf_iis_server + image: etsi.arf.openapi.worldstorage:latest + volumes: + - C:/Fraunhofer/Servers/ETSI/appsettings.json:/app/appsettings.json:rw + - C:/Fraunhofer/Servers/ETSI/WWWRoot:/app/wwwroot:rw + ports: + - 8082:44301 + networks: + vpcbr: + ipv4_address: 172.24.30.100 + restart: unless-stopped diff --git a/openapi b/openapi new file mode 160000 index 0000000000000000000000000000000000000000..08aa3ca82b2f3d78ee765df3e0d9046b3ab81ba1 --- /dev/null +++ b/openapi @@ -0,0 +1 @@ +Subproject commit 08aa3ca82b2f3d78ee765df3e0d9046b3ab81ba1 diff --git a/readme.md b/readme.md index 239c654ec05abb4110cc7e9b95e1dffc5d413f3f..7715c1703f55da421ea836d5522282614ac07657 100644 --- a/readme.md +++ b/readme.md @@ -11,7 +11,7 @@ --- -# Description +# Description - Version for STF 669 This repo should be used to construct a complete ASP-Net REST server compliant to the ARF World Storage API. It uses auto-generated ASP.NET server code. We propose to use the open source OpenAPI-Generator for this. @@ -34,7 +34,7 @@ What you need: 1. Installed npm: https://phoenixnap.com/kb/install-node-js-npm-on-windows 2. Installed openapi generator with npm: https://openapi-generator.tech/docs/installation/ -3. Installed docker (if you want to use it): https://www.docker.com/get-started +3. Installed docker (recommanded): https://www.docker.com/get-started # Code Generation @@ -43,10 +43,10 @@ We provided the file `.openapi-generator-ignore` in `server`, which prevents ope ## Auto-generate server code Open a command shell and execute: ``` - openapi-generator-cli generate -i arf005\API\openapi.yaml -g aspnetcore -o server + npx openapi-generator-cli generate -i openapi/API/worldstorage/worldstorageopenapi.yaml --additional-properties aspnetCoreVersion=5.0,packageName=ETSI.ARF.OpenAPI.WorldStorage,operationModifier=abstract,classModifier=abstract -g aspnetcore -o server/worldstorage ``` -Open the solution `Org.OpenAPITools.sln` (folder `server`) in Visual Studio: +Open the solution `ETSI.ARF.OpenAPI.WorldStorage.sln` (folder `server/worldstorage`) in Visual Studio: ## In Visual Studio Open `NuGet Package Manager` and add `MongoDB.Driver`. @@ -54,37 +54,41 @@ Open `NuGet Package Manager` and add `MongoDB.Driver`. ### File adaptations Change version number in all files if a new version is provided. -### In the folder `Controllers` -Change "`public class`" to "`public abstract class`". +### Implementation folder (new) +All custom files are now in the folder 'ETSI-ARF'. Nothing to do adapt manually after generation. But you have to provide the implementation of new endpoints because they are now directly declared as abstract (default). -Compare files folder in "`ControllersImpl`" with the corresponding files in "`Controllers`" and adapt if necessary. +The folder contains following subfolders: +`Controllers` -Methods should be the same with "`override`" instead of "`virtual`". +Compare files folder in "`ETSI-ARF/Controllers`" with the corresponding files in "`Controllers`" and insert if necessary the new methods. + +Methods should be the same with "`override`" instead of "`abstract`". --- #### If some files are missing (and only then!) -Copy them from folder `Controllers`, rename them (append `Impl`) and handle them like the already existing files, i.e.: -Change classnames by appending `Impl` to the original classnames (and change filenames accordingly) and inherit from original class in `Controllers` (instead of `ControllerBase`) - -..and replace `virtual` by `override` with all methods. - Add ``` - using Org.OpenAPITools.Services; using MongoDB.Driver; ``` -Add a private readonly service class variable like in the already existing files. +Add a private readonly service class variable like in the already existing files, e.g.: +``` +private readonly TrackableService _trackableService; +``` Add a constructor with this service class variable like in the already existing files. +``` +public TrackablesApiControllerImpl(TrackableService trackableService) +{ + _trackableService = trackableService; +} +``` -Remove sample code and replace it by using the appropriate methods of the corresponding classes in the folder `Services` (which you may be have to create). - ---- +Implement endpoint code using the appropriate MogoDB methods of the corresponding classes from the folder `Services` (which you may be have to create). ### In the folder `Models` -Add to the classes to be stored in the database (i.e. `Trackable.cs`, `WorldAnchor.cs`, `WorldLink.cs`) : +Add to the classes to be stored in the database (i.e. `Trackable.cs`, `WorldAnchor.cs`, `WorldLink.cs`) inherited from `IModels` (definition of the extra UUID): ``` using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; @@ -109,6 +113,17 @@ If you don't have a MongoDB, follow the instructions in `readme.md` in `server/p ...and put MongoDB in folder `server/programs/MongoDB` (download MongoDB as zip-file from https://www.mongodb.com/try/download/community and unzip the file into this directory, so that the bin-directory is in this folder). +## How to dump database +Execute the following command in docker: +``` + mongodump --db **insert database_name** --out /data-dump/`date +"%Y-%m-%d"` +``` + +## How to import database +Execute the following command in docker: +``` + mongorestore --db **insert database_name** **insert path_to_bson_file** +``` # Use in Visual Studio Make sure, that an instance of MongoDB is running. @@ -116,38 +131,28 @@ Make sure, that an instance of MongoDB is running. Start application with IIS Express. -# Use within a Docker +# Use API within a Docker + +## Creating the IIS docker ## Remove the substring `src/Org.OpenAPITools/` in Dockerfile (if not already done) -open a command shell and generate docker by executing in `server/src/Org.OpenAPITools`: +open a command shell and generate docker by executing: ``` - docker build -t org.openapitools . + build-iis-docker-ws.bat ``` -## How to start +## How to start (with Docker-Compose) The easiest way is to use docker-compose: -Open a command shell and use docker-compose (if necessary adapt docker-compose.yml) by executing in `server/src/Org.OpenAPITools`: +Open a command shell and use docker-compose (if necessary adapt docker-compose.yml) by executing`: ``` - docker-compose up --force-recreate --remove-orphan --detach + docker-compose.bat ``` Open http://localhost:8080/openapi/index.html in a web-browser, if you want to check the functionalities using SwaggerUI ## How to stop -Open a command shell by executing in `server/src/Org.OpenAPITools`: +Open a command shell by executing in `server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage`: ``` docker-compose down ``` - -## How to dump database -Execute the following command in docker: -``` - mongodump --db **insert database_name** --out /data-dump/`date +"%Y-%m-%d"` -``` - -## How to import database -Execute the following command in docker: -``` - mongorestore --db **insert database_name** **insert path_to_bson_file** -``` diff --git a/server/worldanalysis/.openapi-generator-ignore b/server/worldanalysis/.openapi-generator-ignore new file mode 100644 index 0000000000000000000000000000000000000000..7484ee590a3894506cf063799b885428f95a71be --- /dev/null +++ b/server/worldanalysis/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/server/worldanalysis/README.md b/server/worldanalysis/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ac3ce3def60533a7bcacb5549e0767e71ac7ee5f --- /dev/null +++ b/server/worldanalysis/README.md @@ -0,0 +1,50 @@ +# ETSI.ARF.OpenAPI.WorldAnalysis - ASP.NET Core 5.0 Server + +API ensuring interoperability between Scene Management and a World Analysis service + +## Upgrade NuGet Packages + +NuGet packages get frequently updated. + +To upgrade this solution to the latest version of all NuGet packages, use the dotnet-outdated tool. + + +Install dotnet-outdated tool: + +``` +dotnet tool install --global dotnet-outdated-tool +``` + +Upgrade only to new minor versions of packages + +``` +dotnet outdated --upgrade --version-lock Major +``` + +Upgrade to all new versions of packages (more likely to include breaking API changes) + +``` +dotnet outdated --upgrade +``` + + +## Run + +Linux/OS X: + +``` +sh build.sh +``` + +Windows: + +``` +build.bat +``` +## Run in Docker + +``` +cd src/ETSI.ARF.OpenAPI.WorldAnalysis +docker build -t etsi.arf.openapi.worldanalysis . +docker run -p 5000:8080 etsi.arf.openapi.worldanalysis +``` diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/.gitignore b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..1ee53850b84cd478da00264a919c8180a746056e --- /dev/null +++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/.gitignore @@ -0,0 +1,362 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Dockerfile b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..1203a89ba3579798b30bb4b67e45538b2c42d7b5 --- /dev/null +++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Dockerfile @@ -0,0 +1,32 @@ +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +# Container we use for final publish +FROM mcr.microsoft.com/dotnet/core/aspnet:5.0-buster-slim AS base +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +# Build container +FROM mcr.microsoft.com/dotnet/core/sdk:5.0-buster AS build + +# Copy the code into the container +WORKDIR /src +COPY ["src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI.ARF.OpenAPI.WorldAnalysis.csproj", "ETSI.ARF.OpenAPI.WorldAnalysis/"] + +# NuGet restore +RUN dotnet restore "ETSI.ARF.OpenAPI.WorldAnalysis/ETSI.ARF.OpenAPI.WorldAnalysis.csproj" +COPY ["src/ETSI.ARF.OpenAPI.WorldAnalysis/", "ETSI.ARF.OpenAPI.WorldAnalysis/"] + +# Build the API +WORKDIR "ETSI.ARF.OpenAPI.WorldAnalysis" +RUN dotnet build "ETSI.ARF.OpenAPI.WorldAnalysis.csproj" -c Release -o /app/build + +# Publish it +FROM build AS publish +RUN dotnet publish "ETSI.ARF.OpenAPI.WorldAnalysis.csproj" -c Release -o /app/publish + +# Make the final image for publishing +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "ETSI.ARF.OpenAPI.WorldAnalysis.dll"] diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Program.cs b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Program.cs new file mode 100644 index 0000000000000000000000000000000000000000..95e22062027cf8674a23366f548a3e07c48c5d15 --- /dev/null +++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Program.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace ETSI.ARF.OpenAPI.WorldAnalysis +{ + /// <summary> + /// Program + /// </summary> + public class Program + { + /// <summary> + /// Main + /// </summary> + /// <param name="args"></param> + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + /// <summary> + /// Create the host builder. + /// </summary> + /// <param name="args"></param> + /// <returns>IHostBuilder</returns> + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup<Startup>() + .UseUrls("http://0.0.0.0:8080/"); + }); + } +} diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Startup.cs b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Startup.cs new file mode 100644 index 0000000000000000000000000000000000000000..eca56318884a59c800e6719a1f8e6f8e2405d8c2 --- /dev/null +++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Startup.cs @@ -0,0 +1,144 @@ +/* + * World Analysis API + * + * API ensuring interoperability between Scene Management and a World Analysis service + * + * The version of the OpenAPI document: 1.0.0 + * + * Generated by: https://openapi-generator.tech + */ + +using System; +using System.IO; +using System.Reflection; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +using ETSI.ARF.OpenAPI.WorldAnalysis.Authentication; +using ETSI.ARF.OpenAPI.WorldAnalysis.Filters; +using ETSI.ARF.OpenAPI.WorldAnalysis.OpenApi; +using ETSI.ARF.OpenAPI.WorldAnalysis.Formatters; + +namespace ETSI.ARF.OpenAPI.WorldAnalysis +{ + /// <summary> + /// Startup + /// </summary> + public class Startup + { + /// <summary> + /// Constructor + /// </summary> + /// <param name="configuration"></param> + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + /// <summary> + /// The application configuration. + /// </summary> + public IConfiguration Configuration { get; } + + /// <summary> + /// This method gets called by the runtime. Use this method to add services to the container. + /// </summary> + /// <param name="services"></param> + public void ConfigureServices(IServiceCollection services) + { + + // Add framework services. + services + // Don't need the full MVC stack for an API, see https://andrewlock.net/comparing-startup-between-the-asp-net-core-3-templates/ + .AddControllers(options => { + options.InputFormatters.Insert(0, new InputFormatterStream()); + }) + .AddNewtonsoftJson(opts => + { + opts.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + opts.SerializerSettings.Converters.Add(new StringEnumConverter + { + NamingStrategy = new CamelCaseNamingStrategy() + }); + }); + services + .AddSwaggerGen(c => + { + c.EnableAnnotations(enableAnnotationsForInheritance: true, enableAnnotationsForPolymorphism: true); + + c.SwaggerDoc("1.0.0", new OpenApiInfo + { + Title = "World Analysis API", + Description = "World Analysis API (ASP.NET Core 5.0)", + TermsOfService = new Uri("https://github.com/openapitools/openapi-generator"), + Contact = new OpenApiContact + { + Name = "OpenAPI-Generator Contributors", + Url = new Uri("https://github.com/openapitools/openapi-generator"), + Email = "" + }, + License = new OpenApiLicense + { + Name = "NoLicense", + Url = new Uri("https://opensource.org/licenses/BSD-3-Clause") + }, + Version = "1.0.0", + }); + c.CustomSchemaIds(type => type.FriendlyId(true)); + c.IncludeXmlComments($"{AppContext.BaseDirectory}{Path.DirectorySeparatorChar}{Assembly.GetEntryAssembly().GetName().Name}.xml"); + + // Include DataAnnotation attributes on Controller Action parameters as OpenAPI validation rules (e.g required, pattern, ..) + // Use [ValidateModelState] on Actions to actually validate it in C# as well! + c.OperationFilter<GeneratePathParamsValidationFilter>(); + }); + services + .AddSwaggerGenNewtonsoftSupport(); + } + + /// <summary> + /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + /// </summary> + /// <param name="app"></param> + /// <param name="env"></param> + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + app.UseHsts(); + } + + app.UseHttpsRedirection(); + app.UseDefaultFiles(); + app.UseStaticFiles(); + app.UseSwagger(c => + { + c.RouteTemplate = "openapi/{documentName}/openapi.json"; + }) + .UseSwaggerUI(c => + { + // set route prefix to openapi, e.g. http://localhost:8080/openapi/index.html + c.RoutePrefix = "openapi"; + //TODO: Either use the SwaggerGen generated OpenAPI contract (generated from C# classes) + c.SwaggerEndpoint("/openapi/1.0.0/openapi.json", "World Analysis API"); + + //TODO: Or alternatively use the original OpenAPI contract that's included in the static files + // c.SwaggerEndpoint("/openapi-original.json", "World Analysis API Original"); + }); + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/appsettings.Development.json b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/appsettings.Development.json new file mode 100644 index 0000000000000000000000000000000000000000..e203e9407e74a6b9662aab8fde5d73ae64665f18 --- /dev/null +++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/appsettings.json b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/appsettings.json new file mode 100644 index 0000000000000000000000000000000000000000..def9159a7d9403c04a926f64e71ef3ee7c9e4c57 --- /dev/null +++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/server/worldstorage/.openapi-generator-ignore b/server/worldstorage/.openapi-generator-ignore new file mode 100644 index 0000000000000000000000000000000000000000..38a29adafc14ce78675a6e1e9d59359e5eaf3b8d --- /dev/null +++ b/server/worldstorage/.openapi-generator-ignore @@ -0,0 +1,41 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md + +# +# ETSI - ISG - ARF +# +**\ETSI.ARF.OpenAPI.WorldStorage.csproj + +# Modules to initialize World Storage database, server.... +**/Program.cs +**/Startup.cs +**/Dockerfile +**/appsettings.json + +# Implementation of REST request and database functionalities +**/ETSI-ARF + +# Design of some web pages +**/wwwroot/portal +**/wwwroot/index.html \ No newline at end of file diff --git a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/Dockerfile b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..1d802b381d537ef7a107138148f635129f6a5e42 --- /dev/null +++ b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/Dockerfile @@ -0,0 +1,32 @@ +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +# Container we use for final publish +FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim AS base +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +# Build container +FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build + +# Copy the code into the container +WORKDIR /src +COPY ["ETSI.ARF.OpenAPI.WorldStorage.csproj", "ETSI.ARF.OpenAPI.WorldStorage/"] + +# NuGet restore +RUN dotnet restore "ETSI.ARF.OpenAPI.WorldStorage/ETSI.ARF.OpenAPI.WorldStorage.csproj" +COPY [".", "ETSI.ARF.OpenAPI.WorldStorage/"] + +# Build the API +WORKDIR "ETSI.ARF.OpenAPI.WorldStorage" +RUN dotnet build "ETSI.ARF.OpenAPI.WorldStorage.csproj" -c Release -o /app/build + +# Publish it +FROM build AS publish +RUN dotnet publish "ETSI.ARF.OpenAPI.WorldStorage.csproj" -c Release -o /app/publish + +# Make the final image for publishing +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "ETSI.ARF.OpenAPI.WorldStorage.dll"] diff --git a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/Services/BaseService.cs b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/Services/BaseService.cs new file mode 100644 index 0000000000000000000000000000000000000000..2e1e8cd20f075246799805acf4d48032155098a9 --- /dev/null +++ b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/Services/BaseService.cs @@ -0,0 +1,214 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Net.NetworkInformation; + +using MongoDB.Driver; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; +using ETSI.ARF.OpenAPI.WorldStorage.Models; +using Microsoft.AspNetCore.Mvc; + + +#pragma warning disable CS1591 // Fehlendes XML-Kommentar für öffentlich sichtbaren Typ oder Element +namespace ETSI.ARF.OpenAPI.WorldStorage.Services +{ + abstract public class BaseService<T> + { + // The object is created by first access? so the singleton may be null if nothing here was called before + static public BaseService<T> Singleton => _singleton; + static protected BaseService<T> _singleton = null; + + public IDatabaseSettings Settings => _settings; + protected IDatabaseSettings _settings; + + protected string mongoServer; + protected MongoClient mongoClient; + protected IMongoDatabase mongoDatabase; + protected IMongoCollection<T> mongoCollection; + + public BaseService(IDatabaseSettings settings) + { + // Store the singleton + if (_singleton != null) throw new Exception("Service can only be instantiated one time!"); + else _singleton = this; + + _settings = settings; + + // Open the mongo database + //BsonSerializer.RegisterSerializer(new GuidSerializer(GuidRepresentation.Standard)); + mongoServer = SelectMongoServer(settings); + mongoClient = new MongoClient(mongoServer); + mongoDatabase = mongoClient.GetDatabase(settings.DatabaseName); + } + + ~BaseService() + { + // to do? + } + + // Check manually if a GUID document already exists! + public bool CheckIfExist(Guid GUID) + { + foreach (var obj in Get()) + { + if (((Models.IModels)obj).GUID == GUID) return true; + } + return false; + } + + public long NumOfDocuments() + { + if (mongoCollection == null) return -1; + else return mongoCollection.CountDocuments(new BsonDocument()); + } + + private void _print(T obj) + { + Console.WriteLine("[Mongo Server] User # " + ((Models.IModels)obj).GUID); + } + + public void PrintAll() + { + // Execute a _print to all elements + var a = mongoCollection.Find(_ => true); + a.ForEachAsync(_print).Wait(); + } + + public T Create(T obj) + { + // Test first if GUID doesn't exist + bool exist = CheckIfExist(((Models.IModels)obj).GUID); + if (!exist) + { + mongoCollection.InsertOne(obj); + return obj; + } + else return default(T); + } + + // + // Get the current mongo _id of the object in the database + // + #region Get current mongo ID or object/document + private Guid _newObjGUID; + private T _currentObj; + private ObjectId _currentMongoID; + private ObjectId getMongoIDFromName(string name) + { + var res = mongoCollection.Find(obj => ((Models.IModels)obj).Name == name).FirstOrDefault(); + if (res == null) return ObjectId.Empty; + else return ((Models.IModels)res)._mongoID; + } + + private ObjectId getMongoIDFromGUID(Guid GUID) + { + _newObjGUID = GUID; + _currentMongoID = ObjectId.Empty; + var i = mongoCollection.Find(_ => true); + i.ForEachAsync(_compareAndRemember).Wait(); + return _currentMongoID; + } + + private void _compareAndRemember(T obj) + { + if (_currentMongoID != ObjectId.Empty) return; // take the first one + if (((Models.IModels)obj).GUID == _newObjGUID) + { + _currentObj = obj; + _currentMongoID = ((Models.IModels)obj)._mongoID; + } + } + #endregion + + //abstract public long Replace(T obj); + public long Replace(T obj) //=> assetCollection.ReplaceOne(asset => asset.GUID == GUID, obj); + { + // Which mongo document? + ObjectId mongoID = getMongoIDFromGUID(((Models.IModels)obj).GUID); + + // Object was found, so replace it using the _id + if (mongoID != ObjectId.Empty) + { + ((Models.IModels)obj)._mongoID = mongoID; + return mongoCollection.ReplaceOne(o => ((Models.IModels)o)._mongoID == mongoID, obj).MatchedCount; + } + else return 0; + } + + public long Remove(T obj) => Remove(((Models.IModels)obj).GUID); + + public long Remove(Guid GUID) + { + //return mongoCollection.DeleteOne(o => ((Models.IModels)o).GUID == GUID); + + // Which mongo document? + ObjectId mongoID = getMongoIDFromGUID(GUID); + + // Object was found, so replace it using the _id + if (mongoID != ObjectId.Empty) + { + // todo: check if the user is allowed to delete the object! + + return mongoCollection.DeleteOne(o => ((Models.IModels)o)._mongoID == mongoID).DeletedCount; + } + else return 0; + } + + // Return all documents + public List<T> Get() => mongoCollection.Find(_ => true).ToList(); + + // Limit the result to the first n documents + public List<T> Get(int limit) => mongoCollection.Find(_ => true).Limit(limit).ToList(); + + // Search manually the document with the id GUID + public T Get(string name) => mongoCollection.Find(obj => ((Models.IModels)obj).Name == name).FirstOrDefault(); + + // Geht nicht! public override T Get(Guid GUID) => mongoCollection.Find<T>(o => o.GUID == GUID).FirstOrDefault(); + // Work around: look in all elements if the GUIDs are equals + public T Get(Guid GUID) + { + foreach (T obj in Get()) + { + if (((Models.IModels)obj).GUID == GUID) return obj; + } + return default(T); + } + + // + // Select the server or use a local database + // + private string SelectMongoServer(IDatabaseSettings settings) + { + string server = null; + Ping myPing = new Ping(); + try + { + PingReply reply = myPing.Send(settings.MongoSrv, 1000); + if (reply != null) + { + server = settings.MongoSrv; + Console.WriteLine("[Mongo Server] Status: " + reply.Status + " \n Time: " + reply.RoundtripTime.ToString() + " \n Address: " + reply.Address); + } + } + catch (PingException e) + { + server = null; + Console.WriteLine("[Mongo Server] Ping: " + e.ToString()); + } + + if (server is null) + { + // Use the local mongo server + server = "localhost"; + Console.WriteLine("[Mongo Server] Take localhost"); + } + server = "mongodb://" + server + ":" + settings.MongoPort + "/"; + mongoServer = server; + return server; + } + } +} +#pragma warning restore CS1591 // Fehlendes XML-Kommentar für öffentlich sichtbaren Typ oder Element diff --git a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/Services/DatabaseSettings.cs b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/Services/DatabaseSettings.cs new file mode 100644 index 0000000000000000000000000000000000000000..197907ff85d146cf300dbed5a78f035f2f0d9f09 --- /dev/null +++ b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/Services/DatabaseSettings.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +#pragma warning disable CS1591 +namespace ETSI.ARF.OpenAPI.WorldStorage.Services +{ + public interface IDatabaseSettings + { + string LocalDataPath { get; set; } + string DatabaseName { get; set; } + string MongoSrv { get; set; } + string MongoPort { get; set; } + public string CollectionNameWorldLink { get; set; } + public string CollectionNameTrackables { get; set; } + public string CollectionNameWorldAnchor { get; set; } + } + + public class DatabaseSettings : IDatabaseSettings + { + public string LocalDataPath { get; set; } + public string DatabaseName { get; set; } + public string MongoSrv { get; set; } + public string MongoPort { get; set; } + public string CollectionNameWorldLink { get; set; } + public string CollectionNameTrackables { get; set; } + public string CollectionNameWorldAnchor { get; set; } + } +} +#pragma warning restore CS1591 // Fehlendes XML-Kommentar für öffentlich sichtbaren Typ oder Element diff --git a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/Program.cs b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/Program.cs new file mode 100644 index 0000000000000000000000000000000000000000..a99fc849c853d1c2efb1e3a0af4c9a7fc995776f --- /dev/null +++ b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/Program.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using System; + +namespace ETSI.ARF.OpenAPI.WorldStorage +{ + /// <summary> + /// Program + /// </summary> + public class Program + { + /// <summary> + /// Main + /// </summary> + /// <param name="args"></param> + public static void Main(string[] args) + { + Console.WriteLine("ETSI ARF ASP.Net server started."); + CreateHostBuilder(args).Build().Run(); + } + + /// <summary> + /// Create the host builder. + /// </summary> + /// <param name="args"></param> + /// <returns>IHostBuilder</returns> + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup<Startup>() + .UseUrls("http://0.0.0.0:44301/"); // SylR: Wichtig!!! + }); + } +} diff --git a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/Startup.cs b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/Startup.cs new file mode 100644 index 0000000000000000000000000000000000000000..a6299c1ad9a818f15596520c4261a9cc81017778 --- /dev/null +++ b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/Startup.cs @@ -0,0 +1,194 @@ +/* + * World Storage API + * + * API ensuring interoperability between an authoring tool and a World Storage service + * + * The version of the OpenAPI document: 1.0.0 + * + * Generated by: https://openapi-generator.tech + */ + +using System; +using System.IO; +using System.Reflection; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +using ETSI.ARF.OpenAPI.WorldStorage.Authentication; +using ETSI.ARF.OpenAPI.WorldStorage.Filters; +using ETSI.ARF.OpenAPI.WorldStorage.OpenApi; +using ETSI.ARF.OpenAPI.WorldStorage.Formatters; +using ETSI.ARF.OpenAPI.WorldStorage.Services; +using MongoDB.Bson.Serialization.Serializers; +using MongoDB.Bson.Serialization; +using MongoDB.Bson; +using Microsoft.Extensions.Options; + +namespace ETSI.ARF.OpenAPI.WorldStorage +{ + /// <summary> + /// Startup + /// </summary> + public class Startup + { + // + // SylR, Fraunhofer HHI + // + /// <summary> + /// The API version. (how to read it from the yaml?) + /// </summary> + static public string apiVersion = "1.0.0"; + + /// <summary> + /// Demo access key + /// </summary> + static public string accessKey = "My!Key.ETSI"; + + /// <summary> + /// Demo secret key + /// </summary> + static public string secretKey = "GW0Wae1t4Cs5rAqEbPYFWO9J5nSbpJXxp1F3uv0J"; + + /// <summary> + /// Constructor + /// </summary> + /// <param name="configuration"></param> + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + /// <summary> + /// The application configuration. + /// </summary> + public IConfiguration Configuration { get; } + + /// <summary> + /// SylR: Check if the request is authorized + /// </summary> + /// <param name="security"></param> + /// <returns></returns> + //static public bool IsAccessAllowed(ETSI.ARF.OpenAPI.WorldStorage.Models.SecureAccess security) + //{ + // Console.WriteLine(security.AccessKey); + // return true; + // //return (security.AccessKey == accessKey && security.SecretKey == secretKey); + //} + + /// <summary> + /// This method gets called by the runtime. Use this method to add services to the container. + /// </summary> + /// <param name="services"></param> + public void ConfigureServices(IServiceCollection services) + { + // + // SylR, Fraunhofer HHI + // + + // appsetting.json - requires using Microsoft.Extensions.Options + services.Configure<DatabaseSettings>(Configuration.GetSection(nameof(DatabaseSettings))); + + // + // MongoDB + // + BsonSerializer.RegisterSerializer(new GuidSerializer(GuidRepresentation.Standard)); + //BsonDefaults.GuidRepresentationMode = GuidRepresentationMode.V3; + var s = services.AddSingleton<IDatabaseSettings>(sp => sp.GetRequiredService<IOptions<DatabaseSettings>>().Value); + //services.AddSingleton<JobService>(); + //services.AddSingleton<AssetService>(); + + // Add framework services. + services + // Don't need the full MVC stack for an API, see https://andrewlock.net/comparing-startup-between-the-asp-net-core-3-templates/ + .AddControllers(options => { + options.InputFormatters.Insert(0, new InputFormatterStream()); + }) + .AddNewtonsoftJson(opts => + { + opts.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + opts.SerializerSettings.Converters.Add(new StringEnumConverter + { + NamingStrategy = new CamelCaseNamingStrategy() + }); + }); + services + .AddSwaggerGen(c => + { + c.EnableAnnotations(enableAnnotationsForInheritance: true, enableAnnotationsForPolymorphism: true); + + c.SwaggerDoc(apiVersion, new OpenApiInfo + { + Title = "World Storage API", + Description = "World Storage API (ASP.NET Core 5.0)", + TermsOfService = new Uri("https://github.com/openapitools/openapi-generator"), + Contact = new OpenApiContact + { + Name = "OpenAPI-Generator Contributors", + Url = new Uri("https://github.com/openapitools/openapi-generator"), + Email = "" + }, + License = new OpenApiLicense + { + Name = "NoLicense", + Url = new Uri("https://opensource.org/licenses/BSD-3-Clause") + }, + Version = apiVersion, + }); + c.CustomSchemaIds(type => type.FriendlyId(true)); + c.IncludeXmlComments($"{AppContext.BaseDirectory}{Path.DirectorySeparatorChar}{Assembly.GetEntryAssembly().GetName().Name}.xml"); + + // Include DataAnnotation attributes on Controller Action parameters as OpenAPI validation rules (e.g required, pattern, ..) + // Use [ValidateModelState] on Actions to actually validate it in C# as well! + c.OperationFilter<GeneratePathParamsValidationFilter>(); + }); + services + .AddSwaggerGenNewtonsoftSupport(); + } + + /// <summary> + /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + /// </summary> + /// <param name="app"></param> + /// <param name="env"></param> + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + app.UseHsts(); + } + + app.UseHttpsRedirection(); + app.UseDefaultFiles(); + app.UseStaticFiles(); + app.UseSwagger(c => + { + c.RouteTemplate = "openapi/{documentName}/openapi.json"; + }) + .UseSwaggerUI(c => + { + // set route prefix to openapi, e.g. http://localhost:8080/openapi/index.html + c.RoutePrefix = "openapi"; + //TODO: Either use the SwaggerGen generated OpenAPI contract (generated from C# classes) + //c.SwaggerEndpoint("/openapi/" + apiVersion + "/openapi.json", "World Storage API"); + + //TODO: Or alternatively use the original OpenAPI contract that's included in the static files + c.SwaggerEndpoint("/openapi-original.json", "World Storage API Original"); + }); + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/_appsettings.json b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/_appsettings.json new file mode 100644 index 0000000000000000000000000000000000000000..def9159a7d9403c04a926f64e71ef3ee7c9e4c57 --- /dev/null +++ b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/_appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/appsettings.Development.json b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/appsettings.Development.json new file mode 100644 index 0000000000000000000000000000000000000000..e203e9407e74a6b9662aab8fde5d73ae64665f18 --- /dev/null +++ b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/appsettings.json b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/appsettings.json new file mode 100644 index 0000000000000000000000000000000000000000..7b6341fa51bd43caf5b07f8bfc1872c553c0dbb2 --- /dev/null +++ b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/appsettings.json @@ -0,0 +1,18 @@ +{ + "DatabaseSettings": { + "Description" : "Version for IIS", + "LocalDataPath": ".\\wwwroot\\dataspace\\data", + "MongoSrv": "localhost", + "MongoPort": "27017", + "DatabaseName": "WorldStorageAPI", + "CollectionNameWorldLink": "WorldLink", + "CollectionNameTrackables": "Trackables", + "CollectionNameWorldAnchor": "WorldAnchor" + }, + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*" +}